Загрузка данных
#!/usr/bin/env python3
"""
Эксплойт для AES-GCM + GHASH oracle.
Уязвимость: GLOBAL_H восстанавливается через /ghash_probe,
E_K(J0) — через /aes_ecb_tenant,
SECRET_VICTIM — из линейности GHASH.
"""
import urllib.request
import json
import base64
import struct
import sys
BASE = sys.argv[1] if len(sys.argv) > 1 else "http://localhost:5000"
R = 0xE1000000000000000000000000000000
MASK128 = (1 << 128) - 1
def gf128_mul(x, y):
z = 0
v = x & MASK128
for i in range(128):
if (y >> (127 - i)) & 1:
z ^= v
msb = (v >> 127) & 1
v = (v << 1) & MASK128
if msb:
v ^= R
return z & MASK128
def gf128_inv(x):
result = 1
base = x
exp = (1 << 128) - 2
while exp:
if exp & 1:
result = gf128_mul(result, base)
base = gf128_mul(base, base)
exp >>= 1
return result
def ghash(H_bytes, aad, ct):
H = int.from_bytes(H_bytes, "big")
len_block = struct.pack(">QQ", len(aad) * 8, len(ct) * 8)
data = aad + ct + len_block
Y = 0
for i in range(0, len(data), 16):
block = data[i:i+16]
if len(block) < 16:
block = block + b"\x00" * (16 - len(block))
X = int.from_bytes(block, "big")
Y = gf128_mul(Y ^ X, H)
return Y.to_bytes(16, "big")
def post(path, payload):
req = urllib.request.Request(
BASE + path,
data=json.dumps(payload).encode(),
headers={"Content-Type": "application/json"},
method="POST"
)
with urllib.request.urlopen(req, timeout=10) as resp:
return json.loads(resp.read().decode())
def get(path):
with urllib.request.urlopen(BASE + path, timeout=10) as resp:
return json.loads(resp.read().decode())
def ghash_probe(aad, ct):
r = post("/ghash_probe", {
"who": "attacker",
"aad_hex": aad.hex(),
"ct_b64": base64.b64encode(ct).decode()
})
return base64.b64decode(r["ghash_b64"])
def main():
# 1. FLAG_BLOB
r = get("/flag")
flag_blob = base64.b64decode(r["flag_blob"])
FLAG_IV = flag_blob[:12]
FLAG_CT = flag_blob[12:-16]
FLAG_TAG = flag_blob[-16:]
print(f"[+] FLAG_IV = {FLAG_IV.hex()}")
print(f"[+] FLAG_CT = {FLAG_CT.hex()} ({len(FLAG_CT)} bytes)")
print(f"[+] FLAG_TAG = {FLAG_TAG.hex()}")
# 2. Восстанавливаем H^2
X = b"\x00" * 15 + b"\x01" # 1
g1 = ghash_probe(X, b"")
H2 = int.from_bytes(g1, "big")
print(f"[+] H^2 = {g1.hex()}")
# 3. Восстанавливаем H
Y = b"\x00" * 15 + b"\x02" # 2
g2 = ghash_probe(b"", Y)
Y_int = int.from_bytes(Y, "big")
g2_int = int.from_bytes(g2, "big")
print(f"[+] g2 = {g2.hex()}")
# Пробуем разные L
L_candidates = [
0x80, 0x10, 0x08, 0x01, 128, 16, 8, 1,
0x100, 256, 0x40, 64, 0x20, 32, 0x04, 4,
]
H_bytes = None
for L in L_candidates:
try:
H_times_L = g2_int ^ gf128_mul(Y_int, H2)
H = gf128_mul(H_times_L, gf128_inv(L))
H_bytes_test = H.to_bytes(16, "big")
if ghash(H_bytes_test, X, b"") == g1:
print(f"[+] L = {L} works! H = {H_bytes_test.hex()}")
H_bytes = H_bytes_test
break
except Exception as e:
pass
if H_bytes is None:
print("[-] Ни один L не подошёл")
return
# 4. E_K(FLAG_J0)
FLAG_J0 = FLAG_IV + b"\x00\x00\x00\x01"
r = post("/aes_ecb_tenant", {
"who": "victim",
"block_b64": base64.b64encode(FLAG_J0).decode()
})
E_K_J0 = base64.b64decode(r["cipher_b64"])
print(f"[+] E_K(J0) = {E_K_J0.hex()}")
# 5. known = GHASH(H, AAD_VICTIM, FLAG_CT)
known = bytes(a ^ b for a, b in zip(FLAG_TAG, E_K_J0))
print(f"[+] known = {known.hex()}")
# 6. G0 = GHASH(H, AAD_PREFIX || 0^16, FLAG_CT)
AAD_PREFIX = b"user-aad-prefix:"
G0 = ghash(H_bytes, AAD_PREFIX + b"\x00" * 16, FLAG_CT)
print(f"[+] G0 = {G0.hex()}")
# 7. SECRET_VICTIM
N = 2 + (len(FLAG_CT) + 15) // 16 + 1
diff_int = int.from_bytes(bytes(a ^ b for a, b in zip(known, G0)), "big")
H = int.from_bytes(H_bytes, "big")
H_inv = gf128_inv(H)
H_inv_pow = 1
for _ in range(N - 1):
H_inv_pow = gf128_mul(H_inv_pow, H_inv)
SECRET_VICTIM = gf128_mul(diff_int, H_inv_pow).to_bytes(16, "big")
print(f"[+] SECRET_VICTIM = {SECRET_VICTIM.hex()}")
# 8. /verify
r = post("/verify", {
"who": "victim",
"blob": base64.b64encode(flag_blob).decode(),
"aad_hex": SECRET_VICTIM.hex()
})
print(f"[+] /verify response: {r}")
if "plaintext_b64" in r:
flag = base64.b64decode(r["plaintext_b64"]).decode()
print(f"\n[***] FLAG: {flag}")
return flag
return None
if __name__ == "__main__":
main()