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
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
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}
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
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}