思路很棒的一个题,对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()