强制程序使用输入字符串调用C中的函数

时间:2015-02-27 19:05:53

标签: c gdb buffer machine-code

所以我正在做一个练习,我想通过输入缓冲区来调用函数void not_called()。基本上我想要做的是使用缓冲区溢出来调用not_called()。我通过使用二进制漏洞字符串然后使用程序hex2raw接近这一点(采用十六进制格式然后将其转换为十进制数字的ASCII。)我然后将该二进制利用字符串放入.txt文件,然后使用unix终端中的一系列管道来调用not_called(),如下所示:

cat exploit.txt | ./hex2raw | ./nameofpgrm

所以我正在努力寻找二进制利用字符串。我想我需要做的是找到内存中用objdump调用not_called的位置,但我不确定。对我能做什么的任何帮助?我知道我将不得不使用gdb来找到它。我只是不知道在哪里看。

#include <stdlib.h>
#include <stdio.h>
void echo();

/* Main program */
int main() {
  while (1)
      echo();
  return(0); // never called
} // main

/* My gets -- just like gets - Get a string from stdin */
char *mygets(char *dest) {
  int c = getchar();
  char *p = dest;
  while (c != EOF && c != '\n') {
    *p++ = c;
     c = getchar();
  }
  *p = '\0';
  return dest;
} // mygets

/* Echo Line */
void echo() {
  char buf[4];    /* Way too small */

  mygets(buf);
  puts(buf);
} // echo

void not_called() {
  printf("This routine is never called\n");
  printf("If you see this message, something bad has happend\n");
  exit(0);
} // not_called

1 个答案:

答案 0 :(得分:1)

您希望使用从stdin读取的字节覆盖函数echo中的返回地址,以便现在指向not_called入口点。

让我们使用例如Mac OS / X 10.10 aka Yosemite。我简化了代码并添加了一个额外的printf来获取函数not_called的实际地址:

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

void echo(void) {
    char buf[4];    /* Way too small */
    gets(buf);
    puts(buf);
}

void not_called(void) {
    printf("This routine is never called\n");
    printf("If you see this message, something bad has happened\n");
    exit(0);
}

int main(void) {
    printf("not_called is at address %p\n", not_called);
    echo();
}

让我们使用clang编译并执行此代码:

chqrlie> clang t20.c && ./a.out

输出很清楚:

not_called is at address 0x106dade50
warning: this program uses gets(), which is unsafe.

使用十六进制编辑器,让输入硬币并将其粘贴到控制台:短缓冲区buf在64位上对齐,比保存的堆栈帧指针副本低8个字节{{1 },本身后跟我们要覆盖的返回地址。十六进制的输入例如是:

rbp

让我们将这24个字节粘贴到控制台并点击回车:

0000  3031 3233 3435 3637-3839 3031 3233 3435  0123456789012345
0010  50de da06 0100 0000-                     P��.....

函数0123456789012345P��^F^A^@^@^@ 0123456789012345P��^F^A This routine is never called If you see this message, something bad has happened Segmentation fault: 11 使用gets来读取stdin,24个字节存储在echo的末尾之外,覆盖帧指针buf,返回地址和额外的0字节。 rbp然后调用echo输出puts中的字符串。输出在第一个&#34;&#39; \ 0&#39;&#34;正如所料。然后从堆栈中恢复buf并获得损坏的值,控制转移到返回地址。返回地址被函数rbp覆盖,以便接下来执行的操作。实际上,我们看到来自函数not_called的消息,由于某种原因not_called崩溃,而不是优雅地退出流程。

我故意使用exit,因此读者了解使用此函数导致缓冲区溢出的容易程度。无论缓冲区有多大,输入都可以被创造出来以使程序崩溃或使它做有趣的事情。

另一个有趣的发现是Mac OS / X如何试图阻止攻击者过于轻松地使用这个技巧:程序打印的地址因执行而异:

gets

每次都会在不同的地址加载代码,随机选择。 使函数chqrlie > ./a.out < /dev/null not_called is at address 0x101db8e50 warning: this program uses gets(), which is unsafe. chqrlie > ./a.out < /dev/null not_called is at address 0x10af4ae50 warning: this program uses gets(), which is unsafe. chqrlie > ./a.out < /dev/null not_called is at address 0x102a46e50 warning: this program uses gets(), which is unsafe. 返回echo所需的输入每次都不同。尝试自己的操作系统并检查它是否使用此技巧。尝试创建适当的输入以完成工作(这取决于您的编译器和您的系统)。玩得开心!