目录
- 引言
- 软件要求
- 启动虚拟机
- 环境设置
- 禁用地址空间布局随机化(ASLR)
- 设置编译器标志以禁用安全功能
- 概述
- BOF.c
- testShellCode.c
- 解释
- createBadfile.c
- 开始利用漏洞
- 在堆栈上查找返回地址
- 实验2的作业
之前有写过一个 博客,大家可以先看看栈溢出基础。
引言
在本实验中,您将了解如何利用缓冲区溢出和其他内存漏洞来接管易受攻击的程序。目标是研究我提供的程序,然后弄清楚如何使用它来获得对系统的 shell 访问权限。 1996 年,Aleph One 写了一篇关于粉碎堆栈的规范论文。您应该阅读本文,因为它详细描述了堆栈粉碎的工作原理。如今,许多编译器和操作系统已经实现了安全功能,可以阻止本文中描述的攻击。然而,它仍然为新的攻击和(特别是)这个实验室作业提供了非常相关的背景。
Aleph One: Smashing the Stack for Fun and Profit
Another (long) description of Buffer Overflows is here
软件要求
所有必需的文件和源代码都打包在提供的 Lab 2 虚拟机中。
The VMWare Software
https://www.vmware.com/
The VirtualBox Software
https://www.virtualbox.org/wiki/Downloads
https://www.vmware.com/support/developer/ovf/
https://www.mylearning.be/2017/12/convert-a-vmware-fusion-virtualmachine-to-virtualbox-on-mac/
The Kali Linux, Penetration Testing Distribution
GDB: The GNU Project Debugger
GCC, the GNU Compiler Collection
C source file including BOF.c, createBadfile.c, and testShellCode.c
启动虚拟机
Kali Linux VM 具有所有必需的文件。为此实验选择名为 Lab2BufferOverflows 的 VM。
使用用户名 root 和密码 [课堂上待定] 登录 Kali Linux。
在 Kali Linux 中,您应该能够看到一个名为 Lab2-BufferOverflows 的文件夹。该文件包含实验 2 的所有源代码。
环境设置
当前的编译器和操作系统中有许多保护措施可以阻止堆栈攻击,就像我们想要做的那样。我们必须禁用一些安全选项才能允许漏洞利用(请注意,您获得的虚拟机映像已配置环境)。
禁用地址空间布局随机化(ASLR)
地址空间布局随机化 (ASLR) 是当今大多数操作系统中使用的安全功能。 ASLR随机排列进程的地址空间,包括栈、堆和库。它提供了一种使利用难以成功的机制。您可以使用 /proc/sys/kernel/randomize_va_space 接口在 Linux 中配置 ASLR。支持以下值:
0 – No randomization
1 – Conservative randomization
2 – Full randomization Disable ASLR, run:
$ echo 0 > /proc/sys/kernel/randomize_va_space Enable ASLR, run:
$ echo 2 > /proc/sys/kernel/randomize_va_space
请注意,您需要 root 权限才能配置该接口。使用vi修改界面可能会出现错误。下面的屏幕截图显示了 /proc/sys/kernel/randomize_va_space 的值。
但是,此配置在重新启动后将无法保留。您必须在 sysctl 中进行配置。添加文件 /etc/sysctl.d/01-disable-aslr.conf 包含:
kernel.randomize_va_space = 0
这将永久禁用 ASLR。
下面的屏幕截图显示了 ASLR 配置。您可以打开一个终端并尝试一下。
设置编译器标志以禁用安全功能
当您使用 gcc 编译易受攻击的程序(在下一节中解释)时,请使用以下编译器标志来禁用安全功能。
-z execstack
关闭NX保护使堆栈可执行
-fno-stack-proector
删除检测堆栈粉碎漏洞的 StackGuard
-g
启用调试符号
概述
该漏洞利用的目的是教您缓冲区溢出是如何工作的。您必须通过将恶意输入传递到易受攻击的程序来获得 shell。该漏洞将名为“badfile”的文件作为输入。您的工作是创建一个错误文件,导致易受攻击的程序生成 shell。请注意,即使您的 shellcode 移动了几个字节,您也有一个 nop sled 来使漏洞发挥作用。在 Lab2BufferOverflows 文件夹中,它包含您需要使用的 C 文件。下面的屏幕截图表明了这一点。
BOF.c
在 BOF.c 中,有一个无界 strcpy,这意味着任何非 null 终止的内容都将覆盖缓冲区边界,并(希望)将一些信息放入您将设计的堆栈中。您的漏洞必须适用于我的 BOF.c 版本(不需要更改它以使您的代码正常工作)。
要编译 BOF.c,您需要添加提到的编译标志。
$ gcc –g –z execstack –fno-stack-protector BOF.c –o BOF
testShellCode.c
该程序只是让您测试 shell 代码本身。您可以找到或创建许多不同的“shell 代码”,这是了解它们的作用以及它们是否适合您(在您的操作系统上)的好方法。
您使用的实际 shellcode 只是此 C 代码的汇编版本:
#include <stdio.h>
int main( )
{
char *name[2];
name[0] = "/bin/sh";
name[1] = NULL;
execve(name[0], name, NULL);
}
解释
这段代码是一个简单的C程序,它定义了一个shellcode并直接执行它。这个shellcode的目的是启动一个shell。
const char code[] = \
"\x31\xc0" /* Line 1: xorl %eax,%eax */
"\x50" /* Line 2: pushl %eax */
"\x68""//sh" /* Line 3: pushl $0x68732f2f */
"\x68""/bin" /* Line 4: pushl $0x6e69622f */
"\x89\xe3" /* Line 5: movl %esp,%ebx */
"\x50" /* Line 6: pushl %eax */
"\x53" /* Line 7: pushl %ebx */
"\x89\xe1" /* Line 8: movl %esp,%ecx */
"\x99" /* Line 9: cdql */
"\xb0\x0b" /* Line 10: movb $0x0b,%al */
"\xcd\x80" /* Line 11: int $0x80 */
;
Line 1-2: 将EAX寄存器清零,并将EAX的值压入栈中,为系统调用创建一个参数(在x86架构中,系统调用的编号在AL寄存器中)。
Line 3-4: 将字符串//sh和/bin压入栈中,它们将构成/bin//sh的字符串,这是启动shell的命令。
Line 5: 将ESP(栈指针)的值复制到EBX寄存器,为系统调用准备它的第二个参数(指向命令字符串的指针)。
Line 6-7: 再次将EAX的值压入栈中,并将EBX的值压入栈中,为系统调用准备它的第三个和第四个参数(指向环境变量的指针和指向参数的指针)。
Line 8: 将ESP的值复制到ECX寄存器,为系统调用准备第一个参数(指向文件路径的指针)。
Line 9: 将EAX寄存器的值符号扩展到EDX:EAX,为系统调用准备。
Line 10-11: 将系统调用号(execve,即0x0B)加载到AL寄存器,并触发中断,执行系统调用。
int main(int argc, char ** argv) {
int (*func)();
func = (int (*)()) code;
(int)(*func)();
return 0;
}
main 函数中,定义了一个函数指针 func 并将其指向 code(即shellcode)。
通过调用 func 来执行shellcode。
程序返回0,表示正常退出。
createBadfile.c
该程序写出“badfile”,但目前它只是充满了 nops(无操作)。您需要修改它以将您的 shell 代码放入其中并导致代码跳转到 shellcode。 badfile 中已经包含的 shellcode(作为字符数组)确实可以工作。您不需要修改它,但欢迎您这样做。
要编译 testShellCode.c 和 createBadfile.c,您不需要添加前面提到的编译标志。你可以简单地用gcc编译它
开始利用漏洞
整个攻击流程:
使用 createBadfile.c 创建包含NOP sled和shellcode的 “badfile” 文件。
使用 BOF.c 程序读取 “badfile” 文件内容到 aString 缓冲区。
bufferOverflow 函数中的 strcpy 操作导致缓冲区溢出,覆盖了返回地址。
当 bufferOverflow 函数返回时,控制流跳转到NOP sled,然后滑落到shellcode。
Shellcode被执行,启动一个shell。
该实验确实面临两个挑战。要执行 shellcode,您需要覆盖 bufferOverflow() 函数中的返回地址。您必须使该函数的返回地址指向您的 shellcode。
-
你需要找出返回地址存储在哪个内存地址。
-
然后你需要找出你的shellcode在内存中的地址,并将shellcode的地址写入你在步骤1中找到的返回地址。
我将为您提供有关步骤 1 的一些提示。
在堆栈上查找返回地址
为了找到堆栈上的返回地址,我们首先使用 GDB(GNU 项目调试器)来查看汇编代码。您可以从这里 找到有关 GDB 的更多信息。请注意,您还可以使用工具 objdump 来读取汇编代码。
$ gdb BOF
首先,我们反汇编BOF程序的main()函数。我们在 main() 函数中找到了 bufferOverflow() 函数(在 GDB 中输入 disas main)。然后,我们反汇编了 bufferOverflow() 函数,该函数存在漏洞。
$ (gdb) disas main
$ (gdb) disas bufferOverflow
您需要了解汇编代码才能找到返回地址在堆栈上的位置。接下来,在 GDB 中输入 run 来执行 BOF 程序。
$ (gdb) run
正如我们所预料的,BOF 程序生成了一个异常,即分段错误。指令指针(EIP)是0x90909090。这是因为我们在 BOF 程序中的缓冲区溢出的 badfile 上放置了 NOP sled。
您还可以通过在GDB中执行info register来查看更多寄存器信息
$ (gdb) info register
请注意,您始终可以在 GDB 中键入帮助来学习命令。
实验2的作业
一个 zip 文件包含:
-
更新后的 createBadfile.c,用于生成 BOF 程序的输入。
-
badfile 的副本。当 BOF 从 VM 中的命令行运行时,必须生成 shell
-
使用 BOF 程序获取 shell 的屏幕截图(参见下面的简单屏幕截图)
-
包含以下问题答案的文本文件:
a. 当你在没有“-z execstack”的情况下编译时会发生什么?
【如果不使用-z execstack选项,gcc编译器默认会启用NX(No eXecute)保护,这意味着栈是不可执行的。在这个程序中,shellcode存储在栈上,由于栈被标记为可写的,它不能被执行。因此,当程序尝试执行shellcode时,会因为尝试在非可执行段(栈)中执行代码而崩溃并引发段错误。】
b.如果启用 ASLR 会发生什么?返回地址有变化吗?
【ASLR(Address Space Layout Randomization)的意思是操作系统将帮助随机化程序的虚拟内存地址,例如栈、堆、动态库以及程序本身的地址。不同版本的操作系统会以不同的方式实现ASLR。如果程序没有被编译为位置无关可执行文件(PIE),程序本身的地址将不会被随机化。在实验中,如果没有使用-fno-pie禁用PIE,那么程序本身的地址将会被随机化,返回地址也会随之改变。】
c.当您使用 GDB、/home/root/Desktop/Lab2-BufferOverflows/BOF 和 ./BOF 运行 BOF 时,内存中 buffer[] 的地址是否会发生变化?
【是的,buffer[]在内存中的地址会改变。程序的入口并不是main函数,main函数是在__libc_start_main函数中被调用的,而__libc_start_main是在_start函数中被调用的。由于函数调用,argc、argv、envp等都会被压入栈中。argv[0]在不同的运行环境下可能会有所不同,而且GDB和不同shell之间的环境变量也可能不同。GDB启动时默认会使用绝对路径运行,因此压入栈中的内容也不一样,导致栈上数据的地址也不一样。解决这个问题的一种方法是在相同的环境变量下运行生成badfile的程序,这样就可以自动计算出栈上数据的偏移。此外,如果远程环境未知,也可以通过栈喷射等技术来解决。在本例中,由于使用strcpy进行溢出,ROP技术的使用受到限制(需要避免0字节),但仍然可以构造puts ROP来泄露libc基址,然后修改badfile直接攻击libc的Offset。】