CryptoHack Flipping Cookie
题目脚本
from Crypto.Cipher import AES
import os
from Crypto.Util.Padding import pad, unpad
from datetime import datetime, timedelta
KEY = ?
FLAG = ?
@chal.route('/flipping_cookie/check_admin/<cookie>/<iv>/')
def check_admin(cookie, iv):
cookie = bytes.fromhex(cookie)
iv = bytes.fromhex(iv)
try:
cipher = AES.new(KEY, AES.MODE_CBC, iv)
decrypted = cipher.decrypt(cookie)
unpadded = unpad(decrypted, 16)
except ValueError as e:
return {"error": str(e)}
if b"admin=True" in unpadded.split(b";"):
return {"flag": FLAG}
else:
return {"error": "Only admin can read the flag"}
@chal.route('/flipping_cookie/get_cookie/')
def get_cookie():
expires_at = (datetime.today() + timedelta(days=1)).strftime("%s")
cookie = f"admin=False;expiry={expires_at}".encode()
iv = os.urandom(16)
padded = pad(cookie, 16)
cipher = AES.new(KEY, AES.MODE_CBC, iv)
encrypted = cipher.encrypt(padded)
ciphertext = iv.hex() + encrypted.hex()
return {"cookie": ciphertext}
观察代码可知,get_cookie
函数不含参数,返回值为 admin=False;expiry={expires_at}
经过 CBC 加密后把 iv
并在一起后的十六进制文本串;check_admin
函数含有参数 cookie
,iv
,返回值根据其解密后文本串中按 ;
进行 .split()
操作后是否为 admin=True
串,如果满足条件,则返回 FLAG。
我们首先可以利用 get_cookie
划分后得到 iv
的原值与 cookie
经过 CBC 加密后的值,根据 CBC 加密的规则:
- c1 ^ iv ^ KEY = d(c1)
- c2 ^ c1 ^ KEY = d(c2)
- c3 ^ c2 ^ KEY = d(c3)
...
其中 d() 表示加密函数,由于 KEY
未知,我们只能通过字节翻转攻击来构造出 admin=True
子串,不难想到我们可以伪造 iv
,因为 c1
只与 iv
相关,而其余与前一块文本相关。
根据 c1 ^ iv ^ KEY = d(c1),Fake_c1 ^ Fake_iv ^ KEY = d(c1) 可知,构造 Fake_iv = iv ^ admin=True;00000
^ admin=False;expi
即可。
脚本如下
import requests
from tqdm import *
from Crypto.Util.number import *
from pwn import *
requests.adapters.DEFAULT_RETRIES = 100000
s = requests.Session()
s.timeout = (1000.0, 1000.0)
real_c1 = b'admin=False;expi'
fake_c1 = b'admin=True;00000'
get_cookie = requests.get(f'http://aes.cryptohack.org/flipping_cookie/get_cookie')
result = get_cookie.text[11:][:-3]
iv = result[0:32]
ciphertext = result[32:]
iv = xor(bytes.fromhex(iv), xor(real_c1, fake_c1)).hex()
check_admin = requests.get(f'http://aes.cryptohack.org/flipping_cookie/check_admin/{ciphertext}/{iv}')
result = check_admin.text
print(result)