Crypto
block cipher里的分组模式包括ECB/CBC/CFB/CTR等。
其中ECB模式,即电子密码本模式由于每一组都是分开加密,且相同的明文分组一定会得到相同的密文分组,容易受到已知明文攻击。
其加密模式示意图如下:

以这道典型的CTF赛题为例。
题目的源码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
|
def dumb_pad(imp): while len(imp)%16 != 0: imp+="0" return imp
from Crypto.Cipher import AES import hashlib
k = hashlib.sha256() flag = "flag{Crypt0_is_s0_h@rd_t0_d0...}" k.update(flag) key = k.digest() c = AES.new(key,AES.MODE_ECB); def oracle(inp): return c.encrypt(dumb_pad(inp+flag)).encode('hex')
while True: imp = raw_input("Enter your username (no whitespace): ") print("Your Cookie is: " + oracle(imp))
|
可见输入可控的imp数据,经过oracle函数(AES-ECB加密)处理,并且和flag进行拼接得到密文,并作为Cookie返回。
使用socat本地搭建环境测试
1
| socat -T60 TCP-LISTEN:8000,reuseaddr,fork EXEC:"python -u baby_crypt.py"
|
然后使用nc连接本地IP的8000端口
首先手工做一下测试,从代码里可以看到这是AES加密,这种加密的block长度固定为128位,也就是加密的密文以16 byte为一组,我们常见的AES-128/AES-256等指的是密钥key的长度为128或者256位。
当然,这里的加密为AES-256,而且明文是input+flag的形式
那么如果输入是15个A的话,那么就会和flag里的第一个字符一起拼接,从而达到16 byte分组,然后使用ECB模式加密成一组密文。我们可以依次尝试15个A+?里的?这个字符,直到密文的前32位与直接输入15个A的内容一致,那么这个字符就猜对了。
如下图所示,

第一个字符是f,接着猜第二个字符,输入14个A+f+?,可以确定第二个字符是l

以此类推,就可以还原flag的前16 byte,那么如果flag的长度超过16 byte呢,本题就超过了16 byte。
同样的我们可以从第17 byte开始用上面相同的方法,发送15个A+flag{Crypt0_is_s+? 和发送15个A得到的密文进行比较,来猜解?里的字符,注意这里比较的将是第二组密文。

最终就是用脚本来处理,参考这位大佬的脚本:https://github.com/liamh95/CTF-writeups/blob/master/CSAW17/baby_crypt/solve.py
注意这里取r.recvline()[16:48]是因为返回的数据里前16位字符是:"Your Cookie is: "
脚本直接贴在这里:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| from pwn import *
r = remote('192.168.126.146', 8000)
flag = '' found = ''
r.recv() while found != '}': if len(flag) < 16: sendme = 'A'*(15-len(flag)) else: sendme = 'A'*(15-(len(flag)%16)) r.sendline(sendme) if len(flag) < 16: matchme = r.recvline()[16:48] else: matchme = r.recvline()[48:80] for i in range(32, 127): prompt = r.recv() r.sendline(sendme + flag + chr(i)) response = r.recvline() if len(flag) < 16 and response[16:48] == matchme: flag += chr(i) found = chr(i) break elif len(flag) >= 16 and response[48:80] == matchme: flag += chr(i) found = chr(i) break print flag r.recv()
|
运行可得flag:
