Challenge File: rev_infiltration.zip
The zip contents contained a single binary file called "client".
The challenge also has a Docker instance that we need to connect to in order to receive the ciphertext to crack: nc 159.65.20.193 31708
Each time we connect to the server, we receive a different glob of gibberish like this:
When you run the "client" binary, it spits out the usage syntax:
./client [server] [port]
Putting two and two together, we ran the binary against the target server:
./client 159.65.20.193 31708
[!] Untrusted Client Location - Enabling Opaque Mode
file clientclient: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=bcb9d17215725749cf2ce0ee9ef5df3c98ba8f00, for GNU/Linux 4.4.0, stripped
checksec clientArch: amd64-64-littleRELRO: Partial RELROStack: Canary foundNX: NX enabledPIE: PIE enabled
OF COURSE PIE IS ENABLED!!! This means all addresses observed in debugging needs to take the dynamic running binary base address into consideration and adding the tiny offsets from IDA into GDB when setting breakpoints or disassembling functions.
I opened the binary in both Ghidra and IDA, and the decompilation code looks clearer in IDA. A lot of the function names are missing in Ghidra. First I loaded up GDB (I'm using the GEF plugin) and ran it once till completion so it can load the dynamic binary instruction addresses into memory:
gdb ./client
gef➤ run
gef➤ info file
In this "goes_to_funcs" function, I see it calls two other functions, in which I called one of them "contents()" because there's a lot of math stuff happens in it, and another "flag_here()" because that's where the "[!] Untrusted Client Location - Enabling Opaque Mode" message is being printed out with some compare statements in it. So, I was fairly confident something important is happening here for us to find the flag.
char *goes_to_funcs(int file_descriptor)
{
char *result;
result = (char *)content_here(file_descriptor);
if ( !(_DWORD)result )
return flag_here(file_descriptor);
return result;
}
Following the train of thought, here are the relevant parts of the "flag_here()" function:
char *__fastcall flag_here(int num_recvd)
{
char *result; // rax
_BYTE buffer_got[1032]; // [rsp+0h] [rbp-418h] BYREF
unsigned __int64 canary; // [rsp+408h] [rbp-10h]
canary = __readfsqword(40u);
recv(num_recvd, buffer_got, 1024uLL, 0);
puts("[!] Untrusted Client Location - Enabling Opaque Mode");
result = (char *)(canary - __readfsqword(40u));
if ( result && (int)buffer_got > 1 )
return result;
}
The bold piece of code I highlighted seems to be a good spot to put a breakpoint since the next line takes the "result" variable and does some kind of comparison. Inside that if statement, it does a bunch of math, then returns the "result" variable, so I figure it's important. When looking for flags in ciphertext, any kind of compare statement is a good sign because the flag could be in memory for comparison.
Now, back to figuring out what address to set the breakpoint at in GDB. IDA shows that line of code to be at offset 0x132D:
The majority of the beginning of binary hex addresses stay the same. Only the last 3 characters of the hex binary address changes with the offsets for each line of assembly code. Applying this knowledge, above, we saw the .text address starts at 0x00005555555550d0. Changing the last 3 letters to the last 3 letters of the offset 0x132d, it becomes:Before: 0x00005555555550d0
After: 0x000055555555532d
In GDB, we set a breakpoint at that address, and run with the IP and Port from the server:
gef➤ break *0x000055555555532d
gef➤ run 159.65.20.193 31708
The program runs, then breaks at the breakpoint. WHAT'S THIS!? Do I spy a piece of the flag format!?
No comments:
Post a Comment