Magic Gadget
- 前言:
我在最近两周接触到了这个技巧,在不断的学习中发现这个技巧的巧妙,运用之广,真的让人惊讶.
帮我解决了此前的栈相关的两道难题,并在TGCTF中抢到only_gets的一血
- 普遍性:
1._do_global_dtors_aux函数常见于编译后的二进制文件(尤其是 C/C++ 程序),它与 全局对象析构 和 程序终止时的清理逻辑 相关.说明是这个函数普遍存在于二进制文件中
2._libc_csu_init 作为初始化函数,普遍存在于二进制文件中 - 原理
1.ret2csu:
观察下列gadget,我将其分为csu1片段与cus2片段:
很明显我们可以操作这六个寄存器的值,虽然目前看起来r12-r15好像没什么用,但是结合csu2:
此时一切就清晰了:我们可以通过r12-r15操作调用的函数,前三个寄存器的值.而且假如rbx+1==rbp,我们还可以继续通过rop控制执行流.
注意这里调用函数的形式为:
call [r12+rbx8]*
对r12+8rbx(通常rbx==0)的值进行了解引用([]),所以如果想要调用目标函数.
我们应该控制r12为目标函数的got表,rbx==0,那么此时rbp==1.
r13,14,15为目标函数的第一,二,三个参数
2.magic gadget:
直接通过IDA查看,我们是看不到目标gadget的,magic_gadget的定位需要借助”pop rbp,ret;”这个gadget的定位:
address(magic)=address(“pop rbp,ret;”)-1
在pwngdb,我们可以查看到这个magic_gadget:
注意这个gadget,它将ebx的值加在了rbp-0x3d指向的空间,所以如果我们能控制ebx与rbp,我们就能在任意地址中进行偏移.
3.综合利用:
1.控制ebx与rbp可以通过cus1进行,偏移后的结果可以通过cus2调用
2.如果git表可写,我们甚至可以通过偏移直接修改任意got表内地址为system或one_gadget等函数而不需要泄露libc基地址
3.我们还可以在close(1)后将stdout指针内的值重定向到stderr或stdin,恢复回显
我在以下例题中简称这种技术为*重定向
例题
ret2dlresolve:
这道题一直困扰了我很久,我在想如果我不用ret2dlresolve的板子,我是不是就无法解决这个题了:(
当我学会这种方法时我发现:完全可以取代 ret2dlresolve的板子

我没有放题目链接主要是也没什么可放的,大家完全可以自己写个poc验证一下,保护只有NX,还有几个可以利用的got表,存在溢出,但没有合适的gadget
1.定位我们所需的magic gadget与csu gadget,这部按我开头写的就可以,非常简单
2.第一次栈溢出攻击,对write.got进行重定向,注意除了libc要正确,一般重定位所需的偏移会是负数,但是我们的p64()不会自动整形溢出,所以要在结果上加上0x10000000000000000手动整形溢出
1 | from pwn import * |
这次攻击使得write.got被写入system的地址
3.返回show函数,进行第二次ret2csu攻击
将/bin/sh写入bss段,然后使用ret2csu的模板调用write(“/bin/sh”,0,0),此时write.got为system,我们便可以轻松获得shell
1 | payload =b'\x00'*0x78+p64(0x400773)+p64(0)+p64(0x400771)+p64(bss)*2+p64(elf.plt['read'])+p64(0x400607) |
例题
[广东强网杯 2021 个人决赛]hardof:https://www.nssctf.cn/problem/833
这道题和上面很像很像,也是没有合适的gadget,只有两个got表,没有开pie保护
我觉得其实打法和上面一模一样,就贴个wp吧,大家可以试着自己打一下
1 | from pwn import * |
例题
[鹏城杯 2022]one: https://www.nssctf.cn/problem/2423
这道题远程的libc我也没找到,大家本地打着玩一下就好,主要是理解学习这个手法


1.这道题开起来沙箱,禁止我们拿shell,而且在返回前close(1),使得我们无法leak libc,更无法回显flag
2.那么我们的首要任务就是恢复回显
3.stdout,stdin,stderr是三枚存在于bss上的指针,它们指向libc中的三个IO结构体,close,read,write等交互函数是通过这些指针对IO进行交互的,也就是说,如果close(1)后stdout指向的IO文件不再是_IO_2_1_stdout_而是_IO_2_1_stderr_,我们任然能在基于write的输出函数中获得回显,只是write的第一个参数必须设置为stderr代表的文件描述符”2”
4.IO_2_1_stdout_与_IO_2_1_stderr_位于libc中,具有固定的偏移,同时本题可以通过格式化字符串泄露pie,我们便可以将csu+magic的组合拳打在stdout上,将其内部重定向到_IO_2_1_stderr,此时获得回显,然后普通地orw就好
5.我本来写好exp了,但是虚拟机爆炸导致文件全没了,但是我觉得通过上述题目你也能写出对应的脚本:)
谢谢你的浏览(❤ ω ❤)