格式字符串是包含文本和格式参数的ASCII字符串。
例子:
// A statement with format string
printf("my name is : %s\n", "Akash");
// Output
// My name is : Akash
有几种格式字符串指定使用C和许多其他编程语言进行输出,但是我们的重点是C。
格式字符串漏洞是一类利用易于避免的程序员错误的漏洞。如果程序员将攻击者控制的缓冲区作为参数传递给printf(或任何相关函数,包括sprintf,fprintf等),则攻击者可以对任意内存地址执行写操作。下面的程序包含这样的错误:
// A simple C program with format
// string vulnerability
#include
int main(int argc, char** argv)
{
char buffer[100];
strncpy(buffer, argv[1], 100);
// We are passing command line
// argument to printf
printf(buffer);
return 0;
}
由于printf具有可变数量的参数,因此它必须使用格式字符串来确定参数数量。在上述情况下,攻击者可以传递字符串“%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p”,并愚弄printf认为它是有15个参数。它会天真地将堆栈中的下15个地址打印出来,并认为它们是它的参数:
$ ./a.out "%p %p %p %p %p %p %p %p %p %p %p %p %p %p %p"
0xffffdddd 0x64 0xf7ec1289 0xffffdbdf 0xffffdbde (nil) 0xffffdcc4 0xffffdc64 (nil) 0x25207025 0x70252070 0x20702520 0x25207025 0x70252070 0x20702520
在堆栈上大约有10个参数时,我们可以看到0x252070的重复模式–这些是我们在堆栈上的%ps!我们以AAAA开头字符串,以更明确地看到这一点:
$ ./a.out "AAAA%p %p %p %p %p %p %p %p %p %p"
AAAA0xffffdde8 0x64 0xf7ec1289 0xffffdbef 0xffffdbee (nil) 0xffffdcd4 0xffffdc74 (nil) 0x41414141
0x41414141是AAAA的十六进制表示。现在,我们有一种方法可以将任意值(在本例中,我们将传递0x41414141)作为printf的参数。此时,我们将利用另一个格式字符串功能:在格式说明符中,我们还可以选择一个特定的参数。例如,printf(“%2 $ x”,1,2,3)将打印2。通常,我们可以执行printf(“%$ x”)为printf选择一个任意参数。在我们的例子中,我们看到0x41414141是printf的第十个参数,因此我们可以简化我们的string1:
$ ./a.out 'AAAA%10$p'
AAAA0x41414141
防止格式字符串漏洞
- 始终将格式字符串指定为程序的一部分,而不是输入。通过将“%s”指定为格式字符串,而不是将数据字符串用作格式字符串,可以解决大多数格式字符串漏洞
- 如果可能,将格式字符串为常量。将所有变量部分提取为调用的其他参数。难以处理某些国际化库
- 如果以上两种做法都不可行,请使用防御措施,例如Format_Guard。在设计时很少见。也许是继续使用旧版应用程序并降低成本的一种方法。增加对第三方应用程序将是安全的信任。
参考
https://www.owasp.org/index。 PHP/ Format_string_attack
https://www.exploit-db.com/docs/28476.pdf