格式字符串漏洞问题

时间:2012-05-24 15:13:04

标签: c security string-formatting

所以我有这个功能:

void print_usage(char* arg) 
{
    char buffer[640];
sprintf(buffer, "Usage: %s [options]\n"
        "Randomly generates a password, optionally writes it to /etc/shadow\n"
        "\n"
        "Options:\n"
        "-s, --salt <salt>  Specify custom salt, default is random\n"
        "-e, --seed [file]  Specify custom seed from file, default is from stdin\n"
        "-t, --type <type>  Specify different encryption method\n"
        "-v, --version      Show version\n"
        "-h, --help     Show this usage message\n"
        "\n"
        "Encryption types:\n"
        "   0 - DES (default)\n"
        "   1 - MD5\n"
        "   2 - Blowfish\n"
        "   3 - SHA-256\n"
        "   4 - SHA-512\n", arg);
    printf(buffer);
}

我希望利用格式字符串漏洞攻击(我的任务)。这是我的尝试:

我有一个漏洞利用程序,用noops和shell代码填充缓冲区(我用这个程序来缓冲溢出相同的函数,所以我知道它很好)。现在,我做了一个文件的对象转储来找到.dtors_list地址,我得到0x0804a20c,加上4个字节来得到结束我得到0x804a210。

接下来,我使用gdb查找运行程序时我的noops开始的地址。使用这个我得到了0xffbfdbb8。

所以到目前为止,我觉得我是正确的,现在我知道我想使用格式字符串将noop地址复制到我的.dtors_end地址。这是我提出的字符串(这是我作为函数的用户输入提供的字符串):

“\ X10 \ XA2 \ X04 \ X08 \ X11 \ XA2 \ X04 \ X08 \ X12 \ XA2 \ X04 \ X08 \ X13 \ XA2 \ X04 \ X08 %%。168U %% 1 $ N %%。51U% %2 $ N %%。228u %% 3 $ N %%。64U %% 4 $ N“

这对我不起作用。该程序正常运行,%s被替换为我输入的字符串(减去前面的小端内存地址,由于某种原因,两个百分号现在是百分号)。

无论如何,我有点难过,任何帮助都会受到赞赏。

1 个答案:

答案 0 :(得分:0)

免责声明:我不是专家。

您将"\x10\xa2\x04\x08\x11\xa2\x04\x08\x12\xa2\x04\x08\x13\xa2\x04\x08%%.168u%%1$n%%.51u%%2$n%%.228u%%3$n%%.64u%%4$n"作为arg的值传递?这意味着buffer将包含

"Usage:\x20\x10\xa2\x04\x08\x11\xa2\x04\x08\x12\xa2\x04\x08\x13\xa2\x04\x08%.168u%1$n%.51u%2$n%.228u%3$n%.64u%4$n [options]\x0aRandomly..."

现在让我们进一步假设您处于x86-32目标上(如果您使用的是x86-64,这将无效),并且您正在使用不会放置任何内容的优化级别进行编译在print_usage的堆栈帧中,除了640字节的buffer数组。

然后printf(buffer)将按顺序执行以下操作:

  • 按下4字节地址&buffer
  • 按下一个4字节的返回地址。
  • 调用printf ...
  • 打印出"Usage:\x20\x10\xa2\x04\x08\x11\xa2\x04\x08\x12\xa2\x04\x08\x13\xa2\x04\x08"(23个字节的序列)。
  • %.168u:将printf的下一个参数解释为unsigned int,并将其打印在宽度为168的字段中。由于printf没有下一个参数,因此实际上将打印堆栈中的下一件事;也就是说,buffer的前四个字节;即"Usag"0x67617355)。
  • %1$n:将printf的第二个参数解释为指向int的指针,并在该位置存储23 + 168。这会将0x000000bf存储在位置0x67617355中。这是您的主要问题:您应该使用%2$n代替%1$n并在arg的前面添加一个垃圾字节。 (顺便提一下,请注意GNU说"If any of the formats has a specification for the parameter position all of them in the format string shall have one. Otherwise the behavior is undefined."所以你应该通过1$添加%u到你所有的%.51u只是为了安全起见。)
  • %2$n:打印另外51个字节的垃圾。
  • printf:将0x000000f2的第三个参数解释为指向int并在该垃圾位置存储%3$n的指针。如上所述,这应该是"Usage: "
  • ...等等...

因此,您的主要错误是您忘记了0xffbfdbb8前缀。

我假设您尝试将四个字节0x804a210存储到地址0x804a210中。假设你已经开始工作了。但那么下一步将是什么?如何让程序将sprintf处的四字节数量视为函数指针并跳过它?

利用此代码的传统方法是利用"%n"中的缓冲区溢出,而不是printf中更复杂的arg漏洞。您只需要使print_usage长约640个字符,并确保其对应于0xffbfdbb8的返回地址的4个字节包含您的NOP底座的地址。

即使部分也很棘手。您可能会遇到与ASLR相关的事情:仅仅因为您的雪橇在一次运行中存在于地址{{1}}并不意味着它将在下一次运行中存在于同一地址。

这有帮助吗?