Challenge

🦙🦙🦙🦙🦙 < ブロック暗号の基本問題パカ

import os
from Crypto.Cipher import AES
from Crypto.Util.Padding import unpad

flag = os.environ.get("FLAG", "ALPACA{*** REDACTED ***}")

ALPACA = chr(129433)  # "🦙"
print(f"Welcome to my login service {ALPACA}")

key = os.urandom(16)

print(f"[DEBUG] key: {key.hex()}") # Oops!

try:
    ciphertext = bytes.fromhex(input("Enter your ciphertext (hex): "))
    iv = bytes.fromhex(input("Enter your IV (hex): "))

    cipher = AES.new(key, AES.MODE_CBC, iv)
    
    padded_plaintext = cipher.decrypt(ciphertext)
    plaintext = unpad(padded_plaintext, AES.block_size)
    username = plaintext.decode()

    print(f"Welcome, {username}")

    if username == ALPACA * 5:  # username == "🦙🦙🦙🦙🦙"
        print(f"Congratulations! Here is your flag: {flag}")
    else:
        print("Invalid username.")

except Exception as e:
    print(f"something went wrong: {e}")
    exit(1)

Solution

サーバは「ログインサービス」として AES-CBC で復号した平文を username として扱い、

username == "🦙" * 5のときに FLAG を出します。

一方で、クライアント側は ciphertext と IV を自由に入力できます。

keyが出力されるので IV を適当に決めて

ciphertext = AES-CBC-Encrypt(key, iv, pad(target_plaintext))

を送ればよいです。

Final Script

from pwn import remote
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad
import os

io = remote("34.170.146.252", 58209)
io.recvuntil(b"[DEBUG] key: ")
key = bytes.fromhex(io.recvline().strip().decode())

username = ("🦙" * 5).encode()
padded = pad(username, AES.block_size)

iv = os.urandom(16)
cipher = AES.new(key, AES.MODE_CBC, iv)
ct = cipher.encrypt(padded)

io.sendline(ct.hex().encode())
io.sendline(iv.hex().encode())

print(io.recvall().decode(), end="")