赞
踩
RE最近计算机系统课上布置了attacklab,在完成后打算做一个总结,以便后续复盘。
按照助教给的网址,通过学号和学号邮箱获得压缩包。再Linux命令行里利用指令 tar -xvf targetk.tar 将压缩包解压到当前目录下,里面包含以下文件:
1、README.txt:对于实验的说明
2、ctarget:被我们攻击的可执行文件
3、rtarget:被我们攻击的可执行文件
4、cookie.txt:一个8位十六进制数,每个人的都不相同,可能会在后面的实验中使用到。
5、farm.c:一个源文件,里面是许多函数的集合。主要用于攻击rtarget时,找到我们需要的攻击代码
6、hex2raw:一个工具程序,我们按照规定格式输入,hex2raw会把我们的输入转化为字符串。
这里详细说明一下hex2raw的使用:输入格式,一连串的二位十六进制数,每两位之间用空白符隔开。比如说:输入 00 ff 如果我们直接把它输入到输入流中,那么系统会把我们的输入(两个0,一个空格,两个f)都转化为对应的ASCII值,存入到内存当中。而实际上,我们想输入的就是00 ff本身,我们希望内存中存储的值就是00 ff,为了实现目的,我们不能直接输入,而应该做转化。hex2raw的作用就在于帮我们做出这个转化
实验原理是栈溢出攻击。我们的程序是这样的:再main函数里调用test函数
test函数中又调用getbuf函数,getbuf函数中又调用Gets函数。Gets从stdin中读取字符串,但是它不会限制读取字符串的长度。所以,如果输入的字符串过长,超出程序为Gets提供的栈帧的范围,他就会覆盖掉一些信息,从而改变程序运行状态。
对于ctarget的攻击,采用注入代码的方式,因为ctarget的栈没有被设定为可读可写不可执行。
但是对于rtarget,它采取了栈随机化,且限制栈可读可写不可执行,所以无法采用注入代码的方式。而采用ROP技术攻击,后面会讲到如何去做。
下面是test和getbuf函数的源代码
要求:输入一个字符串,覆盖返回地址,使得getbuf返回后程序没有回到test函数,而是进入到touch1函数。
下面看getbuf和touch1函数的汇编代码:
test函数中调用getbuf函数,在进入getbuf函数之前,会首先把返回地址压入栈。随后进入getbuf函数,由汇编代码可知,getbuf函数栈帧有56个字节的大小。
再来看Gets函数,它的第一个参数是%rsp,也就是栈顶地址。而Gets函数和字符串输入函数gets一致。所以可知,Gets函数从栈顶开始把输入的字符串存到栈中。getbuf函数栈帧中有56个字节,这56个字节之下就是返回地址。我们只需要随意输入前56个字节,然后输入touch1函数的地址覆盖掉返回地址,如此便完成了phase1.
创建一个文件pahse1.txt
再使用指令./hex2raw<phase1.txt|./ctarget 通过了phase1。其栈帧图以及运行结果如下:
getbuf返回地址 8bytes |
getbuf栈帧 56bytes |
做一个解释:注意到我输入的touch1函数的地址是小端法输入的。因为我的系统是小端法存储的。对于ret指令,从栈中弹出下一条指令的地址,这个指令是八字节的,从栈中弹出时,按小端法来处理。
要求:需要我们注入一段代码,使得getbuf返回到touch2,并且在touch2中输出有效。
我们先来看touch2函数源代码:
当val值等于cookie时,才会有效输出。所以想完成此phase,需要我们先把cookie值移动带寄存器%rdi,然后再利用ret指令,返回到touch2。
我们可以覆盖原先的getbuf返回地址,使得getbuf结束之后返回到我们注入的一段代码中。这段代码首先把cookie值移动到rdi,然后返回到touch2函数。
先反汇编touch2函数,结果如下:
可以得到touch2函数的返回地址。
所以我们的注入代码应该为:
存在hex.s文件中
后面的push和ret相当于调用了touch2函数
所以我们只需转换为机器码,获得机器码的方法:
在linux终端中依次执行 gcc -c hex.s 和obgdump -d hex.o>hex.txt
再查看hex.txt,输入运行后发现出现段错误。
仔细检查后发现,push后的0x401781没有加上$来表明他是一个立即数。
修改后得到机器码:
注意最后一行是开始输入字符串的首地址。这里我是从字符串的一开始就输入机器码,所以最后一行恰好是进入Gets前%rsp的值。
运行结果如下:
要求:注入一段代码,使得getbug结束后返回到touch3函数,并产生有效输出。
touch3代码如下:
他有一个指针类型的参数sval,在进入hexmatch后,调用函数hexmatch,如果返回值为1则输出有效。所以我们只需要设置正确的sval,使得hexmatch函数返回1即可。
而hexmatch函数:
把unsigned型的val以16进制形式输入到字符串s中,然后比较s与sval,若相同则hexmatch返回1。
具体到phase3,val是cookie的值,也就是说需要sval应该是指向字符串,字符串为“4754662b”
我们来看hexmatch的汇编代码:
他会随机在某个位置写入val,所以这次我们的代码注入位置需要注意,可能会被覆盖。
所以选择在test的栈帧中存放我们的注入代码。
需要实现的:getbuf返回,进入到我们的注入代码
我把字符串形式的cookie存入到getbuf返回地址上方(因为cookie在getbuf栈帧被覆盖后仍被使用,如果不存在test栈帧里,就会被覆盖,无法使用),把注入代码存入到getbuf栈帧里。
栈帧模式图如下:紫色部分是进入touch3后的栈帧。
完成第三关。
从rtaget开始,难度开始提升。
一方面使用了栈随机化技术,每台机器的栈地址都是随机的。所以我们不可能再像前三关那样,直接在gdb中查看栈地址,并注入到代码中。相反的,我们需要用到相对寻址。
另一方面,对栈设定为可读可写不可执行。所以,直接将代码注入栈中的话,栈中的代码不能被执行。因而采用rop技术,通过ret来实现我们需要的代码。
要求:和phase2一样,要求进入到touch2函数并且产生有效输出。所以需要把unsigned类型的cookie传给寄存器%rdi,然后通过ret指令进入到touch2函数中。
lab提供了一个farm.c文件,里面包含很多函数。反汇编rtarget,也会得到farm.c中函数的机器代码以及机器代码存放的地址,这些机器代码没有存放在栈中,是可以执行的,可供我们使用。
我们根据上面的表首先在rtarget的反汇编文件中查询是否有pop %rdi,查询后发现没有。
所以只能考虑pop到另一个寄存器,再把值传送到%rdi中。 依次查询目的寄存器为%rdi的mov指令,发现存在48 89 c7(也就是从%rax传送到%rdi) 且下一个指令恰好是c3(ret)指令。
代码地址为0x401935
再查询是否有pop %rax,
恰好有50 90 c3(lab说明中提到了,90是无用指令,相当于不做任何操作) 所以也找到了。
代码地址401958
与时我们的模式图
结果如下:
原理上和phase4一致,只是更复杂了一些。
要求:进入touch3并且实现有效输出。
也就是说,我们需要传一个参数给touch3,这个参数是一个指向字符串的指针,字符串的内容是cookie。
但是字符串cookie的首地址不好得到,需要某一处的%rsp加上偏移量。
如何实现这个加上偏移量?可以观察farm.c中的函数,注意到有一个神奇的函数
可以实现两个值相加,如果我把%rsp的值给x(rdi),偏移量给y(rsi),再调用这个函数,不就实现了我们的目的了吗。
首先查询是否能直接pop %rsp 查询不到。
然后查询是否能把%rsp的值传送到其他寄存器。
rsp->rax:
在farm.c提供的函数中的只有这一个。但是%rax还要用于pop,所以我们继续寻找,在movl指令。
发现确实只有这一种那么只能采用这一种了。
在查询哪些寄存器可以把值传送给rdi或edi,rsi或esi
edx->esi:
eax->ecx
ecx->edx:
所以考虑先把rsp->rax->ecx->edx->esi
再pop %rax rax->rdi
然后调用加法函数,结果是cookie地址在rax中
然后rax->rdi 然后调用touch3
栈模式图如下:
运行结果:
完成了五个phase。
特别注意用的系统是大端存储还是小端存储。字符串的存储不区分大小端。
另外:如果你用的是wsl2,那么可能会出现ctarget无法运行的情况,建议使用vmware虚拟机。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。