📜  使用示例格式化字符串漏洞和预防措施

📅  最后修改于: 2021-05-25 18:22:14             🧑  作者: Mango

格式字符串是包含文本和格式参数的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

想要从精选的最佳视频中学习和练习问题,请查看《基础知识到高级C的C基础课程》。