NEEPUSec CTF 2023 Loss

题目描述:

from cryptography.hazmat.primitives.ciphers.algorithms import AES
from cryptography.hazmat.primitives.ciphers import Cipher, modes
from Crypto.Util.number import *
import random
flag=b""
key=long_to_bytes(random.getrandbits(16*8))
assert len(flag) == 32
def myhex(x):
    return "".join(hex(k)[2:] for k in x)
def encrypt(pt, key):
	aes = Cipher(AES(key), modes.ECB())
	enc = aes.encryptor()
	ct  = enc.update(pt)
	ct += enc.finalize()
	return ct
ct  = myhex(encrypt(flag, key))
key = myhex(key)

print(ct)
print(key)
# ct  = '98691cbec88e449e8bac58e91142269a7da5efa9e7c62848e7135f1150f02a'
# key = '8ee2b28564433679d93b82873fe8a'

题目分析:

  • 知识导入:
  1. 由题目描述可知是AES_ECB模式

    1. aes = Cipher(algorithms.AES(key), modes.ECB()):
      创建了一个 AES 密码实例,使用给定的密钥 key,并且指定使用 ECB 模式进行加密。
      ECB 模式是一种最简单的加密模式,它将明文分成固定大小的块,然后每个块都使用相同的密钥进行加密。

    2. enc = aes.encryptor():
      创建了一个 AES 加密器实例,可以用于加密数据。

    3. ct = enc.update(pt):
      将明文 pt 传入加密器实例中,调用 update() 方法进行加密,返回加密后的密文。

    4. ct += enc.finalize():
      调用 finalize() 方法,完成加密过程,返回剩余的密文。
      然后将其与之前返回的密文拼接起来,得到完整的密文。

  2. getrandbits(16 * 8) 中,16 * 8 表示要生成一个 16 位字长(即 16 字节)的随机整数。16 * 8 中的 16 表示位数,即 16 位,8 表示一个字节的位数(8个比特位)。因此,16 * 8 表示要生成 16 个字节的随机整数,即 128 比特位的随机整数

  • 这题挺简单的,但有一个坑,忽略的话就。。。
def myhex(x):
    return "".join(hex(k)[2:] for k in x)

思考一下:为什么会单独定义一个这个函数,字节转16进制不直接用byte_data.hex()吗?
所以可猜测,此处有猫腻。
不出所料,此函数会把得到的前置0给省略掉,所以得到的ct和key长度均不符合要求,len(ct) = 62,len(key) = 29。

  • 因此直接爆破即可出结果
  • 方法一(更慢):
from cryptography.hazmat.primitives.ciphers.algorithms import AES
from cryptography.hazmat.primitives.ciphers import Cipher, modes
from itertools import combinations
def decrypt(ct,key): # 加密逆过程
	aes = Cipher(AES(key),modes.ECB())
	dec = aes.decryptor()
	pt = dec.update(ct)
	pt += dec.finalize()
	return pt

def add_zeros(ct,p):
	test_ct = list(ct)
	for i in p:
		test_ct.insert(i,'0')
	return ''.join(test_ct)

ct  = '98691cbec88e449e8bac58e91142269a7da5efa9e7c62848e7135f1150f02a'
key = '8ee2b28564433679d93b82873fe8a'

for mc in combinations(range(len(ct)),2): # 排列组合所有可能缺0的位置
	for mk in combinations(range(len(key)),3):
		test_ct = bytes.fromhex(add_zeros(ct,mc))
		test_key = bytes.fromhex(add_zeros(key,mk))
		flag = decrypt(test_ct,test_key)
		if flag.startswith(b'Neepu{'):
			print('Found!',flag)
  • 方法二(更快):
from cryptography.hazmat.primitives.ciphers.algorithms import AES
from cryptography.hazmat.primitives.ciphers import Cipher, modes
from itertools import combinations
def decrypt(ct,key): # 加密逆过程
	aes = Cipher(AES(key),modes.ECB())
	dec = aes.decryptor()
	pt = dec.update(ct)
	pt += dec.finalize()
	return pt

ct  = '98691cbec88e449e8bac58e91142269a7da5efa9e7c62848e7135f1150f02a' # 62
key = '8ee2b28564433679d93b82873fe8a' # 29

for i in range(30):
	key1 = key[:i] + '0' + key[i:]
	for j in range(31):
		key2 = key1[:j] + '0' + key1[j:]
		for k in range(32):
			true_key = key2[:k] + '0' + key2[k:]
			for m in range(63):
				ct1 = ct[:m] + '0' + ct[m:]
				for n in range(64):
					true_ct = ct1[:n] + '0' + ct1[n:]
					flag = decrypt(bytes.fromhex(true_ct),bytes.fromhex(true_key))
					if flag.startswith(b'Neepu{'):
						print(flag)
# b'Neepu{R41ders_0f_th3_l0st_byt3s}'

浅记一下:
确实是一开始没注意到0的缺失,只知道缺失了,当时看到myhex()函数以为就是单纯的字节转16进制,但后面转念一想如果只是普通的转法,大可直接用byte_data.hex(),所以说都是有含义的啊。技不如人,没什么好说的,坚持学吧。怎么讲呢,就发现自己越多不会,动力还就越大了。

你可能感兴趣的:(流密码,python,密码学,安全)