栈复用(覆盖栈上的有用数据)
- 程序给的输入长度,不够溢出 到返回值,甚至都 不到bp位置 ,这是要考虑覆盖之前函数(或当前函数)的栈上的有用数据,任何利用 程序后续函数调用 时要利用该位置上的数据,来实现栈的漏洞利用。
例题:[BUUCTF在线评测 (buuoj.cn)](https://buuoj.cn/challenges#[ZJCTF 2019]Login)
-
题目给的程序逻辑:输入用户和密码,用户与 admin 比较,密码与 2jctf_pa5sw0rd 比较,最后输出正确并用函数指针调用一个程序前面赋予的函数。
-
checksec检查一下程序保护,只开看一个canary和NX,其实canary都不用看,程序给的溢出点都到不了canary的位置:
-
观察程序给的两个输入点:一个读name、一个读password,读name不用看(输入那么的前面根本没用函数能利用),仔细看一下读password:程序给的输入长度是0x4f,但是要想覆盖到返回值需要0x70长度,很明显不够,s的长度只有8,所以是存在溢出的,能覆盖掉栈上的其他数据,所以观察一下后续程序中是否有对 **该函数栈的重利用 **。
-
直接到最后的比较函数,接受一个函数指针的地址的地址,最后调用了该函数:
-
往前追踪,该指针是 password_checker 函数赋给v7的,并且最后传递出来的是 参数再栈上的地址 而不是参数本身,使用gdb调试看看:
- 调试:可以看到最后是把参数 0x400ab4 在栈上的地址 0x7fffffffdce8 传递给rax(返回值,给到v7),所以参数 0x400ab4 还是在栈上的。
-
进入到最后的比较函数,看看是否访问了 0x7fffffffdce8 该地址出的参数 0x400ab4 如果是使用了,说明肯可能可以利用该漏洞(覆盖):
- 调试:可以看到进入函数前,将 0x7fffffffdce8 的地址 0x7fffffffdd20 给到了rdi进行传参。
- 看到函数的最后掉欧阳那个函数时,使用到了地址 0x7fffffffdce8 处的的值 0x400ab4 。
-
所以可以利用该值的值,来挟持函数的控制流。但是现在有个问题,0x400ab4 是password_checker函数直接赋值的,所以password_checker函数利用不了,但是她的栈会在后面调用 *User::read_password((User )&user_name); 函数,时重新开启(可能值开启部分,毕竟栈不会完全相同)。所以现在只需要调试User::read_password((User *)&user_name);函数,知悉观察栈上的值即可(仔细看栈上地址 0x7fffffffdce8 ):
- 调试,可以看到成功覆盖该地址的值,计算偏移,垃圾数据的长度是0x48,后面返回填充 Admin::shell函数 地址0x0000000000400E88即可,后续在:
- 例子:输入aaaaaaaabaaaaaaacaaaaaaadaaaaaaaeaaaaaaafaaaaaaagaaaaaaahaaaaaaaiaaaaaaa4444,4444就相当于返回地址。
-
EXP:
from pwn import * # from LibcSearcher import * context(os='linux', arch='amd64', log_level='debug') p=remote('node5.buuoj.cn',29890) # p = process("./pwn") p.recvuntil(b'Please enter username: ') p.sendline(b"admin") p.recvuntil(b'lease enter password:') payload = b'2jctf_pa5sw0rd'+b'\x00'*(0x48)+p64(0x0000000000400E88) p.sendline(payload) p.sendline(b'cat flag') p.interactive()
本地成功拿到flag。