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() # ensure plaintext is valid ascii
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):
# xor 2 bytestrings, repeating the 2nd one if necessary
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()