Загрузка данных
#!/usr/bin/env python3
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 >> i) & 1:
z ^= v
msb = (v >> 127) & 1
v = (v << 1) & MASK128
if msb:
v ^= R
return z & MASK128
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 solve_linear(H_k, D):
"""Решает X * H_k = D в GF(2^128) через линейную алгебру над GF(2)."""
# Столбцы матрицы: H_k * e_i
cols = [gf128_mul(H_k, 1 << i) for i in range(128)]
# Строим расширенную матрицу: строки по битам j=0..127
rows = []
for j in range(128):
row = 0
for i in range(128):
if (cols[i] >> j) & 1:
row |= (1 << i)
if (D >> j) & 1:
row |= (1 << 128)
rows.append(row)
# Гаусс
pivot_row = 0
for col in range(128):
pivot = -1
for r in range(pivot_row, 128):
if (rows[r] >> col) & 1:
pivot = r
break
if pivot == -1:
continue
rows[pivot_row], rows[pivot] = rows[pivot], rows[pivot_row]
for r in range(128):
if r != pivot_row and ((rows[r] >> col) & 1):
rows[r] ^= rows[pivot_row]
pivot_row += 1
# Собираем X
X = 0
for r in range(pivot_row):
for col in range(128):
if (rows[r] >> col) & 1:
if (rows[r] >> 128) & 1:
X |= (1 << col)
break
# Проверка
check = 0
for i in range(128):
if (X >> i) & 1:
check ^= cols[i]
if check != D:
return None
return X
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=15) as resp:
return json.loads(resp.read().decode())
def get(path):
with urllib.request.urlopen(BASE + path, timeout=15) as resp:
return json.loads(resp.read().decode())
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. GLOBAL_H = AES_ECB(SERVICE_KEY, 0^16) через /aes_ecb_service
print("\n[*] Получаем GLOBAL_H через /aes_ecb_service...")
r = post("/aes_ecb_service", {
"block_b64": base64.b64encode(b"\x00" * 16).decode()
})
GLOBAL_H = base64.b64decode(r["cipher_b64"])
print(f"[+] GLOBAL_H = {GLOBAL_H.hex()}")
# 3. E_K(FLAG_J0) через /aes_ecb_tenant (who=victim)
print("\n[*] Получаем E_K(FLAG_J0) через /aes_ecb_tenant...")
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()}")
# 4. known = GHASH(H, AAD_VICTIM, FLAG_CT) = FLAG_TAG XOR E_K(J0)
known = bytes(a ^ b for a, b in zip(FLAG_TAG, E_K_J0))
print(f"[+] known = {known.hex()}")
# 5. G0 = GHASH(H, AAD_PREFIX || 0^16, FLAG_CT)
AAD_PREFIX = b"user-aad-prefix:"
G0 = ghash(GLOBAL_H, AAD_PREFIX + b"\x00" * 16, FLAG_CT)
print(f"[+] G0 = {G0.hex()}")
# 6. diff = known XOR G0 = SECRET_VICTIM * H^k
diff = bytes(a ^ b for a, b in zip(known, G0))
diff_int = int.from_bytes(diff, "big")
print(f"[+] diff = {diff.hex()}")
# 7. k (степень H для SECRET_VICTIM)
# data = AAD_PREFIX (16) || SECRET_VICTIM (16) || FLAG_CT || len_block
# N = 2 + ceil(len(FLAG_CT)/16) + 1
N = 2 + (len(FLAG_CT) + 15) // 16 + 1
k = N - 1
print(f"[+] N = {N}, k = {k}")
# 8. H^k
H = int.from_bytes(GLOBAL_H, "big")
H_k = 1
for _ in range(k):
H_k = gf128_mul(H_k, H)
print(f"[+] H^k = {H_k:032x}")
# 9. Решаем X * H_k = diff через линейную алгебру
print("\n[*] Решаем линейное уравнение X * H^k = diff...")
X = solve_linear(H_k, diff_int)
if X is None:
print("[-] Решение не найдено")
return None
SECRET_VICTIM = X.to_bytes(16, "big")
print(f"[+] SECRET_VICTIM = {SECRET_VICTIM.hex()}")
# 10. Проверка
check = ghash(GLOBAL_H, AAD_PREFIX + SECRET_VICTIM, FLAG_CT)
print(f"[+] check = {check.hex()}")
print(f"[+] known = {known.hex()}")
if check == known:
print("[+] ПРОВЕРКА ПРОЙДЕНА!")
else:
print("[-] ПРОВЕРКА НЕ ПРОЙДЕНА")
return None
# 11. /verify
print("\n[*] Отправляем на /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()