Challenge

[AlpacaHackが隠している世界の真実] 🦙←この絵文字はアルパカではなくラマ

from Crypto.Util.Padding import pad, unpad
from Crypto.Cipher import AES
import os
import json

aes_key = os.urandom(16)
flag = os.environ.get("FLAG", "Alpaca{dummy}")

def register(username, message):
    data = json.dumps({"name": username, "message": message}).encode()
    cipher = AES.new(aes_key, AES.MODE_CBC)
    token = cipher.encrypt(pad(data, 16))
    print("[debug]", cipher.iv.hex())
    return token

def login(iv, token):
    data = unpad(AES.new(aes_key, AES.MODE_CBC, iv=iv).decrypt(token), 16)
    data = json.loads(data)
    return data["name"], data["message"]

token = register("alpaca", "paca paca!")
print("This is your login token:", token.hex())

print("Oops! I forgot to save the iv, so I can't decrypt the token! Do you know it?")
iv = bytes.fromhex(input("help me> "))

try:
    username, message = login(iv, token)
except Exception as e:
    print("something wrong:", e)
    exit(1)

if username == "alpaca":
    print("paca paca!")
    print("Thanks! That really helped!")
elif username == "llama":
    print("llama!?!!?", flag)
    print("Oh no, I accidentally leaked the flag...")
else:
    print(f"{username}... who are you?")

AES-CBC で暗号化されたJSONを、こちらが IVを入力できる 形で復号させるサービスです。

Solution

CBCの1ブロック目は次のように計算されます。

P1 = D(C1) XOR IV

つまり IVをいじるだけで P1 を好きに改変できます。

サーバは username == "llama" のときフラグを出します。

元は {"name": "alpaca", ...} です。

ただ "alpaca""llama" は長さが変わるので、16バイトに収めるために: の後を スペース2つにして調整します。

'{"name": "llama' (16 bytes)

Final Script

from ptrlib import *
from binascii import unhexlify as u

p = Socket("nc 34.170.146.252 13161")
iv = u(p.recvlineafter("[debug] "))

x = b'{"name": "alpaca'
y = b'{"name":  "llama'
iv2 = xor(xor(x,y),iv)

p.sendline(iv2.hex())
p.sendline(b"cat f*")
print(p.recvall())