上周末有一个密码挑战,密文是以下十六进制:
FC 89 BF C2 B0 5F 1C 2E 64 B8 78 43 92 78 3A C9
我确信这是使用 AES/Rijndael 128 位 ECB 加密的,密钥是 REDRYDER,并且已经发布了一个解决方案来确认这一点。纯文本为 FLAG=DAISY。我编写了一个简单的 PHP mcrypt 脚本,无需加盐或无需 IV 字符串即可解密,并且可以正确解密。但是,当我尝试使用 openssl 时,我没有得到纯文本:
echo "0: FC 89 BF C2 B0 5F 1C 2E 64 B8 78 43 92 78 3A C9" | xxd -r | openssl aes-128-ecb -d -k REDRYDER -nosalt -nopad ; echo
这只是输出一些二进制数据。我还尝试通过 dd conv=swab 传递输入来进行字节交换。
我究竟做错了什么?
答案1
该openssl
命令行工具是 OpenSSL 库的演示。它有一个相当随意的界面和糟糕的文档。除了测试 OpenSSL 库之外,我不建议将其用于任何其他用途。 (是的,有人用 来管理 CA。openssl
我担心他们的理智。)
AES使用密钥进行操作,而不是使用密码。 AES-128 密钥正好是 16 个字节。
该选项-k
不接受密钥作为输入,而是接受密码。该密码经过哈希处理以派生密钥;默认值是 MD5,可以使用命令行选项覆盖它-md
。据我所知,手册中没有记录这一点,您只需阅读源代码(apps/enc.c
,调用EVP_BytesToKey
)。 MD5 摘要从任何字符串生成一个 16 字节值,但这不是此处使用的值。在这种情况下,关键实际上是空字节REDRYDER\0\0\0\0\0\0\0\0
在哪里\0
。
该选项-K
允许您传递十六进制的密钥。如果您传递的字节数少于密钥大小,OpenSSL 将以空字节完成。所以要传递key REDRYDER\0\0\0\0\0\0\0\0
,你可以传递$(echo REDRYDER | od -An -tx1 | tr -d ' ')
which is 5245445259444552
。
使用密钥 52454452594445520000000000000000 对密文块 FC89BFC2B05F1C2E64B8784392783AC9 进行 AES-128-ECB 解密操作,得到 464c41473d4441495359000000000000(使用十六进制来表示字节序列)。那是FLAG=DAISY\0\0\0\0\0\0
。
对于像这样的小加密操作,我喜欢使用 Python 顶层加密货币图书馆。
>>> from binascii import hexlify, unhexlify
>>> from Crypto.Cipher import AES
>>> ciphertext = unhexlify('FC 89 BF C2 B0 5F 1C 2E 64 B8 78 43 92 78 3A C9'.replace(' ', ''))
>>> key = 'REDRYDER'.ljust(16, '\0')
>>> AES.new(key, AES.MODE_ECB).decrypt(ciphertext)
'FLAG=DAISY\x00\x00\x00\x00\x00\x00'
答案2
这有效:
echo '0: FC89BFC2B05F1C2E64B8784392783AC9' | xxd -r | openssl enc -aes-128-ecb -d -nopad -nosalt -K 5245445259444552
我不明白 -k 选项的输入,但如果您将纯文本密钥转换为十六进制(正确的字节顺序)并使用 -K 代替,它就可以工作。
openssl 是巫毒!