我正在阅读有关代码中的漏洞并遇到此格式字符串漏洞。
格式化字符串错误最常出现在程序员希望的时候 打印包含用户提供的数据的字符串。程序员可以 错误地写printf(缓冲区)而不是printf(“%s”,缓冲区)。该 第一个版本将缓冲区解释为格式字符串,并解析任何 它可能包含的格式说明。简单的第二个版本 像程序员想要的那样在屏幕上打印一个字符串。
我遇到了printf(缓冲区)版本的问题,但我仍然没有了解攻击者如何利用此漏洞执行有害代码。有人可以通过示例告诉我这个漏洞如何被利用吗?
答案 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等等,那么它就像任意代码执行一样严重 - 如果您有足够的时间尝试足够的格式字符串,这就是琐碎。