[ pwn 学习 ] CTFshow-pwn20
2026-08-22 20:36:38

pwn20

1.提交格式

提交ctfshow{【.got表与.got.plt是否可写(可写为1,不可写为0)】,【.got的地址】,【.got.plt的地址】}

4 个问题 2 个”能不能写” 2 个”地址是多少”

2. IDA

F5 反编译 main 函数,核心逻辑非常短:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
int main(int argc, const char **argv) {
// 第一步:打印一大段花里胡哨的横幅(省略)
puts("...* CTFshow --- PWN --- ...");
puts("...* Hint: What is RELRO protection ? ...");

// 第二步:把你传的十六进制参数转成地址
long addr = strtol(argv[1], NULL, 16);

// 第三步:往这个地址写入 "RELR" 四个字符
*(long *)addr = 0x52454c52;

// 第四步:把刚写入的内容读出来打印
printf("RELRO: %x", *(int *)addr);

return 0;
}

总结:

1
你给它一个地址 → 它往里写 "RELR" → 读出来显示给你
地址可写 地址不可写
打印 RELRO: 52454c52 程序直接段错误崩溃

程序本质上就是一个 GOT 表可写性测试器。


3.前置基础:程序怎么找到函数

先理解程序怎么调用函数

代码里有一行printf("hello")

但是printf的真实代码在哪?它不在程序里,在一个叫libc的公共库里

问题是:编译的时候,编译器根本不知道运行的时候libc会被加载到内存的哪个位置

3.1 两张表

程序内部准备了两张表 一张是PLT表 一张是GOT表

  • PLT表是中转站,负责任务分发
  • GOT表是电话簿,记录每个函数真正的内存地址

3.2 GOT表两部分:.got和.got.plt

.got .got.plt
存放内容 杂项(全局变量的地址、动态链接器的内部数据等) 函数指针(每个外部函数对应一个 GOT 条目)
条目数量 很少(2-4 条) 较多(每个导入函数一条)
是攻击目标吗 不太是 是的

3.3 为什么GOT表需要能写

Linux为了启动快。默认不会在程序启动时把所有函数地址都查好,而是用到谁再查谁(也就是延迟绑定 Lazy Blinding)

时机 GOT 表里 printf 那一栏写的是
程序刚启动,还没调用过 printf “我还没查呢,去找 ld.so 帮忙查”
第一次调用 printf ld.so 查到地址,填进 GOT 表
之后再调用 printf 地址已经在表里了,直接跳转

也就是说GOT表在程序运行过程中是需要被写入的(至少第一次调用时)

4.漏洞点:GOT劫持

漏洞在于如果GOT表是可随意更改的 黑客就可以

1
2
3
原来 GOT 表里:  printf → 0x7f1234567890 (libc 里 printf 的地址)
↓ 黑客偷偷改成
改后 GOT 表里: printf → 0x7f9988776655 (system 函数的地址)

之后程序只要调用printf("/bin/sh"),实际执行的就是system("/bin/sh")—— 拿到 shell。

也就是GOT劫持(GOT Hijacking****)

5.RELRO:给GOT上锁

为了防GOT劫持,系统出了一个保护叫RELRO(RELocation Read-Only,把重定位相关的表设为可读)

5.1 三种等级

等级 保护程度 .got 能写? .got.plt 能写? 备注
No RELRO 🔓 完全没上锁 ✅ 能写 ✅ 能写 想怎么改就怎么改
Partial RELRO 🔐 上了一半锁 ❌ 不能写 ✅ 能写 杂项页.got锁了,但是函数地址页.got.plt还能写(因为要支持延迟绑定)
Full RELRO 🔒 锁死了 ❌ 不能写 ❌ 不能写 启动时一次性把所有地址函数查好填完,然后整本电话簿焊死。没法修改了。

5.2 怎么判断等级

方法 No RELRO Partial RELRO Full RELRO
checksec No RELRO Partial RELRO Full RELRO
程序头里是否有 GNU_RELRO段? ❌ ✅ ✅
动态段里是否有 BIND_NOW? ❌ ❌ ✅

6.解题

6.1 可写性

第一步用checksec看一眼

RELRO:NO RELRO没有保护

NO RELRO就是两张表都能写 所以前两个空填 1 和 1

6.2 找两张表地址

第二步用readelf找地址

readelf -S pwn20 | grep -A "\.got"

就知道了.got和.got.plt的地址 并且标志是WA(可写)

6.3 可选验证:运行程序试一下

程序本身就可以验证。给它地址,它能写进去就说明可写

写.got表

52454c52="RELR"写入成功

写.got.plt表

ctfshow{1_1_0x600f18_0x600f28}


相关笔记

RELRO 三部曲:

  • pwn21 — Partial RELRO:.got 锁、.got.plt 可写
  • pwn22 — Full RELRO:全部锁死,.got.plt 消失

前置基础:

  • pwn1 — checksec 查看安全保护
  • pwn25 ret2libc 栈溢出利用 (NX 绕过) — GOT 泄露 + ret2libc 实战
上一页
2026-08-22 20:36:38
下一页