HTB Cyber Apocalypse 2025: EndlessCycle
TL;DR
Dumping de-obfuscated code from a self-decrypting program.
Challenge Overview
Looks like a simple flag checker.

Under Ghidra decompilation, it doesn't look like the string "What is the flag?" is displayed.

Further analysis shows that the program attempts to decrypt itself using a known seed value of CFD2BC5B
through rand()
, and then finally turns it into execution by calling (*pcVar2)()
.

Debugging
To debug this, set a breakpoint at the instruction before the program jumps to the decrypted shellcode with breakrva 0x1214
.

Upon hitting the breakpoint, the obfuscated code would have been decrypted completely by then. At this point, we can now dump the decrypted program withdump memory decrypted.bin $rax $rax+0x9e
. The higher memory region is at an offset of 0x9e
because we know that the original program allocated a buffer size of 0x9e
via mmap()
.
Opening the dumped program in radare2 now shows the expected string of "What is the flag?".

Going back to GDB, performing a step-into with si
should also give the same result.

We can then inspect the entire decrypted program in assembly with x/50i $rip
or x/50i $pc
.
There are two areas of interest here. Let's set a breakpoint at both addresses:
0x7ffff7fc2059
:xor DWORD PTR [rcx],0xbeefcafe
0x7ffff7fc207a
:repz cmps BYTE PTR ds:[rsi],BYTE PTR es:[rdi]

Upon breaking at 0x7ffff7fc2059
, we learn that our user input is loaded into rcx
, which is then XOR-ed with the key of 0xbeefcafe
.

At the 2nd breakpoint, we learn that the XOR key is loaded into rdi
, whereas rsi
appears to be our ciphertext.

Solution
XORing the ciphertext in blocks of 4 bytes yields the flag.

Flag: HTB{l00k_b3y0nd_th3_w0rld}
Last updated