缓冲区溢出是一种软件漏洞类型,发生在程序向内存块(缓冲区)写入的数据超过了为其分配的空间。这可能导致各种问题,包括崩溃、意外行为,有时还包括安全漏洞,攻击者可以利用这些漏洞。
以下是与缓冲区溢出相关的关键概念的简要概述:
Overview
Buffer overflow is defined as the condition in which a program attempts to write data beyond the boundary
of a buffer. This vulnerability can be used by a malicious user to alter the flow control of the program,
leading to the execution of malicious code. The objective of this lab is for students to gain practical insights
into this type of vulnerability, and learn how to exploit the vulnerability in attacks.In this lab, students will be given four different servers, each running a program with a buffer-overflow
vulnerability. Their task is to develop a scheme to exploit the vulnerability and finally gain the root privilege
on these servers. In addition to the attacks, students will also experiment with several countermeasures
against buffer-overflow attacks. Students need to evaluate whether the schemes work or not and explain
why. This lab covers the following topics:
- Buffer overflow vulnerability and attack
- Stack layout in a function invocation
- Address randomization, Non-executable stack, and StackGuard
- Shellcode. We have a separate lab on how to write shellcode from scratch
该实验室的目标是让了解对这种类型的漏洞,并学习如何在攻击中利用漏洞。实验中,将有四个不同的服务器,每个服务器都运行一个具有缓冲区溢出漏洞的程序。实验的任务是开发一种利用漏洞的方案,并最终获得这些服务器上的ROOT权限。除了攻击,本次实验还介绍了几种应对缓冲区溢出攻击的对策。
实验资料网址:https://seedsecuritylabs.org/Labs_20.04/Software/Buffer_Overflow_Server/
实验要求:请修改 shellcode,可以使用它来删除文件。
先修改shellcode_32.py中的代码:
我们想让shellcode运行一些其他命令,我们只需要修改特定的命令字符串即可。但是,在进行更改时,我们需要确保不要更改该字符串的长度。如果我们改变长度,我们需要修改二进制部分。要使该字符串末尾的星号保持在同一位置,可以添加或删除空格。
我们要shellcode执行的命令为:删除当前目录的jay1an_test文件。
生成codefile_32,运行a32.out查看结果:
发现第二次的ls命令的返回结果中没有了test_jay1an文件,证明我们成功实现了文件删除的功能。
在进行Task 2\3\4\5之前,将Linux的地址随机化对策关闭:
先修改attack-code文件夹中的exploit.py文件:
修改shellcode,复制task1中的shellcode,将执行的命令换为反弹shell的命令,模拟攻击者将服务器的shell反弹到本机上。
修改start、ret、offset值:
通过正常的传参测试发现 &bof()=0xffffd348,&buffer=0xffffd2d8,所以二者的差 &bof()-&buffer=0x348-0x2d8=112
所以我们将offset的值设为112+4,并且把shellcode放在content数组的后面,也就是badfile的尾部。
检测结果:
在“本机“ 12345端口打开nc监听。
生成badfile并发送至server-1,实施攻击。
在本机nc监听成功。
分析:
为什么start=512-len(shellcode),ret=&bof + 20,offset = &bof-&buffer+4?
这样设置start的值,目的是让shellcode在badfile的最后部,即shellcode和RET之间的NOP指令尽可能多。
那么该程序的结构大致如下:
因为输入的517bytes的内容大于函数bof()给buffer数组分配的空间,也就是说,多余的会超出buffer所享有空间,而这多余的部分会覆盖EBP和RET,导致bof()函数结束后会跳转到某个未知的地址,而这个地址就是被覆盖后原RET位置上的值。
我们可以构造badfile函数的内容,使得函数结束后跳转到我们预想的地方。那么我们就需要知道修改badfile内容中的哪一个部分才能精准覆盖RET。根据前面server返回的信息可以算出&bof-&buffer=112,所以offset应该为112+4,因为EBP的上一个值是RET。
那么ret的值怎么定呢?ret的值只要是shellcode和RET之间的任意一个NOP指令即可,所以*ret的值应该为&bof加上一个值,这个值的最小值为8(42)**,也就是说,一定要跨越EBP和RET。
分析:
Level-2 Attack比Level-1 Attack不同的就是:在server输出端隐藏了&bof的值,只给出了&buffer的值。
意味着,现在攻击者不知道buffer的size具体有多大,那么就不能“精确“地覆盖RET了,但是由于在栈中(32bits程序),每一帧的size都是4bytes,所以攻击者可以将从&buffer起的栈帧都填成精心设定的ret,比如说,我们将从&buffer起的60个栈帧(604=240byets)都填成精心设定的ret,只要真正的RET位置在这240bytes内,那么当bof()函数结束后,仍然会返回我们精心设定的ret值。*这样我们只需要保证ret值在shellcode和RET中间的NOP指令上即可。
实验过程:
正常输入,发现 &buffer=0xffffd288
根据上面的分析修改exploit.py代码:
测试结果:
在本机上监听。
生成badfile,攻击server-2.
本机上监听成功。
实验成功。
Server-3给出了&buffer和&bof的值,只是变成了64位程序。
实验步骤:
正常输入,查看&buffer和&bof。
根据&buffer和&bof的值修改exploit.py文件。
结果检测:
在本机上12345端口上监听。
生成badfile文件,攻击server3。
本机成功连接到server-3.
分析:
由于64位程序的特性**(0x41à0x0041,strcmp函数会在00处终止,故无法用NOP过渡)**,我们将shellcode的位置从badfile的末尾调到了badfile的头部,也就是把start的值改为0。
那么程序的结构大致如下:
这样,ret值设置为&buffer,offser设置为&bof-&buffer+8,就会使得RET被覆盖为ret,即函数结束时电脑会执行地址为ret的代码,正是我们设置的shellcode。
Server-4的buffer数组太小,不能使用Task4的方法,不能将shellcode放在&buffer和&bof之间了。
分析:但是shellcode是在main函数中,作为参数传给bof函数,所以shellcode在main函数的代码段中仍然存在,可以使得bof函数结束后直接跳转到main函数中的shellcode地址。
那么程序的结构因该大致如下:
设置offset为&bof-&buffer+8,ret设置为main函数中的shellcode的地址,使程序结束后执行main函数中的shellcode。
实验步骤:
正常输入得到&buffer和&bof的值。
根据&buffer和&bof的值修改exploit.py中的start、ret、offset。
结果检验:
本机12345端口开启nc监听:
生成badfile并发送至server-4进行攻击:
本机成功收到server-4反弹的shell。
为什么ret = &bof + 1234 ?
因为dummy函数中的数组是1000bytes,在main函数中NOP指令是可以奏效,只需要大致定位到main函数中的517字节的数据块即可,简单尝试几次即可。
实验步骤:
开启地址随机化:
测试:
server-1:
server-3:
发现这三次的&bof和&buffer的地址都发生了变化,这意味着我们不能用原来的办法,不能用固定的内存地址去指定溢出内存地址,这无疑增加了栈溢出攻击的难度。
使用自带的爆破工具对32位server-1进行爆破:
原理:因为32位程序所使用的随机位数不多,所以可以通过暴力遍历的方式实现攻击。
先在本机上的端口开启监听,然后运行爆破程序:
正常来说,几分钟后即可成功。
进入 server-code 文件夹,去除 -fno-stack-protector 编译 stack.c,并将 badfile 作为输入。
然后系统会检测到stack smashing,会终止该进程。
在shellcode文件夹下,去除-z execstack,再编译call_shellcode.c,执行生成的out文件会出现Segmentation fault,栈不再运行了。