exp1 逆向与Bof基础实验报告
20192213刘子谦
1 实验要求概述
一句话,通过三种方法,在linux(kali)环境中,尝试隐藏的执行getshell函数,或者其他自定义代码段
2 实验方案
2.1 NOP, JNE, JE, JMP, CMP汇编指令的机器码
- NOP:NOP指令即“空指令”。执行到NOP指令时,CPU什么也不做,仅仅当做一个指令执行过去并继续执行NOP后面的一条指令。(机器码:90),实验中我们构造滑行区就是用的这个机器命令x90。
- JNE:条件转移指令,如果不相等则跳转。(机器码:75)
- JE:条件转移指令,如果相等则跳转。(机器码:74)
- JMP:无条件转移指令。段内直接短转Jmp short(机器码:EB)段内直接近转移Jmp near(机器码:E9)段内间接转移Jmp word(机器码:FF)段间直接(远)转移Jmp far(机器码:EA)
- CMP:比较指令,功能相当于减法指令,只是对操作数之间运算比较,不保存结果。cmp指令执行后,将对标志寄存器产生影响。其他相关指令通过识别这些被影响的标志寄存器位来得知比较结果。
2.2 掌握反汇编与十六进制编程器
反汇编:把目标代码转为汇编代码的过程,也可以说是把机器语言转换为汇编语言代码
反汇编主要用的命令是objdump -d 目标文件 | more
十六进制编程器是wxHexEditor,可以进行十六进制的查看和相应修改,避免我们对vim编辑器的操作不熟悉导致的错误。
3 实验流程
3 本次实验说明
3.1 实践目标
本次实践的对象是一个名为pwn1的linux可执行文件。
该程序正常执行流程是:main调用foo函数,foo函数会简单回显任何用户输入的字符串。
该程序同时包含另一个代码片段,getShell,会返回一个可用Shell。正常情况下这个代码是不会被运行的。我们实践的目标就是想办法运行这个代码片段。我们将学习两种方法运行这个代码片段,然后学习如何注入运行任何Shellcode。
- 三个实践内容如下:
- 手工修改可执行文件,改变程序执行流程,直接跳转到getShell函数。
- 利用foo函数的Bof漏洞,构造一个攻击输入字符串,覆盖返回地址,触发getShell函数。
- 注入一个自己制作的shellcode并运行这段shellcode。
- 这几种思路,基本代表现实情况中的攻击目标:
- 运行原本不可访问的代码片段
- 强行修改程序执行流
- 以及注入运行任意代码。
4 我的实验流程
4.1 方法1:直接修改汇编代码
4.1.1 本方案原理
一句话,查看代码后发现:call指令后面对应的是foo函数的地址,改成对应我们需要的函数的地址即可
4.1.2 过程
4.1.2.1 下载pwn1可执行文件
4.1.2.2 查看汇编代码(反汇编)
在kali中执行以下命令,实现反汇编
objdump -d pwn | more
4.1.2.3 查看跳转地址
如图所示,跳转地址为:8048491
4.1.2.4 计算我们需要的地址
在科学计算器中,我们计算出了对应的地址
4.1.2.5 修改为我们需要的地址
修改并替换之前的地址即可
修改之后的效果如下:
16进制机器码修改完成以后,我们查看一下对应的汇编指令,需要的命令与第二步的命令相同,对当前文件进行反汇编即可
如图所示,已经成功将对应的跳转地址,改为了getShell函数所对应的地址位置,因此,程序不再会执行foo函数,而会执行getShell函数,下一步中,将测试一下效果。
4.1.2.6 测试效果
最后是第一条思路的效果测试:
如上图所示,我们已经完成了对对应文件的修改。原来的可执行文件的功能是输入一段字符串,程序会重复输出该字符串
现在我们的程序变成了打开一个“命令行”,如图所示,我们执行了ls命令后,成功打印出了当前目录下的所有文件
实验成功!
4.2 方法二:通过构造输入参数,造成BOF攻击,改变程序执行流
- 知识要求:Call指令,EIP寄存器,指令跳转的偏移计算,补码,反汇编指令objdump,十六进制编辑工具
- 学习目标:理解可执行文件与机器指令
- 进阶:掌握ELF文件格式,掌握动态技术
4.2.1 原理
一句话,输入超长字符串(合适长度),造成缓冲区溢出,从而实现覆盖掉之后的代码段,进而修改了跳转地址,从而达到跳转到对应函数的目的
4.2.2 过程
4.2.2.1 查看分析汇编代码
首先进行反汇编
输入:objdump -d pwn1 | more
反汇编之后,尝试读懂其中的代码
4.2.2.2 找到bof漏洞
我们可以利用foo函数的BOF漏洞:
这里读入字符串,但系统只预留了(28)字节的缓冲区,超出部分会造成溢出,我们的目标是覆盖返回地址
上面的call调用foo,同时在堆栈上压上返回地址值:(80484ba)
4.2.2.3 确认输入字符串哪几个字符会覆盖到返回地址
运行这步时需检查自己的虚拟机是否有安装gdb,在终端中输入gdb -v即可,若找不到该命令,则需先进行安装操作。参考课题负责人实验报告,依次输入下列代码:
sudo chmod a+w /etc/apt/sources.list
sudo chmod a-w /etc/apt/sources.list
apt-get update
apt-get install gdb
最后再通过gdb -v,显示出版本号即为安装成功。
随后根据实验指导书上的步骤进行分析
当输入的为1111111122222222333333334444444455555555可以看到eip的值0x35353535也就是5555的ASCII码:
如果输入字符串1111111122222222333333334444444412345678,那 1234 那四个数最终会覆盖到堆栈上的返回地址,进而CPU会尝试运行这个位置的代码。那只要把这四个字符替换为 getShell 的内存地址,输给pwn1,pwn1就会运行getshell。
4.2.2.4 3.覆盖返回地址
通过反汇编时可以看到getshell的内存地址,即0804847d。
输入11111111222222223333333344444444\x7d\x84\x04\x08,覆盖返回地址:
4.2.2.5 4.构造输入字符串
先生成包括\x7d\x84\x04\x08这样的16进制值字符串的一个文件。
输入perl -e ‘print “11111111222222223333333344444444\x7d\x84\x04\x08\x0a”’ > input
(关于Perl:Perl是一门解释型语言,不需要预编译,可以在命令行上直接使用。使用输出重定向“>”将perl生成的字符串存储到文件input中。)
如上图所示,我们成功执行了getShell
4.3 方法三:注入Shellcode并执行
4.3.1 原理
基础知识:
- shellcode就是一段机器指令(code)
通常这段机器指令的目的是为获取一个交互式的shell(像linux的shell或类似windows下的cmd.exe),
所以这段机器指令被称为shellcode。 - 在实际的应用中,凡是用来注入的机器指令段都通称为shellcode,像添加一个用户、运行一条指令。
4.3.2 过程
4.3.2.1 准备工作
先通过execstack - s指令来设置堆栈可执行
再用 execstack -q 指令查询文件的堆栈是否可执行
关闭地址随机化
注意,此次我们需要先安装prelink软件包
4.3.2.2 构造要注入的payload
根据实验指导书提示:
Linux下有两种基本构造攻击buf的方法:
retaddr+nop+shellcode
nop+shellcode+retaddr
因为retaddr在缓冲区的位置是固定的,shellcode要不在它前面,要不在它后面。
简单说缓冲区小就把shellcode放后边,缓冲区大就把shellcode放前边。
我们这个buf够放这个shellcode了
结构为:nops+shellcode+retaddr。
nop一为是了填充,二是作为“着陆区/滑行区”。
我们猜的返回地址只要落在任何一个nop上,自然会滑到我们的shellcode。
输入perl -e ‘print “\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\x4\x3\x2\x1\x00”’ > input_shellcode
再开另外一个终端,用gdb来调试pwn1这个进程。
先attach上该进程,随后设置断点。
找到shellcode
4.3.2.3 成功运行shellcode
5 总结
- 本次实验最大的感受就是看似困难的网络对抗技术其实实际上手后没有想象的那么难。对于Linux基础的学习平时接触的比较少,所以在这次的实验过程中属于是边学边用的状态,知识来源主要是老师在云班课的资源,在和其他同学讨论的过程中,完成了本次实验。
- 我认为本次实验最困难的一点是注入shellcode,它涉及到的覆盖过程比较复杂,需要清楚理解指令的内容及执行过程,在每一条指令执行过程之后,esp和ebp的位置要找得到,计算好“缓冲区”的区域大小。这是我认为的注入shellcode这部分的关键所在。