字符串格式化所导致的漏洞

1 HelloWorld

就个人来说,所学的c,第一个成功运行的程序既是HelloWorld了:

1
2
3
4
5
6
#include <stdio.h>
int main()
{
printf("Hello, World!");
return 0;
}

当时觉得很简单、很理所当然。然而,如此简单的程序之下,调用的printf,却是一个非常难理解的”变参函数”。

普通的参数遵守调用规则,从右到左参数依次压栈。而变参函数呢?堆栈又由谁恢复呢?

2 变参函数导致的问题

printf来说,该函数的函数原型是

1
int printf(const char *format,...);

简单来说,该变参函数,使用format所指向的字符串来解析后面不定长(以...表示)的参数。

那么,若format中所声明的参数,多于实际传入的变量又该如何呢?这就导致了所谓的fmt漏洞:printf函数会输出堆栈上传入参数之后的数据。

对于%s来说,会输出传入值所指向的字符串;对于%p来说,会输出传入的值。当然,也可以使用%c/%x/%d等方式输出堆栈上的数据。

3 任意位置

3.1 使用$说明参数编号

此处参数编号从format之后开始,从1数起。

1
2
3
4
5
6
int main()
{
printf("%3$s, %2$s!\n","x","World","Hello");
return 0;
}
//Hello, World!!

看wiki上说不是c99标准,但是感觉遇到的都实现了这玩意吧。

3.2 使用传入的参数作为地址

因为传入的参数也是在堆栈上,所以很随意就可以用$来指出来,再搭配$s一波美滋滋。

4 写数据

甚至你还可以用printf来写内存, 参数%i会统计当前已输出的字符个数,并赋值到传入值所指向的位置。

1
2
3
4
5
6
7
8
9
10
#include <stdio.h>
int main()
{
int x=0;
printf("Hello%n, World!\n",&x);
printf("%d",x);
return 0;
}
//Hello, World!
//5#

5 其他

突然、就水了一波。
最近在看CS的那些玩意,cna真乃神器。现在就差个beacon的源码搞一搞了,虽然看片子、看代码好像没有像那些exe能直接插的地方,但是说不定那些正版大佬的Arsenal就会附赠一份beacon源码呢。所以还是只能用那屎一样的ruby写的msf了么……