AttackLab实验
实验内容
官网:http://csapp.cs.cmu.edu/3e/labs.html
“AttackLab”是一个Linux下的可执行C程序,包含了5个阶段(phase1~phase5)的不同内容。程序运行过程中,要求学生能够根据缓冲区的工作方式和程序的反汇编代码来确定攻击字符串长度和字符串中的关键内容。每次成功实现缓冲区溢出攻击时都会有提示相应内容,如果攻击失败则单纯的提示segmentation fault相关信息。
要求攻击字符串的执行不许绕开代码中的validate函数,缓冲区溢出之后对应ret的返回地址可以是以下类型:
- 函数touch1、touch2、touch3的首地址;
- 自行注入的攻击的首地址;
- 在后两个阶段中(ROP攻击),与farm.c的对应的可利用的gadget的起始地址,farm.c对应的机器码已经包含在可执行文件中。可以使用的gadget首地址需处于start_farm和end_farm之间的部分。
注意:前三个阶段使用ctarget作为攻击目标文件,后两个阶段中使用rtarget作为攻击目标文件。
每个阶段考察一个缓冲区溢出方式,难度逐级递增:
- 阶段1:使用非ROP方式对ctarget进行攻击,调用touch1,且成功输出Touch1!: You called touch1。若不完全满足题目要求,则会提示“Misfire”和FAIL相关字段。
- 阶段2:使用非ROP方式对ctarget进行攻击,调用touch2,且成功输出Touch2!: You called touch2。攻击过程中需要改写cookie变量的值。若不完全满足题目要求,则会提示“Misfire” 和FAIL相关字段。
- 阶段3:使用非ROP方式对ctarget进行攻击,调用touch3,且成功输出Touch3!: You called touch3。攻击过程中需要使hexmatch的返回值能够正确引导validate函数。若不完全满足题目要求,则会提示“Misfire” 和FAIL相关字段。
- 阶段4:使用ROP方式对rtarget进行攻击,调用touch2,且成功输出Touch2!: You called touch2。若不完全满足题目要求,则会提示“Misfire” 和FAIL相关字段。
- 阶段5:使用ROP方式对rtarget进行攻击,调用touch3,且成功输出Touch3!: You called touch3。若不完全满足题目要求,则会提示“Misfire” 和FAIL相关字段。
ctarget和rtarget都从standard input读入数据,可以以重定向文件的形式进行输入。实验利用getbuf函数中的缓冲区。getbuf函数的结构如下:
unsigned getbuf()
{
char buf[BUFFER_SIZE];
Gets(buf);
return 1;
}
函数中的Gets函数与标准库中的gets函数类似,它从standard input中读取字符(以\n或者EOF结尾)并将它们添加字符串结尾符\0后存入缓冲区中。学生需要根据ctarget和rtarget文件及其反汇编代码来确定缓冲区位置及大小,并想办法构建出攻击字符串。
实验材料
- cookie.txt 个人cookie。
- ctarget 阶段 1-3 的攻击对象程序。
- farm.c 可利用代码片段源码。
- hex2raw 将字符串转二进制程序。
- rtarget 阶段 4-5 的攻击对象程序。
- README.txt 实验介绍文件。
附录
运行时栈
函数间的转移控制
机器码表
注意:D 为 2 字节功能性 nop 指令的编码,对前后文无影响。
实验过程
分析 ctarget 程序
我们需要利用程序中的 getbuf 函数,我们需要查看 ctarget 中的 getbuf 函数。执行 objdump -d ctarget > ctarget.s
查看 ctarget 的汇编代码:
从中可以看出 getbuf 的栈结构为(地址从大到小):ret 返回地址,0x38 内存空间(sub $0x38,%rsp
)。并且 Gets 函数存放数据从 getbuf 的栈底开始(mov %rsp,%rdi
)。
阶段1
我们需要利用 getbuf 函数来非正常跳转,所以我们需要利用溢出来修改 ret 返回地址。填充 0x38 个空字节来抵达存储 ret 返回地址的空间,然后填写目标地址来覆盖正常返回的地址。
-
查看 touch1 函数的地址
-
创建输入文件 phase1.txt 并写入 0x38 个空字节和 touch1 函数的地址(小端法)。
-
使用 hex2raw程序将 phase1.txt 转换成二进制文件 phase1-raw.txt
执行
./hex2raw < phase1.txt > phase1-raw.txt
-
运行 ctarget 查看结果
执行
./ctarget < phase1-raw.txt -q
PASS 通过。
阶段2
我们需要在阶段1的基础上,修改 cookie 变量的值。
-
查看 touch2 函数
从中我们看出 touch2 的地址为 0x4018d5,cookie 变量存储在 %edi 中。我们需要一段代码(即攻击代码)将 %edi 的值修改为我们的 cookie 值。
-
查看 cookie.txt 文件中的值
cookie.txt 文件中的值为 0x77058131
-
构造攻击代码
所以我们需要的攻击代码为
mov $0x77058131,%edi ret
-
创建 inject.s 文件,将攻击代码写入,并编译。
执行
gcc -c inject.s
,得到 inject.o 文件 -
反编译 inject.o 文件得到攻击代码的机器码
执行
objdump -d inject.o
-
寻找攻击代码可存放位置
通过前文的分析我们知道,我们输入的起始地址是在 getbuf 的栈底,所以我们可以将攻击代码放到 getbuf 的栈底。通过 gdb 定位到 getbuf 函数中的 Gets 行,查看 %rsp 即 getbuf 的栈底。
所以第一个 ret 我们需要跳转到 getbuf 的栈底,即 0x55660908 处先执行我们的攻击代码,再通过攻击代码的 ret 跳转到 touch2 中。
-
查看 touch2 的地址
-
创建 phase2.txt 并将攻击代码和两个跳转地址写入
-
使用 hex2raw 程序将 phase2.txt 转换成二进制文件 phase2-raw.txt
执行
./hex2raw < phase2.txt > phase2-raw.txt
-
运行 ctarget 查看结果
PASS 通过
阶段3
本阶段需要通过 touch3 函数。
-
查看 touch3 函数
从中我们了解 touch3 函数的地址为 0x4019ec ,且我们需要修改 %rdi 的值使其满足 hexmatch 函数。
-
查看 hexmatch 函数
发现 hexmatch 比较了两个字符串,所以我们需要修改 %rdi 的值,使其为我们 cookie 字符串的首地址。
-
构造 cookie 字符串
字符串在机器中是以 0 结尾字符序列,我们采用的是 16 进制,对照 ascii 码表 cookie:0x77058131 的字符序列应该为
37 37 30 35 38 31 33 31 00
-
寻找 cookie 字符串存放地址
我们可以放到返回地址 2 后
所以字符串的地址为栈底地址+ 72 字节(即,0x48),根据前文栈底地址为 0x55660908 ,所以字符串地址为 0x55660950 。
-
构造攻击代码
mov $0x55660950,%rdi ret
-
将攻击代码写入 inject.s 并编译
执行
gcc -c inject.s
得到 inject.o 文件。 -
反编译 inject.o 文件,得到攻击代码的机器码
执行
objdump -d inject.o
-
攻击代码存放位置同阶段 2 ,在 phase3.txt 文件写入攻击代码,返回地址 1,2,和字符串。
-
使用 hex2raw 程序将 phase3.txt 转为二进制文件 phase3-raw.txt
执行
./hex2raw < phase3.txt > phase3-raw.txt
-
运行 ctarget 查看结果
PASS 通过
分析 rtarget 程序
阶段 4,5 要求我们使用 ROP 的方式来进行攻击,即我们无法自己编写攻击代码来,但我们可以利用程序中带 ret 的片段代码来构造我们需要的攻击代码。
-
反编译 rtarget 程序
执行
objdump -d rtarget > rtarget.s
-
对照附录机器码表,查找可利用片段
编号 gadget地址 机器码 功能 1 0x401a8f+0x3 | 0x401a92 48 89 c7 c3 mov %rax,%rdi 2 0x401a8f+0x4 | 0x401a93 89 c7 c3 mov %eax,%edi 3 0x401a9d+0x3 | 0x401aa0 58 c3 pop %rax 4 0x401acd 48 8d 04 37 c3 lea (%rdi,%rsi,1),%rax 5 0x401af9+0x1 | 0x401afa 89 d6 c3 mov %edx,%esi 6 0x401b1b+0x3 | 0x401b1e 48 89 e0 c3 mov %rsp,%rax 7 0x401b1b+0x4 | 0x401b1f 89 e0 c3 mov %esp,%eax 8 0x401b3e+0x2 | 0x401b40 89 ca (20 d2) c3 mov %ecx,%edx 9 0x401b0d+0x2 | 0x401b0f 89 c1 (08 c9) c3 mov %eax,%ecx 注意:括号内字节码可忽视
阶段4
阶段 4 的目标同阶段 2,但我们需要利用 gadget 来修改 %rdi。
-
查看可利用的 gadget
我们可以利用 gadget3 来修改 %rax 的值,再利用 gadget1 将 %rax 赋给 %rdi。
pop %rax mov %rax,%rdi ret
-
查看 touch2 地址
-
编写 phase4.txt
-
使用 hex2raw 将 phase4.txt 转二进制文件 phase4-raw.txt
执行
./hex2raw < phase4.txt > phase4-raw.txt
-
运行 rtarget 查看结果
PASS 通过
阶段5
阶段 5 同阶段 3 ,但没办法直接计算地址。不过我们可以通过 gadget4 来间接计算地址,通过 gadget 6,1 将 %rdi 改为 %rsp ,通过 gadget 3,9,8,5 修改 %rsi,然后通过 gadget 4 计算地址,再通过 gadget 1 将 %rdi 该为计算后的地址。
-
创建 phase5.txt 按顺序将 gadget 地址写入
-
查看 touch3 地址
-
将 touch3 地址和偏移量 0x48(即,72 字节)写入 phase5.txt
-
使用 hex2raw 将 phase5.txt 转二进制文件 phase5-raw.txt
执行
./hex2raw < phase5.txt > phase5-raw.txt
-
运行 rtarget 查看结果
PASS 通过