Augury - BuckeyeCTF 2025

2025. 11. 9. 16:18·

Đề bài


Giải

Bài này cho mình dịch vụ TCP (qua TLS) ở augury.challs.pwnoh.io:1337 cho ta 3 lựa chọn:

  • View Files: liệt kê và cho tải “ciphertext” của secret_pic.png (trả về một chuỗi hex rất dài)
  • Upload File: nhập tên file, password, và nội dung file ở dạng hex; server thông báo “Your file has been uploaded and encrypted”
  • Exit

Mục tiêu: giải mã secret_pic.png để lấy flag

Điểm mấu chốt: Challenge cố tình “quảng cáo” cực kỳ an toàn ⇒ gợi ý đến các lỗi keystream reuse trong OTP/CTR/stream cipher — một bẫy crypto kinh điển của CTF

Quan sát A - Định dạng trả về

  • Khi “View Files”, server trả một dòng hex (không phải file PNG thật). Vậy dòng hex này là ciphertext của secret_pic.png

Quan sát B - Hành vi “Upload”

  • Màn hình upload yêu cầu “nội dung file ở dạng hex” và báo “has been encrypted”
  • Nếu hệ thống dùng stream cipher / OTP-style: C = P ⊕ K (K là dòng keystream). Nếu re-use keystream (cùng K) cho mọi file, ta sẽ phá được

Giả thuyết 1 - Keystream bị tái sử dụng giữa các file

  • Nhiều đề beginner làm đúng lỗi này: dùng một PRNG tạo K và không thay nonce/IV, hoặc thậm chí “keo kiệt” dùng cùng một K cho tất cả.
  • Nếu đúng, ta có thể tự tạo chosen-plaintext, để suy ra K rồi giải file bí mật.

Đây không phải đoán mò:
(i) Đề cho phép xem ciphertext bất kỳ lúc nào.
(ii) Đề cho phép upload dữ liệu hex tuỳ chọn → môi trường hoàn hảo cho chosen-plaintext.
(iii) Thông điệp “the most secure encryption techniques” ở CTF thường là irony (mồi).

Cách kiểm chứng giả thuyết 1

Ta thử upload một file có plaintext toàn 0 với độ dài đúng bằng ciphertext secret_pic.png. Khi đó:

C_zero = 0x00...00 ⊕ K = K

Tức ciphertext của file toàn 0 chính là keystream
Rồi ta lấy:

P_secret = C_secret ⊕ K = C_secret ⊕ C_zero

Nếu giả thuyết đúng, P_secret sẽ là PNG hợp lệ (bắt đầu bằng 8 byte PNG magic 89 50 4E 47 0D 0A 1A 0A) và file mở được

Khai thác

Bước 1 - Lấy ciphertext bí mật

Vào menu → View Files → chọn secret_pic.png → lấy chuỗi hex (gọi là CT_secret_hex)
Kiểm tra chiều dài (số ký tự hex phải chẵn; số byte = số ký tự / 2)

Bước 2 - Chuẩn bị upload file toàn 0

Ta cần upload một chuỗi hex gồm 00 lặp lại chính xác len(CT_secret_hex) ký tự (tức số byte bằng số byte của C_secret)

Tại sao phải đúng độ dài?
Vì ta muốn keystream K đủ dài để XOR toàn bộ C_secret. Nếu ngắn hơn, ta chỉ giải được prefix. Nếu dài hơn, server có thể cắt hoặc từ chối (tuỳ cài đặt)

Bước 3 - Upload file “keystream”

  • Name: keystream (tuỳ ý, miễn trùng để lát nữa “View Files” đọc được)
  • Password: tuỳ ý
  • Contents (hex): 00 * (số byte C_secret)

Server báo “Your file has been uploaded and encrypted”

Bước 4 - Lấy keystream

  • Vào View Files → chọn keystream → lấy CT_zero_hex
  • Giải thích: vì plaintext = all-zero nên CT_zero = K

Bước 5 - Giải mã

  • P_secret = hex_to_bytes(CT_secret_hex) XOR hex_to_bytes(CT_zero_hex)
  • Ghi ra secret.png và kiểm tra magic:
  •  
    $ file secret.png secret.png: PNG image data, ...
  • Nếu là PNG hợp lệ, mở ảnh, flag thường nằm chữ vẽ trên ảnh hoặc PNG text chunk

Bước 6 - Tự động hoá (pwntools)

Script (đã chạy thành công ở bạn) làm đúng các bước trên:

  • Lấy secret_pic.png (ciphertext)
  • Upload zero file cùng độ dài (thu được keystream)
  • XOR ra secret.png
  • Thử trích flag từ tEXt/iTXt/zTXt (nếu có)

Nếu file secret.png báo data: thường do một trong các lỗi thao tác (phần dưới).

Script

#!/usr/bin/env python3
from pwn import *
import struct

HOST, PORT = "augury.challs.pwnoh.io", 1337
PNG_SIG = b"\x89PNG\r\n\x1a\n"

A = 3404970675
B = 3553295105
MOD = 2**32

def u32(b): return struct.unpack(">I", b)[0]
def p32(x): return struct.pack(">I", x & 0xffffffff)

def recv_menu(r):
    r.recvuntil(b"Please select an option:")
    r.recvuntil(b"> ")

def get_ciphertext(r, name):
    r.sendline(b"2")
    r.recvuntil(b"Choose a file to get")
    r.recvuntil(b"> ")
    r.sendline(name.encode())
    ct_hex = r.recvline().strip().decode()
    recv_menu(r)
    return bytes.fromhex(ct_hex)

def next_state(s):
    return (s * A + B) % MOD

def gen_stream(s0, nbytes):
    out = bytearray()
    s = s0
    while len(out) < nbytes:
        out += p32(s)
        s = next_state(s)
    return bytes(out[:nbytes])

def main():
    r = remote(HOST, PORT, ssl=True)
    recv_menu(r)
    ct = get_ciphertext(r, "secret_pic.png")
    r.close()

    s0 = u32(ct[0:4]) ^ u32(PNG_SIG[0:4])
    s1 = u32(ct[4:8]) ^ u32(PNG_SIG[4:8])

    if next_state(s0) != (s1 & 0xffffffff):
        print("[!] LCG check failed — cipher might differ. Abort to avoid garbage.")
        print(f"    s0={s0:#010x}, predicted s1={next_state(s0):#010x}, observed s1={s1:#010x}")
        return

    print(f"[*] Recovered LCG seed/state s0 = {s0:#010x}")
    ks = gen_stream(s0, len(ct))
    pt = bytes(c ^ k for c, k in zip(ct, ks))

    with open("secret.png", "wb") as f:
        f.write(pt)
    print("[+] Wrote secret.png")

    print("[*] First 16 bytes:", pt[:16].hex())
    if pt.startswith(PNG_SIG):
        print("[+] Looks like a valid PNG. Open secret.png to read the flag!")
    else:
        print("[!] Doesn’t start with PNG signature; share the first 32 bytes above if still off.")

if __name__ == "__main__":
    main()

Flag

Flag: bctf{pr3d1c7_7h47_k3y57r34m}

'WriteUp > Crypto' 카테고리의 다른 글

[Crypto] Guess Me! - m0leCon Teaser 2026  (0) 2025.10.25
[Crypto] EZ-Des - Dreamhack  (0) 2025.10.21
'WriteUp/Crypto' Other posts in category
  • [Crypto] Guess Me! - m0leCon Teaser 2026
  • [Crypto] EZ-Des - Dreamhack
longhd
longhd
Longhd's Blog
  • longhd
    Ha Duy Long - InfosecPTIT
    longhd
  • Total
    Today
    Yesterday
  • About me

    • Hello I'm Duy Long 👋🏻
    • View all categories (117) N
      • Certificates (4)
      • CTF (3)
      • WriteUp (94) N
        • Forensics (44) N
        • Steganography (5)
        • RE (9) N
        • OSINT (8)
        • Web (17)
        • MISC (6)
        • Crypto (3)
        • Pwn (2)
      • Love Story (0)
      • Labs (15)
        • Information Gathering (10)
        • Vulnerability Scanning (2)
        • Introduction to Web Applica.. (1)
        • Common Web Application Atta.. (1)
        • SQL Injection Attacks (1)
  • Blog Menu

    • Home
    • Tag
    • GuestBook
  • Popular Posts

  • Tags

    picoCTF
    PTITCTF2025
    Re
    Forensics
    SunshineCTF2025
    CSCV2025
    OSINT
    POCCTF2025
    Dreamhack
    misc
    CTF
    V1tCTF2025
    writeup
    CHH
    Web
    EnigmaXplore3.0
    Steganography
    htb
    THM
    BuckeyeCTF2025
  • Recent Comments

  • Recent Posts

  • hELLO· Designed ByLong.v4.10.4
longhd
Augury - BuckeyeCTF 2025
Go to Top

티스토리툴바