本次实验中用于攻击的目标程序名为 stack.c,位于 server-code 文件夹内。该程序具有缓冲区溢出漏洞,你的任务是利用此漏洞获得 root 权限。下面列出的代码去除了部分非关键信息,所以与从实验设置文件获取的内容略有不同。
/* 存在漏洞的程序 stack.c */
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
/* 更改此值将改变堆栈布局 */
#ifndef BUF_SIZE
#define BUF_SIZE 100
#endif
int bof(char *str)
{
char buffer[BUF_SIZE];
/* 下面的语句存在缓冲区溢出问题 */
strcpy(buffer, str);
return 1;
}
void foo(char *str)
{
...
bof(str);
}
int main(int argc, char **argv)
{
char str[517];
int length = fread(str, sizeof(char), 517, stdin);
foo(str);
fprintf(stdout, "==== Returned Properly ====\n");
return 1;
}
上述程序存在缓冲区溢出漏洞。它从标准输入读取数据,然后将这些数据复制到 bof() 函数中的另一个缓冲区内。原始输入的最大长度为 517 字节,但 bof() 中的缓冲区只有BUF_SIZE 个字节长(小于 517)。因为 strcpy() 不会检查边界,会发生缓冲区溢出。
该程序将在具有 root 权限的服务器上运行,并将标准输入重定向到服务器与远程用户之间的 TCP 连接。因此,程序实际上是从远程用户处获取数据。如果攻击者能够利用此缓冲区溢出漏洞,他们就可以在服务器上获得 root shell。
要编译上述存在漏洞的程序,需要使用 -fno-stack-protector 和 -z execstack 选项关闭 StackGuard 和非可执行堆栈保护。下面是一个编译命令的例子(环境变量 L1 设定了 stack.c 中的 BUF_SIZE 常量值)
$ gcc -DBUF_SIZE=$(L1) -o stack -z execstack -fno-stack-protector stack.c
我们将把 stack 程序编译成 32 位和 64 位两个二进制文件。我们的预构建的 Ubuntu 20.04 虚拟机是 64 位虚拟机,但仍支持 32 位二进制文件。我们只需要在 gcc 命令中使用 -m32 选项来实现 32 位编译。
对于 32 位编译,我们也使用 -static 生成一个静态链接的可执行文件,该文件是自包含的且不依赖于任何动态库。因为我们的容器中没有安装 32 位动态库。
编译命令已提供在 Makefile 中。要编译代码,请输入 make 来执行这些命令。变量 L1、L2、L3 和 L4 在 Makefile 中被设置,并会在编译过程中使用。
编译完成后,需要将可执行文件复制到 bof-containers 文件夹中以便容器可以使用。以下命令完成编译和安装。
在 server-code 文件夹中,可以找到名为 server.c 的程序。这是服务器的主要入口点。它侦听端口 9090。当接收到 TCP 连接时,会调用 stack 程序,并将该 TCP 连接作为标准输入提供给 stack 程序。这样,在 stack 读取标准输入数据时,实际上是读取了 TCP 连接上的数据,即这些数据由 TCP 客户端的用户提供。你们不需要阅读 server.c 的源代码。