Lazy CBC
题目描述
我是一个很懒的开发者,并且希望我的CBC加密算法能正常运行。什么是初始化向量,听起来不是那么重要。
题目代码
from Crypto.Cipher import AES
KEY = ? FLAG = ?
@chal.route('/lazy_cbc/encrypt/<plaintext>/') def encrypt(plaintext): plaintext = bytes.fromhex(plaintext) if len(plaintext) % 16 != 0: return {"error": "Data length must be multiple of 16"}
cipher = AES.new(KEY, AES.MODE_CBC, KEY) encrypted = cipher.encrypt(plaintext)
return {"ciphertext": encrypted.hex()}
@chal.route('/lazy_cbc/get_flag/<key>/') def get_flag(key): key = bytes.fromhex(key)
if key == KEY: return {"plaintext": FLAG.encode().hex()} else: return {"error": "invalid key"}
@chal.route('/lazy_cbc/receive/<ciphertext>/') def receive(ciphertext): ciphertext = bytes.fromhex(ciphertext) if len(ciphertext) % 16 != 0: return {"error": "Data length must be multiple of 16"}
cipher = AES.new(KEY, AES.MODE_CBC, KEY) decrypted = cipher.decrypt(ciphertext)
try: decrypted.decode() except UnicodeDecodeError: return {"error": "Invalid plaintext: " + decrypted.hex()}
return {"success": "Your message has been received"}
|
题目描述
题目给出了三个函数。
1.encrypt函数会对用户输入的明文进行加密。
2.receive函数会对用户输入的密文进行解密。
加解密函数中特殊的地方在于,采用了AES的CBC模式,但是此时的IV=key
3.当我们获得加密时采用的密钥key之后,可以通过get_flag函数获得flag
分析CBC的解密模式,我们可以得到。
①
②
我们将一式和二式等号左右分别异或得到
这时,如果我们把,我们得到
这样我们可以得到key,调用get_flag即可获得flag。
解密代码
import requests from pwn import xor import binascii url="https://aes.cryptohack.org/lazy_cbc/receive/" a="00"*32 r=requests.get(url+a+"/") res=r.text[29:-3] print(res) p1=res[:32] p2=res[32:] print(p1,p2) p1=bytes.fromhex(p1) p2=bytes.fromhex(p2) ans=xor(p1,p2).hex() urls="https://aes.cryptohack.org/lazy_cbc/get_flag/" r=requests.get(urls+ans+"/") flag=r.text[14:-3] flag=int(flag,16) print(binascii.unhexlify(hex(flag)[2:]))
|
Triple DES
题目描述
数据加密标准是AES的先驱,并且仍然广泛用于一些发展缓慢的领域,如支付卡行业。这一挑战表明了DES的一个奇怪的弱点,这是安全分组密码不应该有的。
题目代码
from Crypto.Cipher import DES3 from Crypto.Util.Padding import pad
IV = os.urandom(8) FLAG = ?
def xor(a, b): return bytes(x ^ y for x,y in zip(a, b * (1 + len(a) // len(b))))
@chal.route('/triple_des/encrypt/<key>/<plaintext>/') def encrypt(key, plaintext): try: key = bytes.fromhex(key) plaintext = bytes.fromhex(plaintext) plaintext = xor(plaintext, IV)
cipher = DES3.new(key, DES3.MODE_ECB) ciphertext = cipher.encrypt(plaintext) ciphertext = xor(ciphertext, IV)
return {"ciphertext": ciphertext.hex()}
except ValueError as e: return {"error": str(e)}
@chal.route('/triple_des/encrypt_flag/<key>/') def encrypt_flag(key): return encrypt(key, pad(FLAG.encode(), 8).hex())
|
题目分析
本题考查的是,DES当中的Weak Key,采用Weak Key对明文进行加密得到密文之后,如果我们再次采用相同的密钥对密文进行加密,那么就会恢复明文,解释如下。
,
Weak Key
经过测试,发现 b'\x00'*8 + b'\xff'*8 这个密钥可以使用。
解题代码
def encrypt(key, plain): url = "http://aes.cryptohack.org/triple_des/encrypt/" rsp = requests.get(url + key + '/' + plain + '/').json() if rsp.get("error", None): raise ValueError(rsp["error"]) return rsp["ciphertext"]
def encrypt_flag(key): url = "http://aes.cryptohack.org/triple_des/encrypt_flag/" rsp = requests.get(url + key + '/').json() if rsp.get("error", None): raise ValueError(rsp["error"]) return rsp["ciphertext"]
key = b'\x00'*8 + b'\xff'*8 flag = encrypt_flag(key.hex()) flag_sz = 34 cipher = encrypt(key.hex(), flag) print_blk(cipher, 16) print(bytes.fromhex(cipher))
|
CTRIME
题目分析
明文当中有很多的冗余,为什么不先压缩一下呢。
题目代码
from Crypto.Cipher import AES from Crypto.Util import Counter import zlib
KEY = ? FLAG = ?
@chal.route('/ctrime/encrypt/<plaintext>/') def encrypt(plaintext): plaintext = bytes.fromhex(plaintext)
iv = int.from_bytes(os.urandom(16), 'big') cipher = AES.new(KEY, AES.MODE_CTR, counter=Counter.new(128, initial_value=iv)) encrypted = cipher.encrypt(zlib.compress(plaintext + FLAG.encode()))
return {"ciphertext": encrypted.hex()}
|
题目分析
题目当中只有一个encrypt对用户输入的明文进行加密,但是在加密之前,首先将明文和flag拼接之后再进行压缩,我们对压缩之后得到的内容加密得到密文。
zlib在压缩时会消除了重复的字符串,我们可以利用这个特点,求解flag,我们用X表示flag当中未知的字符,我们已知flag以"crypto{"开头,我们将plaintext设置为crypto{X,我们对crypto{X+flag进行压缩后加密,记录密文的长度,之后我们遍历X的所有可能字符,假设遍历到字符'a'的时候,密文的长度没有发生变化,那么X='a',之后我们再遍历后续的flag字符得到完整flag。
解题代码
import requests import json
base = "http://aes.cryptohack.org/ctrime/encrypt/"
flag = b"crypto{"
response = requests.get(base + flag.hex()) ciphertext = json.loads(response.content)["ciphertext"] baseLength = len(ciphertext)
while 1: for i in range(32, 128): response = requests.get(base + flag.hex() + f"{hex(i)[2:]:0<2}") ciphertext = json.loads(response.content)["ciphertext"] if len(ciphertext) == baseLength: flag += bytes([i]) print(flag) baseLength = len(ciphertext) if i == ord("}"): exit() break
if i == 127: print("Failed to find another byte. Found flag: " + flag.decode()) exit()
|