思路很棒的一个题,对AES-CBC模式的攻击也不是一次两次了,其实题目也给了思路,只是自己不清楚原理,导致看不懂提示做不出来
题目代码:
#coding: UTF-8 import socket import flag from Crypto.Cipher import AES def padding(message): toPadByte = 16 - len(message) % 16 paddedMessage = message + chr(toPadByte) * toPadByte return paddedMessage def encrypt(plain): key = flag.flag[5:-1] assert len(key) == 16 iv = key plain = padding(plain) aes = AES.new(key, AES.MODE_CBC, iv) cipher = aes.encrypt(plain) cipher = cipher.encode("base_64") return cipher def runTheClient(): s = socket.socket() host = socket.gethostname() # set this to the host's IP address port = flag.port plain = "blablabla_" + "I_enjoy_cryptography" + "_blablabla" cipher = encrypt(plain) s.connect((host, port)) s.send(cipher) print s.recv(1024).strip() s.close() if __name__ == "__main__": runTheClient()
这里需要把握的重点有两个,A:iv = key,B:plain的提示:分为三个部分,第一部分和第三部分很类似!
首先,贴出来CBC模式加密解密原理图:
https://blog.poxiao.me/p/advanced-encryption-standard-and-block-cipher-mode/
图片来自维基百科
所以我们可以看一看,如果plain的第一部分和第三部分都相等,会出现什么情况
IV ^ BCD(C1,K) = PLAIN1
C1 ^ BCD(C2,K) = PLAIN2
C2 ^ BCD(C3 , K) = PLAIN3
IV = K
我们与服务器通信时,控制的是密文,服务器回传解密,所以采取选择密文攻击,从而对IV也就是K进行信息泄露:
当C1 = C3,且C2 = 0时,发现:K = IV = PLAIN1 ^ PLAIN3!
https://www.anquanke.com/post/id/146419#h3-20
所以,代码如下:(来自上面的链接)
#coding: UTF-8 import socket import flag from Crypto.Cipher import AES def padding(message): toPadByte = 16 - len(message) % 16 paddedMessage = message + chr(toPadByte) * toPadByte return paddedMessage def encrypt(plain): key = flag.flag[5:-1] assert len(key) == 16 iv = key plain = padding(plain) aes = AES.new(key, AES.MODE_CBC, iv) cipher = aes.encrypt(plain) cipher = cipher.encode("base_64") return cipher def runTheClient(cipher): s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) host = "registry.asuri.org" port = 10003 # plain = "blablabla_" + "I_enjoy_cryptography" + "_blablabla" # cipher = encrypt(plain)message = s.recv(1024) s.connect((host, port)) # message = s.recv(1024) # print message s.send(cipher) message = s.recv(1024) print message s.close() return message def crack(): block = "A" * 16 cipher = block + "x00"*16 + block cipher = cipher.encode("base_64") + "n" message = runTheClient(cipher) if "high ASCII" in message: begin = message.find(":") plain = message[begin+1:].strip().decode("base_64") block1 = plain[:16] block3 = plain[32:48] key = "" for i in range(16): key = key + chr(ord(block1[i]) ^ ord(block3[i])) print "key", key if __name__ == "__main__": crack()