this is a CTF challenge. try to get the flag.
yes
Flag: ALEXCTF{W3_L0v3_C_W1th_CL45535}
How it was found (quick summary):
- The main routine (FUN_00400b89) iterates through the user-supplied string character-by-character and compares each byte to entries in an array located in .data (0x6020c0).
- At 0x6020a0 there is a pointer to a long obfuscated message stored in .rodata (0x400e58…).
- The integer array in .data actually stores indices into that .rodata string; assembling those indices builds the real flag.
- Re-building with a short Python script produced: “ALEXCTF{W3_L0v3_C_W1th_CL45535}”.
Submitting that string satisfies the program and solves the challenge.
show me the script that gets the flag.
I've placed a Python script (extract_flag.py) in the artifact pane.
Run it in the same directory as the re2 binary and it will print the flag.
Please use sandbox_execute tool to run extract_flag.py.
The extraction script executed successfully and printed:
Flag: ALEXCTF{W3_L0v3_C_W1th_CL45535}
That’s the complete working proof-of-concept in Python.
can you explain the program logic?
Program overview (symbols are the autogenerated names Ghidra gave).
-
Entry →
FUN_00400b89
• Checksargc. If it isn’t 2, prints “Usage …” and exits.
• Builds a C++std::stringfromargv[1]and then iterates over it with the usualbegin()/end()iterator helpers.
•local_68holds the current iterator.
•local_1cis a simple position counter (0, 1, 2 …). -
Hidden reference data in
.data
• At virtual address0x6020a0there is an 8-byte pointer that points to.rodataaddress0x400e58.
• Starting at0x400e58is the long innocuous-looking text:L3t_ME_T3ll_Y0u_S0m3th1ng_1mp0rtant_A_{FL4G}_W0nt_b3_3X4ctly_th4t_345y_t0_c4ptur3_H0wev3r_1T_w1ll_b3_C00l_1F_Y0u_g0t_1t
• Immediately after the pointer, at
0x6020c0, is an array of 32-bit integers. Each word is an index into the text above. These indices spell the flag. -
The per-character loop
Pseudocode for the core body:iter = s.begin(); while (iter != s.end()) { char ch = *iter; uint32_t expectedIndex = *(uint32_t*)(0x6020c0 + 4*position); char expected = text[expectedIndex]; if (ch != expected) failure(); ++iter; ++position; } success();•
FUN_00400d3dreceives two iterators, converts them to raw pointers (FUN_00400dac), and returns whether they differ (i.e.,iter != end).
•FUN_00400d9adereferences the iterator (*iter).
•FUN_00400d7aincrements the iterator (++iter).
•FUN_00400b56prints “Better luck next time” and exits on the first mismatch.
• After every character matches, the loop ends andFUN_00400b73prints “You should have the flag by now”. -
Why the flag isn’t stored directly
The flag bytes never exist in memory as a contiguous string. They have to be reconstructed runtime from the big text + the index table. Reverse engineering that table (or simply running the program with a correct input) reveals the flag. -
Reconstructing the flag electrically
A short script:ro_text = read_null_terminated(0x400e58) indices = read_uint32_array(0x6020c0) flag = ''.join(ro_text[i] for i in indices if i < len(ro_text))which yields
ALEXCTF{W3_L0v3_C_W1th_CL45535}
Hence the program simply verifies that the single command-line argument equals this flag, character-by-character, using an indirection layer to avoid placing the flag plainly in the binary.*
