[DawgCTF 2025] [RE] Oops, I spilled my invisibility potion!

TL;DR
Running the 64‑bit ELF prints only four innocuous lines. In the disassembler you immediately notice ~20 consecutive mov …, imm64 instructions that fill a 160‑byte stack buffers before the first puts.
That buffer holds the obfuscated flag.

1. Create an array of 20 Qwords at that address (IDA: Create → Array of Qwords) and export the raw bytes (enc_flag.bin).
View attachment Screenshot 2025-04-21 234658.png
screenshot‑2025‑04‑21‑234658‑png.2


2. Pattern analysis

F3 A0 80 84 F3 A0 81 A1 F3 A0 81 B7 F3 A0 81 A7 …
└─UTF‑8 prefix──┘└─variable byte─┘

Every 4‑byte group forms one supplementary Unicode code‑point (F3 A0 81/80 XX), rendered invisible.
The fourth byte (XX) changes; the other three repeat.

3. Deriving the XOR key with known flag prefix

First variable byte = 0x84. Test: 0x84 ⊕ 0xC0 = 0x44 ('D') — aligns with the expected “DawgCTF”.
Repeated checks (0xA1 ⊕ 0xC0 → 'a', etc.) confirm key = 0xC0.

4. Decode with a lil py script

Python:
raw = open("enc_flag.bin", "rb").read()
flag = ''.join(chr(raw[i] ^ 0xC0)           # XOR with 0xC0
               for i in range(3, len(raw), 4))  # take every 4th byte (offset 3)
print(flag)   # DawgCTF{u_can_tag_but_u_cant_hide}

Wrap up
UTF‑8 camouflage
– F3 A0 81/80 xx resides in the Supplementary Private Use‑B area, usually displayed as zero‑width glyphs.
XOR 0xC0 – sets the top two bits (110xxxxx). Reversing it restores a valid 7‑bit ASCII character.
Stack‑strings with imm64 – long runs of immediate moves to the stack almost always signal embedded data.

Trivial re chall, but cool with the invis glyphs

Flag:> DawgCTF{u_can_tag_but_u_cant_hide}
 

Attachments

  • Screenshot 2025-04-21 234658.png
    46.3 KB · Views: 2
Back
Top