基于堆栈的缓冲区溢出对以 fclose() 结尾的函数的可利用性

逆向工程 静态分析 开发
2021-06-18 13:57:48

我一直在讨论 GNU libc 的fclose()功能在没有指向 FILE 数据结构的有效指针的情况下调用时阻止成功利用由于分段错误而导致的微不足道的漏洞的有效性(libc 在其他操作系统上的实现会静默失败)。例如,给定一个简单的易受攻击的函数,strcpy()例如:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void
foo(const char *str)
{
  char buffer[256];
  int ret = 123456;

  FILE *fp = tmpfile();
  strcpy(buffer, str);
  if (ret != 500)
    fclose(fp);
}

int 
main(int argc, const char **argv)
{
  foo(argv[1]);
  return 0;
}

即使在禁用大多数内存保护机制的情况下进行编译:

cc -m32 -ggdb -fno-stack-protector -z execstack -z norelro main.c

该函数,无论是否被覆盖EIP都不会返回,因为:

(gdb) r $(python -c "print 'A' * 286")
Program received signal SIGSEGV, Segmentation fault.
0xf7e3e307 in fclose@@GLIBC_2.1 () from /lib/libc.so.6
(gdb) ba
#0  0xf7e3e307 in fclose@@GLIBC_2.1 () from /lib/libc.so.6
#1  0x080484bc in foo (str=0x41414141 <error: Cannot access memory...
#2  0x41414141 in ?? ()
#3  0x41414141 in ?? ()

有没有办法实现这种微不足道的功能的代码执行?由于该ret变量可以被覆盖,是否可以将其设置为 500 以便fclose()可以绕过确定是否调用的 if 条件

1个回答

由于该ret变量可以被覆盖,是否可以将其设置为 500 以便fclose()可以绕过确定是否调用的 if 条件

如果攻击者可以ret用任意值覆盖堆栈上的变量,那么是的。

并且即使攻击者无法覆盖ret,他们仍然可以使用fp可接受的值覆盖fclose()

此外,您的方法依赖于几个特定于实现的细节。例如:

  • 您假设它fp介于buffer和 返回地址之间,但编译器可能不会以这种方式对变量进行排序。
  • 您假设fclose()在给出损坏的指针时总是会抛出异常,但这不是必需的,并且可能会在 GNU libc 的未来版本中发生变化。