0.前言
本文讨论的是栈溢出类题目的思路及一般解法,不包含完整解题过程与高级ROP等。
这也算是这一个月来学习的一个总结与复习。
栈类题目的思想在我看来其实归根到底就是劫持控制流。只要能把控制流劫持到我们想要的地方,目的便达成了。
1.栈溢出的原理
我们知道,栈是一种由高地址向低地址增长的数据结构,而数据的写入是由低地址向高地址的。
倘若向栈中的某个变量写入的字节数超出了这个变量本身的大小,此时若不能很好地控制它,则会发生越界写入。导致的后果是栈中其他数据的改写。
而我们又知道,栈在函数调用的过程中会将rip压栈,此时若能溢出到此处,则可以改写rip的值,使之指向我们想要程序返回的地址,从而控制了执行流。
由此可见,栈溢出漏洞的利用实现要满足两个条件:
1. 程序运行的过程中允许向栈内写入数据。(没法写入怎么溢出?)
2. 写入的数据大小能够发生越界写入。
(其实栈类题也有可能不要溢出就可以达到getshell的目的,例子就是之前“鲲鹏杯”的pwn_1。这题比较特殊。)
2.栈溢出的基本方法
2-0.利用思路
从大体上可以分为两类:
- 直接控制执行流。(shellcode&&rop)
- 间接控制执行流。(改写函数指针,如got表)
2-1.ret2text
这类题算是pwn的hello_world。若程序中本身存在后门函数,直接覆盖返回地址到后门函数的地址即可。
rop的构建一般为:1
payload=b'A'*N+p64(0)+p64(backdoor_addr)
2-2.ret2shellcode
这类题唯一的难点应该是shellcode的构建,如果缓冲区长度允许,直接用pwntools自带的shellcode即可。如果缓冲区长度受限,则需要自己(上网积累)较短的通用shellcode。
能用shellcode的条件是程序有RWX的段,如果开了NX保护则不考虑这种方法。
rop的构建与上一个类似:
1 | shellcode=''' |
2-3.ret2libc
控制执行流返回到libc中的函数,如system()、execv()。
提供一种rop的构建:
1 | payload=b'A'*N+p64(0)+p64(pop_rdi_ret) |
2-4.ret2csu
程序中存在__libc_csu_init() ,可考虑构建通用gadget。
提供一种rop的构建:
1 | payload=b'A'*N+p64(0)+p64(gadget1_addr) |
参考:浅谈ret2csu
2-5.栈转移
大多数情况下栈溢出的长度不足以完成rop,甚至只够覆盖到返回地址。此时考虑栈转移(栈迁移)。
提供一种rop的构建:
1 | payload1=b'A'*N+p64(buf_addr)+p64(leave_ret) |
参考:浅谈简单的栈转移
2-6.libc地址泄露
当开启了ASLR保护时需要先用已知系统函数地址通过偏移计算泄露libc基地址再通过偏移计算我们需要的系统函数的地址。
参考:浅谈libc地址泄露
2-7.劫持got表
(待补充……)
3.总结
发现漏洞,想办法利用漏洞,具体情况具体分析。
栈类的题不是一成不变的,要多积累,多理解。