0x00 前言
这是BUUCTF上的一道只有1分的题目,本以为就是个简单的ROP,但是弄了半天打不通,最后借助大佬们的WP完成了这道题目,写此博客记录几个易错点。
0x01 分析
1 | Arch: i386-32-little |
程序没开PIE和Canary。
1 | int __cdecl main(int argc, const char **argv, const char **envp) |
1 | void __cdecl get_flag(int a1, int a2) |
可以看到在main函数存在明显的栈溢出,并且存在get_flag函数,但是该函数有一个if语句,不过我们可以绕过这个if验证。
难点1
main函数中v4距离返回地址多少个字节?
在IDA中,可以看到对v4的描述如下
1 | char v4; // [esp+4h] [ebp-38h] |
按照以往经验,可能会直接认为v4距离ebp有0x38个字节,所以距离返回地址有0x38+4个字节,但实际上并不是这样,来看main函数的汇编。
这个程序不同于别的程序,在main函数的开始并没有push ebp; mov ebp,esp
这种操作。
这个程序的main栈帧(叫法可能不太准确?但是大体意思应该表达清楚了)并没有ebp,寻址方式是esp寻址。
所以v4距离返回地址有0x38个字节,这一点确实困扰我好大一会
0x02 Let’s do it!
test.py
1 | from pwn import * |
栈溢出覆盖eip
本地可以打通,但是远程不可以,这里的get_flag是取的绕过if检测以后的那部分。
test2.py
这个方法运用了mprotect函数
,这个函数也是这道题目的第二个重点。
我是第一次接触这个函数,通过大佬们的博客也了解到了这个函数的功能以及用法。
1 |
|
addr 我们要进行修改权限的地址
len 是我们要修改的地址的长度
prot 是我们想给从addr到addr + len 这段地址赋予的权限,就是rwx这三个权限,我们可以赋予这段地址rw(可读可写权限但不可执行),也可以赋予r(仅可读),也可以赋予rwx(可读可写可执行)。
rwx 对应的是 7
所以我们这个方法的目的就是借助这个函数,将一段地址的权限修改为可读可写可执行,然后在这段地址上写入shellcode,然后想办法控制eip到这段地址,从而执行shellcode。
这段地址我们选择的是 bss = 0x80ea000
这里执行2次rop,但是依然是本地可以打通,远程打不通。。。
1 | from pwn import * |
exp.py
既然两次rop打不通,那就试试一次吧。
如何把2次rop变成一次rop?
我们可以借助 pop..ret 来实现。
注意到mprotect和read函数都是有3个参数,所以我们需要3次pop,一次ret的gadget。
通过命令ROPgadget --binary get --only 'pop|ret' |grep pop
可以看到一大堆gadgets
随便选一个含有3个pop和1个ret的就行。
这里我们选择0x0806fc08 : pop esi ; pop ebx ; pop edx ; ret
1 | from pwn import * |
家里网速最近不太好,没加sleep的时候一直打不通。。。还以为exp出问题了,调了半天才发现是网速问题。。。
最后还是要感谢各位师傅们的博客