刷题 AES ECB Cryptohack ECB-Oracle AiY0u 2025-07-15 2025-07-15 Oracle在密码学里通常指一个能提供特定信息的黑盒系统
而ECB这一工作模式最大的不安全性来自于这一特性——相同的明文块能够产生相同的密文块
当ECB遇到了Oracle,我们就能够通过爆破,逐步的获取每一位明文的信息。具体解题步骤如下:
分析题目代码
加密程序很简单明了,将plaintext与FLAG拼接在一起,然后进行ECB加密
这里plaintext是我们自己输入的,FLAG是固定的。
从这一点至少可以分析出,我们尝试不同长度的plaintext根据ECB加密生成的密文长度,来判断flag的长度
发现输入plaintext长度为1~6字节时,加密结果为32字节
输入plaintext长度为7字节时,加密结果为48字节
由此可知:
1 2 len(FLAG)+6 = 32 len(FLAG) = 26
真的是这样吗?
翻车记录 :翻车记录之pad填充 | AiY0u的博客
所以其实我们还要再减去1 ,真实的FLAG长度为25
我们已知了FLAG的开头为crypto{,结尾为}
那么需要爆破16个字符,直接爆破计算量是很大的
我们需要利用开头所说的ECB的安全问题,假设我们输入的plaintext为15个a,那么第一个明文块将会是‘aaaaaaaaaaaaaaac’
假设我们输入9个a,那么第一个明文块将会是‘aaaaaaaaacrypto{’
假设我们输入8个a,那么第一个明文块将会是‘aaaaaaaacrypto{?’
这里留了一个未知的字符?进来,对应加密得到16字节的密文m
我们只需要将?字符从ASCII码值32遍历到127,最多96次就能得到?字符
其他字符的获取与这一原理相似,不再赘述
写python脚本需要用到request库
我们先用开发者工具查看一下该网站加密时的http请求是怎么写的
发现get请求的请求头为
返回结果
那么我们就可以开始写代码了
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 import requests def get_url(plaintext): #生成url plaintext_hex = plaintext.encode('utf-8').hex() url = 'https://aes.cryptohack.org/ecb_oracle/encrypt/' + plaintext_hex + '/' return url def get_c(url): #生成对应密文 c = requests.get(url) return c.text[15:47] #这里先看一眼c.text是啥,然后再把需要的部分切片出来 #寻找前15字节 flag1 = '' for i in range(15,0,-1): url1 = get_url('a'*i) m1 = get_c(url1) for j in range(32,128): t = chr(j) url2 = get_url('a'*i+flag1+t) m2 = get_c(url2) if m1==m2: flag1 += t print('find:'+t) print('now:' + flag1) break else: print("not true the "+t)
对flag的后半部分原理相同,但是代码会有许多差别,可以自行体会
别问我为什么print这么多,我debug de了好久
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 33 34 35 36 37 import requests from Crypto.Util.Padding import pad def get_url(plaintext): plaintext_hex = plaintext.encode('utf-8').hex() url = 'https://aes.cryptohack.org/ecb_oracle/encrypt/' + plaintext_hex + '/' return url def get_c_i(url): #前16字节密文 c = requests.get(url) return c.text[15:47] def get_c_l(url): #后16字节密文 c = requests.get(url) return c.text[-35:-3] flag2 = '' for i in range(8,18): url1 = get_url('a'*i) m1 = get_c_l(url1) print(m1) for j in range(32,128): t = chr(j) padded = pad(t.encode() + flag2.encode(),16) padded = padded.decode() print(padded) url2 = get_url(padded) m2 = get_c_i(url2) print(m2) if m1==m2: flag2 = t + flag2 print('find:'+t) print('now:' + flag2) break else: print('not true the'+t)
将两段代码分别运行,便能获取两段flag