文章目录

  • AES-CBC
  • 加密
  • 解密
  • 题目
  • 过程分析
  • 解题脚本


AES-CBC

aes-cbc模式加密在加密和解密是需要一个初始化向量(Initialization Vector, IV),在每次加密之前或者解密之后,使用初始化向量与明文或密文异或。

加密

加密时,明文首先与IV异或,然后将结果进行块加密,得到的输出就是密文,同时本次的输出密文作为下一个块加密的IV。

AES128 初始化向量_python

解密

解密时,先将密文的第一个块进行块解密,然后将结果与IV异或,就能得到明文,同时,本次解密的输入密文作为下一个块解密的IV。

AES128 初始化向量_AES128 初始化向量_02

题目

from Crypto.Cipher import AES
import binascii
import hashlib
from secret import flag

assert flag[:5] == "flag{" and flag[-1:] == "}"

key = b"J1fx2g1jDak1c***"
l = len(key)

message = b"I have had my invitation to this world's festival, and thus my life has been blessed" + binascii.unhexlify(hashlib.sha256(key).hexdigest())[:10]

iv = flag[5:-1]
message = message + bytes((l - len(message) % l) * chr(l - len(message) % l), encoding = "utf-8")
aes = AES.new(key, AES.MODE_CBC, iv)
print(binascii.hexlify(aes.encrypt(message)))
#******************************************************************************************************************************************************6ece036e495d363b647d7f2749c4c2f3dd78f8637b

过程分析

根据题目我们可以知道如下信息:

key的前十三字节为b'J1fx2g1jDak1c',后三位未知

明文的前八十四字节为b"I have had my invitation to this world's festival, and thus my life has been blessed"
中间十字节未知,最后是bytes((l - len(message) % l) * chr(l - len(message) % l), encoding = "utf-8"),也就是bytes((16-94%16)*chr(16-94%16))==>bytes(2*chr(2)),即b'\x02\x02'
    
密文的后21字节为binascii.unhexlify('6ece036e495d363b647d7f2749c4c2f3dd78f8637b')

根据上面aes-cbc的加密过程和解密过程
我们可以通过已知的最后一个密文块进行aes-ecb模式解密再与最后一个明文块异或,即可得到最后一次的iv,也是上次块加密后的密文
代码逻辑如下:

from Crypto.Cipher import AES
#密文
enc = 'xxxxxx*****'
#明文
m = 'yyyyyy******'
aes_ecb = AES.new(key,AES.MODE_ECB)
#aes ecb模式解密
xor_result = aes_ecb.decrypt(enc)
#解密结果与明文按位异或
iv = bytes([i^j for i,j in zip(m,xor_result)])

ps:因为最终的message与key的值相关,所以我们需要确定key的值才能确定message的值,再依次往回推进而解出初始iv(即flag)

因此我们需要先生成key的字典,然后去爆破Key

import string

dic = string.printable[:62]
with open('key_table.txt','wb') as file:
    for i in dic:
        for j in dic:
            for k in dic:
                key = b"J1fx2g1jDak1c"+i.encode()+j.encode()+k.encode()
                file.write(key+b'\n')

file.close()

AES128 初始化向量_AES128 初始化向量_03


在aes-ecb模式中每一个块的大小是16byte,而已知的密文为后21byte,也就是说倒数第二个密文块已知5byte.由此我们可以根据最后一个块解密的值与最后一个明文块异或的结果的后5byte做比对,得到的值相同的话,该key即为所求key

from Crypto.Cipher import AES
from tqdm import tqdm
import binascii
import hashlib

def xor(m: bytes, c: bytes):
    return bytes([i ^ j for i, j in zip(m, c)])

enc = binascii.unhexlify('5d363b647d7f2749c4c2f3dd78f8637b')
five_part = binascii.unhexlify(b"6ece036e49")
f = open("key_table.txt","rb+")
pbar = tqdm(range(238328))
for i in f:
    key = i[:16]
    aes = AES.new(key, AES.MODE_ECB)
    dec = aes.decrypt(enc)
    m = b"ssed" +binascii.unhexlify(hashlib.sha256(key).hexdigest())[:10]+b'\x02\x02'
    xor_result = xor(m,dec)
    pbar.update(1)
    if five_part in xor_result:
        print(key)
        break
file.close()

AES128 初始化向量_开发语言_04


爆破得到Key:

b'J1fx2g1jDak1c7s4'

AES128 初始化向量_ci_05

ok,确定key之后我们的明文也就确定下来了

key = 'J1fx2g1jDak1c7s4'
message = b"I have had my invitation to this world's festival, and thus my life has been blessed" + binascii.unhexlify(hashlib.sha256(key).hexdigest())[:10]
message = message + bytes((l - len(message) % l) * chr(l - len(message) % l), encoding="utf-8")

#b'I have had my invitation to this world\'s festival, and thus my life has been blessed\xedVG\xfd"\xe6\x9d\xd5\xb9\xe2\x02\x02'

之后将明文分组(16byte一组),然后将上一组解得enc用key去解密,得到xor_result,再将xor_result与同组明文异或即可得到本组iv和上一组enc。以此反复,一步步往前推即可计算出初始iv,即flag。

接下来是愉快的搓脚本时间(bushi

AES128 初始化向量_d3_06

l = len(key)
message = b"I have had my invitation to this world's festival, and thus my life has been blessed" + binascii.unhexlify(hashlib.sha256(key).hexdigest())[:10]
message = message + bytes((l - len(message) % l) * chr(l - len(message) % l), encoding="utf-8")
for i in range(0,len(message),16):
    aes_ecb = AES.new(key,AES.MODE_ECB)
    dec_c = aes_ecb.decrypt(enc)
    enc = xor(message[len(message)-i-16:len(message)-i],dec_c)
    print(enc)

解题脚本

import binascii
import hashlib
import string
from Crypto.Cipher import AES
from tqdm import tqdm

def xor(m: bytes, c: bytes):
    return bytes([i ^ j for i, j in zip(m, c)])
dic = string.printable[:62]
with open('key_table.txt','wb') as file:
    for i in dic:
        for j in dic:
            for k in dic:
                key = b"J1fx2g1jDak1c"+i.encode()+j.encode()+k.encode()
                file.write(key+b'\n')

file.close()


enc = binascii.unhexlify('5d363b647d7f2749c4c2f3dd78f8637b')
five_part = binascii.unhexlify(b"6ece036e49")
f = open("key_table.txt","rb+")
pbar = tqdm(range(238328))
for i in f:
    key = i[:16]
    aes = AES.new(key, AES.MODE_ECB)
    dec = aes.decrypt(enc)
    m = b"ssed" +binascii.unhexlify(hashlib.sha256(key).hexdigest())[:10]+b'\x02\x02'
    xor_result = xor(m,dec)
    pbar.update(1)
    if five_part in xor_result:
        print(key)
        break
file.close()
l = len(key)
message = b"I have had my invitation to this world's festival, and thus my life has been blessed" + binascii.unhexlify(hashlib.sha256(key).hexdigest())[:10]
message = message + bytes((l - len(message) % l) * chr(l - len(message) % l), encoding="utf-8")
for i in range(0,len(message),16):
    aes_ecb = AES.new(key,AES.MODE_ECB)
    dec_c = aes_ecb.decrypt(enc)
    enc = xor(message[len(message)-i-16:len(message)-i],dec_c)
    print(enc)

flag

flag{welcome_1234_igd}

【大概世间有一种自讨苦吃,叫做设身处地,处处为他人着想。】