如何利用Format-String漏洞?

时间:2011-09-18 05:17:56

标签: c security format-string

我正在阅读有关代码中的漏洞并遇到此格式字符串漏洞

Wikipedia说:

  

格式化字符串错误最常出现在程序员希望的时候   打印包含用户提供的数据的字符串。程序员可以   错误地写printf(缓冲区)而不是printf(“%s”,缓冲区)。该   第一个版本将缓冲区解释为格式字符串,并解析任何   它可能包含的格式说明。简单的第二个版本   像程序员想要的那样在屏幕上打印一个字符串。

我遇到了printf(缓冲区)版本的问题,但我仍然没有了解攻击者如何利用此漏洞执行有害代码。有人可以通过示例告诉我这个漏洞如何被利用吗?

5 个答案:

答案 0 :(得分:92)

您可以直接或间接地以多种方式利用格式字符串漏洞。让我们使用以下作为示例(假设没有相关的操作系统保护,这是非常罕见的):

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的{​​{1}}的值,如上所示:

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

有许多可能性和技巧可以尝试(直接参数访问,大字段宽度使环绕可能,构建自己的基元),这只是触及冰山一角。我建议阅读更多关于fmt字符串漏洞的文章(Phrack有一些非常优秀的文章,虽然它们可能有点先进)或者是一本涉及该主题的书。


免责声明:这些例子是由Jon Erickson撰写的 Hacking:剥削艺术(第2版)一书中的[虽然不是逐字逐句]。

答案 1 :(得分:15)

有趣的是,没有人提到过POSIX支持的n$符号。如果您可以将格式字符串控制为攻击者,则可以使用以下符号:

"%200$p"

读取堆栈上的200 th 项(如果有)。目的是你应该列出从1到最大的所有n$数字,它提供了一种重新排序参数如何出现在格式字符串中的方法,这在处理I18N时非常方便(L10N,G11N,M18N * )。

然而,一些(可能是大多数)系统在如何验证n$值方面有些缺乏实用性,这可能导致可以控制格式字符串的攻击者滥用。结合%n格式说明符,这可能导致在指针位置写入。


* 首字母缩略词I18N,L10N,G11N和M18N分别用于国际化,本地化,全球化和多国化。数字表示省略的字母数。

答案 2 :(得分:9)

啊,答案在文章中!

  

不受控制的格式字符串是一种在1999年左右发现的软件漏洞,可用于安全漏洞。以前认为无害的格式字符串漏洞可用于崩溃程序执行有害代码

     

典型的漏洞使用这些技术的组合来强制程序使用指向某些恶意shellcode的指针覆盖库函数的地址或堆栈上的返回地址。格式说明符的填充参数用于控制输出的字节数,%x标记用于从堆栈中弹出字节,直到达到格式字符串本身的开头。格式字符串的开头是精心设计的,其中包含 %n格式令牌随后可以使用要执行的恶意代码的地址覆盖的地址

这是因为%n 会导致printf 数据写入,这是一个位于堆栈上的变量。但这意味着它可以任意写入某些内容。所有你需要的是有人使用那个变量(如果碰巧是一个函数指针它相对容易,你只想知道如何控制它的值)它们可以让你任意执行任何东西。

看一下文章中的链接;他们look interesting

答案 3 :(得分:2)

我建议阅读有关格式字符串漏洞的this讲义。 它详细描述了发生了什么以及如何发生,并且有一些图像可以帮助您理解该主题。

答案 4 :(得分:0)

AFAIK主要是因为它可能导致程序崩溃,这被认为是拒绝服务攻击。您所需要的只是提供一个无效的地址(实际上任何,只有少数%s可以正常工作),它就变成了一种简单的拒绝服务(DoS)攻击。

现在,理论上可能可以在异常/信号/中断处理程序的情况下触发任何东西,但是弄清楚如何做到这一点超出了我 - 你需要弄清楚如何任意数据写入内存。

但是,为什么有人关心程序崩溃,你可能会问?这不仅仅给用户带来不便(无论如何还是应得的)?

问题是一些程序被多个用户访问,因此崩溃它们的成本是不可忽视的。或者有时它们对系统的运行至关重要(或者它们可能正在做一些非常关键的事情),在这种情况下,这可能会损害您的数据。当然,如果你崩溃记事本然后没有人可能会关心,但如果你崩溃CSRSS(我相信实际上有一个类似的错误 - 一个双重免费的错误,特别是)然后是的,整个系统正在与你同行


更新

有关我所指的CSRSS错误,请参阅this link


编辑:

请注意,读取任意数据与执行任意代码一样危险!如果您读取密码,cookie等等,那么它就像任意代码执行一样严重 - 如果您有足够的时间尝试足够的格式字符串,这就是琐碎