Basic Rev
How well do you know assembly? Do you know of any tools that can help you? (Hint, they both start with the letter G.)
The attachment is basic_rev. The hint did its job: I used Ghidra to find the interesting branch and GDB to let the binary assemble the flag for me.
First I ran the supplied binary to see what it expects.
$ ./basic_rev
Enter an integer: 1337
Wrong number!
There is only one input and the error mentions a wrong number, so the first goal is to find the integer that reaches the interesting branch. Ghidra’s decompiler makes main() very clear:
undefined8 main(void)
{
int local_c;
local_c = 0;
std::cout << "Enter an integer: ";
std::cin >> local_c;
constructFlag(local_c);
return 0;
}
The input is passed directly to constructFlag(). That function is much larger because it creates and joins several C++ strings, but the comparison controlling the real branch stands out in the decompiled output:
if (param_1 == 0x121) {
local_128 = "ctf";
// several string concatenations follow
}
else {
std::cout << "Wrong number!";
}
0x121 is hexadecimal, so the required decimal input is 289.
>>> int("121", 16)
289
$ ./basic_rev
Enter an integer: 289
Finished processing flag!
The success message confirms the number, but the binary still does not print the flag. Looking back at the decompiler explains why: constructFlag() assembles a string in memory and then only prints Finished processing flag!. The completed value never becomes normal program output.
There are two reasonable ways forward. The string fragments can be reconstructed from Ghidra, or we can let the binary perform all of its own concatenations and inspect the result in GDB. I chose the second approach.
$ gdb ./basic_rev
gef> break *constructFlag(int)
Breakpoint 1 at 0x2399
gef> run
Enter an integer: 289
After the breakpoint, I stepped through the function while watching the registers and stack. Early values contain only partial strings, so stopping immediately is not enough. Near constructFlag(int)+841, the concatenations are complete and GEF identifies a register as a pointer to a printable string.
gef> context registers
$rax : 0x00555555557030 → "Finished processing flag!"
$rdi : 0x0055555556c790 → "byuctf{***********************}"
$rip : 0x005555555566e2 → <constructFlag(int)+841>
Examining the address held in rdi reveals the full value that the program constructed.
gef> x/s $rdi
0x55555556c790: "byuctf{t35t_fl4g_pl3453_ign0r3}"
Flag:
byuctf{t35t_fl4g_pl3453_ign0r3}
The fragments were all visible in Ghidra too, scattered between noisy C++ string operations. Watching rdi at the end was less typing and much harder to get wrong.