【问题标题】:Format String Vulnerability troubles格式化字符串漏洞问题
【发布时间】:2012-05-31 04:52:59
【问题描述】:

所以我有这个功能:

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 被我输入的字符串替换(减去前面的小端内存地址,两个百分号现在由于某种原因变成了一个百分号)。

无论如何,我在这里有点难过,任何帮助将不胜感激。

【问题讨论】:

  • printf()%% 格式化为 % 以便您的输出仍然可以包含 % 符号。
  • 哦,那我得用百分号了?
  • 相反,如果您希望输出具有%%,则格式字符串需要具有%%%%
  • 好吧,我并不关心我的输出,我只是希望将这些数字(代表我的 noop 地址)复制到指定的 .dotrs_end。我是一个 C 菜鸟,所以我很确定我在提供的字符串中搞砸了。

标签: c security string-formatting


【解决方案1】:

免责声明:我不是专家。

您将"\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字节地址&amp;buffer
  • 推送一个 4 字节的返回地址。
  • 调用printf...
  • 打印出"Usage:\x20\x10\xa2\x04\x08\x11\xa2\x04\x08\x12\xa2\x04\x08\x13\xa2\x04\x08"(23 个字节的序列)。
  • %.168u:将printf 的下一个参数解释为一个无符号整数,并将其打印在宽度为168 的字段中。由于printf 没有下一个参数,这实际上是要在堆栈上打印下一个东西;即buffer的前四个字节;即"Usag" (0x67617355)。
  • %1$n:将@​​987654340@ 的第二个参数解释为指向 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$s 添加到所有 %us 中。)
  • %.51u:再打印 51 个字节的垃圾。
  • %2$n:将 printf 的第三个参数解释为指向 int 的指针,并将 0x000000f2 存储在该垃圾位置。如上所述,这应该是%3$n
  • ...等...等...

因此,您的主要错误是您忘记考虑 "Usage: " 前缀。

我假设您试图将四个字节 0xffbfdbb8 存储到地址 0x804a210 中。假设你已经让它工作了。但是你的下一步是什么?如何让程序把0x804a210处的四字节数量当作函数指针进行跳转?

利用此代码的传统方法是利用sprintf 中的缓冲区溢出,而不是printf 中更复杂的"%n" 漏洞。您只需将arg 设置为大约 640 个字符长,并确保它对应于print_usage 的返回地址的 4 个字节包含您的 NOP 雪橇的地址。

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

这有帮助吗?

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-10-26
    • 2014-11-22
    • 1970-01-01
    相关资源
    最近更新 更多