题目在附件中,刚入坑pwn,向各位优秀的师傅拼命学习ing,看到栈迁移的题目,感觉有趣,就总结一下:
所谓栈迁移,一般是指栈溢出后所能构造payload的空间不足,所以需要新的地址空间去构造payload,这就是栈迁移的思想,首先来了解下前置知识:
leave | ret的作用:mov esp,ebp;pop ebp;ret
实现栈迁移的基本思想:
首先相当于ebp把esp勾引到ebp自己的身边,然后这个女人又跑到另一个ebp藏身地去了,esp要经历重重难关(运行命令),再次找到leave | ret(指路仙人),ret才能再次与ebp美女相会,但是ebp又会跑到下一个ebp美女藏身地,有种煮熟的鸭子飞了的感觉。
这里总结了2种题型和方法:(我们把开辟的堆栈空间叫做新世界)
一、直接给出通往新世界的钥匙:pivot(64位)
第一步看看保护措施,确定方略,再看看ida里面有用信息:
看到是栈的溢出漏洞,但是发现栈空间不够写指令,又发现题目创建了个堆给你,应该可以好好利用这个堆实现我们想要的功能,正常来说,当栈的空间不够用时是写入bss段的,这里正好可以用,就先用吧,接下来找有用信息:
先在第二个fget下断点爆破溢出栈大小:
可以知道,栈大小为40(覆盖了ebp),接下来继续分析找有用信息:
看来没有system和/bin/sh,有些迷,看到还有一个文件libcpovit.so,拖ida进去看看:
发现了我们想要的system函数,同时参数有效,就是直接得到flag,那么接下来就是要在povit中获取这个ret2win的真实地址了,再找找信息:
发现了相同的函数,思路已经比较清晰了:
首先让function函数执行一次,在got表中生成对应的真实地址,然后偏移量是利用在libc表中的偏移和真实表中的偏移相同,先计算出在libc表中ret2win和function之间的偏移。再用function真实地址+偏移地址=ret2win的真实地址。
脚本就可以写出来了:
1、计算偏移:
2、执行一次获取got表上的真实地址:
3、求ret2win的真实地址并调用这个函数:
4、得到堆顶指针:
5、栈溢出操作:
6、验证答案:
完整的脚本:
总结:直接利用给出的钥匙(堆指针heap),成功构造payload,执行,问题不大,其次利用xchg eax,esp的gadget,实现栈顶指针的“劫持”,使esp指向堆指针heap,执行堆里面的payload,学习到一个新姿势(本题64位因为有截断符0xa0,所以用不了leave;ret的gadget,所以利用xchg的方法间接实现)
二、打造通往新世界的钥匙:
按照套路,先检查保护机制,拖进ida分析逻辑,
由count的值可知,main函数只能执行一次,这就有些限制我们的操作了,继续往下分析,read读取0x40(64)个字节到buf0x28(40)的空间中,会溢出0x18(24)个字节,这样用来构造的paylode长度就很有限,那么基于上一题的经验,我们需要一个新的空间去写我们的payload,这里没有直接给出我们能用的空间(上一题有堆heap),那么我们就要自己去构造出这把钥匙,去bss段中去打造钥匙,在这里我们需要两把钥匙(也就是两个ebp),然后通过泄露puts的真实地址来计算system的真实地址,从而getshell,下面具体解答:
先看看栈大小,爆破(覆盖ebp):
找可以用的gadget:
其中pop_ebx_ret作用是保持栈平衡,而leave_ret则是为了实现esp的跳转
选取好了,就可以写payload了,首先是各个地址的计算:
接下来是原始的栈进行溢出操作:
通过leave_ret就会把esp勾引到buf1所在的虚拟栈去执行上面的payload,buf1上的payload内容为:
又再次因为leave_ret跳转到buf2所在的虚拟栈去执行上面的payload,buf2上的payload内容为:
自己画了个图,我们知道esp是一步步地往上执行的,遇到leave_ret时跳转,用图示来看清楚具体的流程:
完整的payload如下:
验证:
总结:在bss段构造虚拟的栈空间,进行payload的构造,通过控制ebp和esp的指向,控制程序的执行流程,利用有限的栈溢出空间,实现更大的空间利用。
大总结:学会了两种方法:1、xchg eax,esp(ebx存的是虚拟栈的ebp地址,只要有这个gadget就可以用)2、leave_ret(没有截断符号例如0xa0,就可以用),融会贯通,本质就是劫持esp到虚拟栈中去执行payload~