【ECB模式攻击原理】
(参考https://zachgrace.com/posts/attacking-ecb/)
ecb模式使用相同的key分块对明文分别进行加密,相同的明文获得相同的密文输出
根据这一特性,可以构造如下数据进行攻击:
首先输入blocksize-1的填充,这样未知字符串的第一个字符将落到填充块的最后一个字节:
XXXXXXAA AAAAAAAA AAAAAAXX
XXXXXXBA AAAAAAAA AAAAAAAX
XXXXXXBB AAAAAAAA AAAAAAAA
这时会得到填充块的一个密文输出,爆破最后一个字节,直到产生与刚才相同的密文输出,就可以确定未知字符串的一个字节,重复这个过程就可以得到完整的未知字符串。
【攻击方法】
向包含未知字符串的明文中插入数据,其实也是枚举验算的过程:
1.获取未知字符串的第一位
比输入name为111111111111,
服务器生成未知字符串"hello, 111111111111, your mission's flag is: FLAGXXXXXX"的用ecb加密过的字符串S1给我们,
其中FLAGXXXXXX是我们希望得到的flag,通过观察题目程序可知,此处ECB使用的是16位的,
进而观察可知,16个字符的字符串加密后就变成32个字符的密文了。
就是说"hello, 111111111111, your mission's flag is: X"的最后一个X刚好是第32位,我们首先通过这个32字符的字符串获取一下加密过的encrypto, 也就是64个字符的字符串S2,那么这个S2必然是S1的前64位。
然而我们刚开始并不知道这个X是什么,于是我们枚举这个X,令X为一个ascii字符,
比如向服务器发送"hello, 111111111111, your mission's flag is: A",获取密文,如果这个密文恰好是S1的前64位,那么A就是flag未知字符串的第一位,于是我们就破解了flag的第一位。
2.获取未知字符串的剩余位
因为A已经是flag的第一位,根据破解的原理,我们要把未知字符放在第32位(此处和第一步一样取一个合理的16的倍数)
那么我们只要把name的名字长度缩小一个字符就可以了
即此时变为 "hello, 11111111111, your mission's flag is: AX"
(可以对照第一步的字符"hello, 111111111111, your mission's flag is: A")
依然枚举X即可,以此类推,获取全部的字符串(此处以"}"为结尾标志)
【代码】
主程序代码:
#!/usr/bin/env python
# coding=utf-8
import conn
import os
number = 30 + 16
mess = "hello, 111111111111, your mission's flag is: moeflag{"
ans = ""
while (number >= 0):
found = False
conn.remote("****YourIp****", 8001)
conn.reads()
mess = "1" * number
conn.sends(mess)
# print("发送 " + mess)
initialdata = conn.reads()
# print("原始 "+initialdata)
blockdata = initialdata[0:128 + 32]
# print("前端 \n"+blockdata)
# print(initialdata)
# print("----------")
array = "mqmwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM0123456789{}_-~!@#$%^&*()-=+[]?><'|,./`\"m" #枚举的集合
for i in array:
conn.reads()
# print("尝试 "+i)
mess = "hello, " + "1" * number + ", your mission's flag is: " + ans + i
conn.sends(mess)
data = conn.reads()
# print(data)
# print data
if data[0:128 + 32] == blockdata:
found = True
print("发现 " + i)
ans += i
number = number - 1
if i == "}":
number = -1
print("答案:" + ans)
os.system("pause")
# message=message+i
break
conn.close()
if not found :
print("没有找到~~")
os.system("pause")
print(ans)
组件conn的代码
import socket
import os
obj = None
def remote(addr, ip):
global obj
obj = socket.socket()
obj.connect((addr, ip))
def reads():
global obj
ret_bytes = obj.recv(1024)
ret_str = str(ret_bytes, encoding="utf-8")
#print(ret_str)
return ret_str
def sends(content):
global obj
obj.sendall(bytes(content + "\n", encoding="utf-8"))
def close():
global obj
obj.close()
注:这两个代码需要放在同级目录下,只要运行main就可以了
【编写调试中的问题】
1.连接socket服务器后,能 收到服务器消息, 但是发送客户端发送数据后,却收不到服务器回复。
见https://blog.csdn.net/u014549283/article/details/81484517
这可能是因为输入的字符串最后没有 换行符,导致服务器认为输入未结束。
2.程序运行到一定程度后没有输出反应:
可能有这么几个原因:
原因1:枚举的字符集合不够大,有些flag中例如“=”这样的数据没有包含进去。也可以采用转ascii,这样更完整,但是运算速度就降低了。
原因2:flag位数太大, 移位以后还不够,可以 分批运行程序,也可以扩大初始name的长度