《打造ctf-Pwn自动化利用框架》的基础知识点

admin 2026-05-27 05:18:11 网络安全文章 来源:ZONE.CI 全球网 0 阅读模式

文章总结: 本文系统梳理了CTFPwn挑战中常见防护机制(NX、RELRO、StackCanary、PIE等)的绕过思路与实战攻击技术,重点解析了ret2libc、ROP、栈迁移等核心利用手法。文档通过对比不同防护组合下的攻击路径,提供了从信息泄露到最终获取shell的完整链式利用方案,具有较强的实战指导价值。 综合评分: 85 文章分类: CTF,二进制安全,Pwn,漏洞分析,实战经验


cover_image

《打造ctf-Pwn自动化利用框架》的基础知识点

原创

MicroPest MicroPest

MicroPest

2026年1月3日 20:20 安徽

在小说阅读器读本章

去阅读

2026,如期而至。

在《打造ctf-Pwn自动化利用框架的Pwn灵境》《打造ctf-Pwn自动化利用框架的Pwn灵境(二)(案例说明)》中有很多pwn方面的知识点,作了一些摘要笔记。

| | | | | | — | — | — | — | | 防护 | 核心作用 | 典型绕过思路 | 适用攻击方式 | | NX (DEP) | 栈/堆不可执行 | 无法直接跑 shellcode | → ret2libc / ret2csu / ROP / ret2syscall | | RELRO | – Partial → GOT 可写 – Full → GOT 只读 | Partial:直接改 GOT Full:不能改 GOT,只能改别的指针 | → Partial: ret2plt / GOT 覆写 → Full: ret2libc / ROP / 打 BSS 函数指针 / 利用 IO_FILE 等结构 | | GOT/PLT | 延迟绑定跳板 | GOT 可写时 → 劫持函数指针 | → Partial RELRO: GOT 覆写 → Full RELRO: 只能借 PLT 跳已解析函数,配合 ROP | | Stack Canary | 栈帧末尾 cookie,防栈溢出 | 泄露 or 绕过 | → SSP leak(打印 canary) → partial overwrite(只改低字节保 canary) → 栈迁移到 BSS/heap(避开 canary 检查) | | PIE | 代码段随机化 | 先泄代码基址 | → 信息泄露 → 计算偏移 → ret2libc/ROP | | Full RELRO + NX + PIE + Canary | 全家桶 | 无单点爆破 | → 全链条:信息泄露 → 栈迁移 → ROP → ret2libc |

一句话速记:

  • 能塞 shellcode 的唯一场景:「NX 关」→ 直接 shellcode。
  • NX 开 → 放弃 shellcode,立刻转 ret2libc / ROP。
  • Partial RELRO → 优先 改 GOT(一次写,终身调用)。
  • Full RELRO → GOT 只读,只能 ROP + ret2libc 或打 结构体指针。
  • Canary → 先 泄 canary 或 partial overwrite,否则把栈迁走。

第一项攻击方式,→ ret2libc / ret2csu / ROP / ret2syscall,作如何理解?

答:把「ret2libc / ret2csu / ROP / ret2syscall」当成同一把“钥匙”的四种齿形——NX 开启后栈不能执行,就把代码段或 libc 里现成的机器指令拼成 chain,让 CPU 去替我们 execve(“/bin/sh”, 0, 0)。一句话:不自带 shellcode,只“借刀杀人”。

  1. ret2libc(最朴素)

栈上直接放 libc 里  system  的地址 +  /bin/sh  字符串地址 + 假返回地址(rdi 参数)。

适用:libc 基址已知、rdi 可控、ASLR 关闭或已泄露。

  1. ret2csu(万能“传参器”)

x64 下前三个参数用寄存器 rdi/rsi/rdx,而 libc 里找不到一次 pop 三寄存器的 gadget?

所有 ELF 都自带  __libc_csu_init  函数,里面固定有两段“万能 gadget”:

pop rbx, rbp, r12, r13, r14, r15

mov rdx, r13; mov rsi, r14; mov edi, r15; call [r12 + rbx*8]

利用它把 rsi/rdx 传好,再 call 任意函数(read、write、mprotect、system)。

适用:64 位、前三个参数都要控、没有其他顺手可用的 pop 链。

  1. ROP(通用“拼图”)

把代码段/libc 里无数  pop reg; ret  /  add rsp, 0xXX; ret  /  xor rax, rax; ret  等小片段拼成一条长链:

 布置参数 → 调函数 → 拿到输出 → 再布置下一组参数 → 调 system 或 one_gadget。

适用:任何架构、任何保护(只要代码段可执行且你能泄基址)。

  1. ret2syscall(“无 libc”版)

不用 libc 函数,直接找一段  syscall  指令 + 对应寄存器 gadget:

pop rax ; ret        # 59 = execve

pop rdi ; ret        # “/bin/sh”

pop rsi ; ret        # 0

pop rdx ; ret        # 0

syscall

适用:静态编译、libc 未知或被打补丁、one_gadget 全失效。

速记:

NX 开 → 不能执行栈 → 只能“借刀”:

ret2libc(借 system)、ret2csu(借传参器)、ROP(借任意 gadget)、ret2syscall(借 CPU 门)。

第二~三项攻击方式,作如何理解?

→ Partial: ret2plt / GOT 覆写

→ Full: ret2libc / ROP / 打 BSS 函数指针 / 利用 IO_FILE 等结构

→ Partial RELRO: GOT 覆写

→ Full RELRO: 只能借 PLT 跳已解析函数,配合 ROP

答:两者非常想像,归总如下,

把 RELRO 当成“能不能改函数指针”的开关,只看 GOT 是否可写——

  1. Partial RELRO

GOT 段 可写,而且函数第一次调用后 GOT 里就躺着真实地址。

攻击套路:

ret2plt – 用已有的 plt 条目触发任意函数(puts@plt、system@plt),但参数你得控制。

GOT 覆写 – 直接把 GOT 里某个已解析函数的地址改成  system  或  one_gadget ;下次程序再正常调用它时就等于调  system(“/bin/sh”) 。

一句话:有写权限就“偷梁换柱”,一次写终身受用。

  1. Full RELRO

GOT 段 只读(启动时就解析完并  mprotect  成不可写),你改不了函数指针。

思路:放弃 GOT,去找别的可写且会被调用的指针——

ret2libc / ROP – 正常栈里布置链,用 libc 里  system  等函数;GOT 不能改,但参数寄存器你能填。

打 BSS/数据段函数指针 – 很多程序把  handler / hook  结构放在 BSS,劫持这些指针。

_IO_FILE 结构 – libc 的 FILE 对象里藏着  vtable  指针( _IO_jump_t ),只要你能写这块内存并触发  fclose / printf ,就能把控制流拐进你的  gadget / one_gadget 。

一句话:GOT 只读就“另找指针”,在可写数据区重新造一张“假表”再让程序去用。

速记:

Partial – 改 GOT 本身;

Full – 放弃 GOT,改别的指针或干脆 ROP。

第四项攻击方式,作如何理解?

答:把 canary 当成“栈守护哨兵”——想溢出就必须先拿到或绕过它。三条路对应三句口诀:

  1. SSP leak(打印哨兵号)

利用格式化串、puts、write 等任意读,先把栈上的 canary 原值打印出来 → 溢出时原封不动写回去,哨兵不报警。

套路: %7$p  泄 canary → 溢出 payload 里把泄露值填到正确偏移。

  1. partial overwrite(只改低位,不动哨兵)

canary 位于栈帧末尾,后面才是 saved RBP/saved RIP。

用单字节/两字节溢出,只改 RBP/RIP 低字节 → canary 高字节原样保留,程序校验通过。

适用:PIE 开启时代码基址高字节不变,低字节改动即可跳转到附近 gadget。

  1. 栈迁移(换条街,不经过哨兵)

把栈指针整体搬到 BSS/heap 等可写段(用  leave ret  或  pop rsp )→ 后续 ROP 布置在新栈,原栈 canary 区不再参与溢出检查。

前提:能写目标地址且知道其地址(UAF、堆溢出、任意写)。

速记:

“要么抄答案(leak),要么只改尾巴(partial),要么整条街搬走(栈迁移)”。

第五项攻击方式,作如何理解?

答:把这条链想成“偷看试卷 → 算答案页码 → 翻到满分公式”三步走:

  1. 信息泄露(偷看试卷)

程序里只要有  printf/puts/write  的任意读,就能把内存“打印”出来——通常泄 1 行就够:

泄一个 代码段地址 → 算出 PIE 基址

泄一个 libc 函数地址(如  __libc_start_main ) → 算出 libc 基址

→ 两张“底牌”到手,ASLR 对你失效。

  1. 计算偏移(算答案页码)

用泄出的运行时地址 减去 其在 ELF/libc 文件里的静态偏移,得到 基址;

再把你想调用的  system 、 one_gadget 、 pop rdi  等静态偏移 加回去,就得到 运行时真实地址。

公式:

runtime_addr = file_offset + base

base = leaked_runtime – leaked_file_offset

  1. ret2libc / ROP(翻到满分公式)

现在所有地址已知,栈溢出时直接填:

[pop rdi; ret]  →  “/bin/sh”  →  [system]

或更长 ROP 链,最终  execve(“/bin/sh”, 0, 0)  —— shell 到手。

速记:

“先偷地址算基址,再把 libc/代码段当成固定靶子打 ret2libc/ROP。”

第六项的攻击方式,作如何理解?

答:把「全链条」想成闯 4 道门,必须按顺序拿钥匙;缺一把就进不了下一关——

  1. 信息泄露(拿“地图”)

PIE + 全保护开启后,代码段、libc、栈地址全部随机。

先利用任意读(printf/puts/gets 溢出、格式化串、UAF 等)泄露出:

代码段基址(__text)

已解析的 libc 指针(如 __libc_start_main、free、puts…)

→ 算出 libc 基址 & 后续 gadget 地址,拿到第一张「地图」。

2.栈迁移(换“战场”)

Canary 堵死原栈溢出,或栈空间太小不够布置 ROP。

利用溢出/任意写把「rbp」或「栈上保存的 rsp」改成已知可写地址(bss/heap/data),再  leave; ret  /  pop rsp; ret  把栈切过去。

→ 新栈在「可写 + 可预知地址」区域,绕过 canary,且空间足够。

3.ROP(布置“跳板”)

在新栈上写入一连串 gadget 地址:

 pop rdi; ret  → 给将要调用的函数传参

 pop rsi; ret  /  pop rdx; ret  …

最终跳向  system  /  execve  /  one_gadget

→ 用代码段里已有的小指令拼出“调函数”逻辑,绕过 NX(栈不可执行)。

4.ret2libc(执行“致命一击”)

最后一条 ROP 链直接  call system(“/bin/sh”)  或  execve(“/bin/sh”, 0, 0) ;

因为第 1 步已拿到 libc 基址,所以  system 、字符串  /bin/sh  地址全部已知。

→ shell 到手,保护全部绕过。

速记:

“先拿地图再搬家,拼图跳板调 libc。”

可访问:http://8.137.125.6:14001/pwn.html


免责声明:

本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。

任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。

本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我

本文转载自:MicroPest MicroPest MicroPest《《打造ctf-Pwn自动化利用框架》的基础知识点》

评论:0   参与:  0