Automne's Shadow.

CSAW CTF 2017 BabyCrypt WriteUp

2019/05/18 Share

Crypto

block cipher里的分组模式包括ECB/CBC/CFB/CTR等。

其中ECB模式,即电子密码本模式由于每一组都是分开加密,且相同的明文分组一定会得到相同的密文分组,容易受到已知明文攻击。

其加密模式示意图如下:

automne

以这道典型的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
#!/usr/bin/python

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):
#c = AES.new(key,AES.MODE_ECB);
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的内容一致,那么这个字符就猜对了。

如下图所示,

automne

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

automne

以此类推,就可以还原flag的前16 byte,那么如果flag的长度超过16 byte呢,本题就超过了16 byte。

同样的我们可以从第17 byte开始用上面相同的方法,发送15个A+flag{Crypt0_is_s+? 和发送15个A得到的密文进行比较,来猜解?里的字符,注意这里比较的将是第二组密文。

automne

最终就是用脚本来处理,参考这位大佬的脚本: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): #only need to check printable characters
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:

automne

CATALOG