pwn-awd知识库
PWN部分
关于 pwn 的修复这个东西我们这里考虑两种场景,第一个给了源码时的修复,第二个是没给源码时的修复
有源码时的基础修复¶
先考虑有源码时的修复,这个其实很好操作,主要是熟练 gcc g++ 的各种指令,还有就是要能快速发现漏洞点,由于 pwn 题中经常是溢出类题型和 UAF 类题型偏多,所以要分别考虑这两种情况,先看有码的溢出类题型(要非常注意 strcat strcpy 等会造成 off by null 的函数
1 | int main(){ |
像这种那肯定是改 %s 为 %23s,不能有更长的了,或者适当增扩 buf 的大小,而且它方法也是类似的,有源码的情况下那可太好修复了
无源码时的程序修改¶
那么无源码应该怎么修复?这似乎就是 pwn 里面的 patch 了
先列出来几张表,下面是 jmp 的相关指令,对于负数等情况需要用到
| 指令 | 机器码 | 指令 | 机器码 |
|---|---|---|---|
| jmp | EB XX | jz | 74 XX |
| je | 74 XX | jne | 75 XX |
| jg | 7F XX | jge | 7D XX |
| jl | 7C XX | jle | 7E XX |
| ja | 77 XX | jae | 73 XX |
| jb | 72 XX | jbe | 76 XX |
| jna | 76 XX | jnb | 73 XX |
| jnae | 72 XX | jnc | 73 XX |
| jnb | 73 XX | jng | 7E XX |
| jnge | 7C XX | jnl | 7D XX |
canary修复方案
不溢出的时候调用 __stack_chk_fail,溢出的时候不调用:这是一个逆向思维,一般人对于canary的印象都是
UAF修补方法
1 | mov qword ptr [rax+18h], 0 |
Patch后上传压缩文件指令
1 | tar -zcvf fix.tar.gz * |
对于 scanf(“%s”) 我们应该如何 patch?
第一个是利用 eh_frame 段,这个段在程序正常运行的时候一般用不到,但是它是被赋予了 X 权限的,也就是说可执行,你比如说对于这个

这里 call 了 __isoc99_scanf 的 PLT,那么我们在 eh_frame 上自己写一个函数,让它 call 到我们自己的函数上去,而我们自己的函数我们就严格限制输入大小,注意这里传入的第二个参数为缓冲区地址
我们这里非常简单暴力,自己通过 syscall 来输入,然后我们去修改 eh_frame,编写如下脚本用于生成字节码
1 | from pwn import * |
然后去修改 call scanf

最后,修改 call scanf 到 call eh_frame 上来,由于这里我们开了 PIE,所以需要 call 一个相对坐标
先看看原本的地方

我们需要把这个函数除了对我们有用的部分全部劫持到我们在 eh_frame 上的代码,多余的部分全部改为 nop

至于这个 0xec5 怎么来的,首先,lea 的长度为 7,所以 lea 的下一条指令的地址是 0x118b,使用 0x2050 - 0x118b 得到 0xec5
现在雀食是可以跳转过来执行了,但是还有最后的问题,即 eh_frame 没有执行权限,这个我们通过 IDA 修改 ELF 的头来实现

Type 一定得是 LOAD,而我们的补丁打在第三个 LOAD 里,所以我将 Flags 修改为了 7,当然啊,这样做其实是非常不负责的,但是做题嘛,怎么快怎么来,如果想要负责的话,那就得加一个 LOAD,并且还需要处理好 PIE,至少现在是正常跑起来了也不会溢出

1 | from pwn import * |
练习源码文件
1 |
|
改printf为puts,如果程序中没有puts的话
修 printf,这里我们通过篡改 printf 为 puts 来实现,但是源程序里并没有 puts,所以需要更改 dyn
我们找到 printf 的 dynsym

我们去修改 printf 为 puts

然后 patch,再次打开程序的时候可以发现就变成 puts 了

堆题的一些通用修复¶
我们都知道堆题高度依赖于 free 函数,如果说 malloc 等函数的不正确使用是造成漏洞的主要原因(比如说溢出就属于长度没控制好),那 free 就是触发漏洞的关键,当然了,还有别的触发方式,我们这里不深入
那么我们如何修复 free 呢?很简单啊, nop 掉就完事了,这样的做法有时候在比赛中比较管用,但也不是啥时候都管用,因此我们还有另外几种方案
nop free**
因为现在的 PWN 题运行过程动不动就有个 5s 以上(比如说要打 IO 爆破),而 checker 的运行只有短短 1~5 秒,那么为什么不从 alarm 上下手呢?
修改alarm函数的时间差
alarm 函数会在时间到了以后强制结束进程,那么我们可以 patch 程序最开始的 alarm(60) 为 **alarm(3)**,这样就有可能造成 checker 通过而 exp 不通过,从而通过 check 逻辑




