bugku
pwn1
这个是典型的新手题了,nc连上去就有shell, 直接可以拿到flag。
pwn2
main函数
这里我们看到read函数位置会出现一个典型的栈溢出。
然后我们计算下溢出位置到返回值之间的距离,就可以控制程序流程了,
接下来我们看到函数中存在一个get_shell_
函数:
然后就可以着手写exp,这里简单写下payload部分:
payload = 'a' * 0x38 + p64(0x0400751)
pwn3
这个题目比较复杂。
查一下保护方法:
main 函数会转到 vul函数,前半部分选择一个文件打开,并没有什么有用的位置,
我们设定一个函数去简单略过这一部分:
def vul(payload1):
sla('path:', 'flag')
sla('len:', '1000')
sa('note:', payload1)
ru(payload1)
然后后半部分,是读取一个长度,然后读入对应长度的数据,并且如果不是0x270的话会重新读入一次。
这里我们看下栈内,读取0x270的话可以恰好覆盖掉ret的位置。
同时我们看到存在一个canary保护。
这个题目的位置,在第一个溢出点后有打印,然后又存在第二个溢出点,这时候我们可以先输入一段,带出canary的值,然后在第二个溢出点放回canary的值。
这时候我们也可以看到基本的题目思路:
- 先利用第一个溢出点后的打印可以泄露出来canary然后第二个溢出点把程序复原
- 由于pie,还得要先泄露程序基地址,然后第二个溢出点复原程序。由于是64位,需要gadget,要泄露程序基地址。
- 泄露libc的基地址,我们可以使用
libcSearcher
,就可以获得sys和binsh的地址,第二个溢出点不能构建rop链,得第一个溢出点写出来然后第二个在覆写一遍前面的一部分。就可以直接getshell
然后我们在第二次输入的时候canary的值放回去,并且可以修改ret地址,由于这个位置开启pie,
这里讲一点。关于pie技术的缺陷:
由于内存是以页载入机制,开启pie的时候,最多影响到单个内存页,一个内存页大小为0x1000, 即我们的低三位是固定不变的,可以通过覆盖地址后几位控制程序流程。 这个方式称为
"partial write"
。
这里我们看到,应该返回地址为2e
我们覆盖修改为20
就可以返回到main 函数再次运行一次了。
这样我们的第二段payload 就是’a’* 600了
这样我们可以进行下一阶段的利用了。
下面是直接泄露程序基地址,我们这个程序使用puts
反显,我们想泄露的值只要没有'\x00'
就可以直接泄露出来。
这样我们的泄露elf的基地址和libc的基地址都只需要思考关于要覆盖的长度的问题。
elf: