缓冲区溢出攻击

20

我正在尝试执行一个非常简单的缓冲区溢出攻击。我对此基本上是新手。因此,如果这个问题很蠢,请原谅我 :-)

代码:

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

int i, n;

void confused(int i) 
{
 printf("**Who called me? Why am I here?? *** %x\n ", i);
}

void shell_call(char *c) 
{
 printf(" ***Now calling \"%s\" shell command *** \n", c);
 system(c);
}

void victim_func()
{
 int a[4];
 printf("Enter n:  ");  scanf("%d",&n);
 printf("~~~~~~~~~~~~~ values and address of n locations ~~~~~~~~~~");
 for (i = 0;i <n ;i++) 
  printf ("\n a[%d] = %x, address = %x", i, a[i], &a[i]);
 printf("\nEnter %d HEX Values \n", n);

 // Buffer Overflow vulnerability HERE!

 for (i=0;i<n;i++)  scanf("%x",&a[i]);
   printf("Done reading junk numbers\n");
}

int main() 
{
 victim_func();
 printf(“\n done”);
 return 0; 
}

使用 objdump 获取函数地址时,我得到了以下信息:

main(): 0x804854d
Address of main() where printf() is called: 0x8048563
victim_func(): 0x8048455
confused(): 0x8048414

现在,我想要通过在victim_func()中溢出缓冲区,并将返回地址覆盖为confused()的地址,从而跳转到函数'confused()'。 我想要从confused()正常返回到main中的printf()语句,并正常退出。因此,我提供以下输入:

Enter n: 7
Enter 7 HEX values:
1
2
3
4
5
8048414 (This is to jump to confused)
8048563 (this is to jump to printf() in main)

虽然该程序从printf语句中打印出“Done”,但它会跳回victim_func()并打印出“Enter n:”

我做错了什么?任何帮助都将不胜感激!

PS:我不确定我是否正确提出了问题。如果需要更多信息,请告诉我。


6
其实,这是一项学校作业! - Ashwin
如果这确实是作业,请标记为作业。 - NPE
9
@VJo,我认为这是一个很好的教育任务 - 我很想自己尝试一下:它需要对汇编代码实现的深刻理解和直接实验。白帽子也需要掌握这些想法。 - Elemental
LOL,哪个学校教如何滥用缓冲区溢出?虽然需要深入理解,但是要理解的是二进制代码。上面的尝试很苍白,不会奏效。 - BЈовић
3
@VJo:实际上,我见过的所有电脑安全课程都包含这样的内容。了解这些攻击方式是很重要的,即使仅仅是为了理解为什么在编程时应该小心。 - carlpett
4个回答

10
缓冲区溢出攻击比这要复杂得多。首先,您需要了解汇编语言才能执行此操作。在反汇编程序和要定位的函数后,您需要确定在执行该函数时的堆栈布局。这是一个使用Visual Studio的缓冲区溢出攻击示例,但原理相同。
#include "stdafx.h"
#include <math.h>

volatile double  test;

double function3()
{
    test++;
    return exp(test);
}

double  function2()
{
    return log(test);
}

double  function1()
{
    int a[5] = {0};           
    a[7] = (int)&function3;
    return exp(function2());

}
int _tmain(int argc, _TCHAR* argv[])
{
    double a = function1();
    test = a;
    return a;
}

感谢反汇编,我们知道在函数1中,a是在函数保存堆栈帧指针之前分配的。在那个值之后的值是返回地址,如果函数1完成,它应该去哪里。
00401090 55               push        ebp    <- we save the stack pointer
00401091 8B EC            mov         ebp,esp 
00401093 83 EC 1C         sub         esp,1Ch <- save space to allocate a[5]
00401096 B8 CC CC CC CC   mov         eax,0CCCCCCCCh 
0040109B 89 45 E4         mov         dword ptr [ebp-1Ch],eax  <- crt debug init a[5]  
0040109E 89 45 E8         mov         dword ptr [ebp-18h],eax 
004010A1 89 45 EC         mov         dword ptr [ebp-14h],eax 
004010A4 89 45 F0         mov         dword ptr [ebp-10h],eax 
004010A7 89 45 F4         mov         dword ptr [ebp-0Ch],eax 
004010AA 89 45 F8         mov         dword ptr [ebp-8],eax 
004010AD 89 45 FC         mov         dword ptr [ebp-4],eax 

从这里我们可以得出结论,如果我们用不同的地址覆盖a [7],函数将返回不是到主函数,而是到我们在a [7]中写入的任何地址。

希望这可以帮助你。


非常感谢您的耐心解释。这就是我卡住的地方!为什么我们需要分配1Ch个字来存储数组的5个单词?您能否在此处详细说明堆栈结构? - Ashwin
这是我的主程序的一部分反汇编代码。 0x0804855b <main+14>: sub $0x4,%esp 0x0804855e <main+17>: call 0x8048455 <victim_func> 0x08048563 <main+22>: movl $0x804872e,(%esp) 0x0804856a <main+29>: call 0x8048364 <printf@plt>所以,我从confused函数返回到了0x8048563的main()。在把返回地址放在正确的内存位置上没有问题。然后,main()恢复并打印“Done”。但此时,它返回到victim_func()并打印“Enter n:”。这就是我不理解的地方。 - Ashwin

3
现在,我想通过溢出那里的缓冲区并将返回地址覆盖为confused()的地址,从victim_func()跳转到函数'confused()'...
在现代Linux平台上,您还需要确保关闭两个安全功能以进行测试。首先是NX堆栈,其次是堆栈保护程序。
要关闭NX-Stacks,请使用-Wl,z,execstack(而不是-Wl,z,noexecstack)。要关闭堆栈保护程序,请使用-fno-stack-protector(而不是-fstack-protector或-fstack-protector-all)。
可能需要关闭第三个保护。该保护是FORTIFY_SOURCE。FORTIFY_SOURCE使用高风险函数的“更安全”变体,例如memcpy和strcpy。编译器在可以推断目标缓冲区大小时使用更安全的变体。如果复制超过目标缓冲区大小,则程序调用abort()。要禁用FORTIFY_SOURCE,请使用-U_FORTIFY_SOURCE或-D_FORTIFY_SOURCE=0编译程序。
这些安全功能默认打开,因为过去有很多问题。总的来说,这是一件好事,因为它可以防止许多问题(例如您正在尝试的问题)。

1

首先,我觉得你不应该在样本输入中输入数字5。你的数组声明为a[4],因此元素的索引为0-3,所以你的攻击输入似乎是错误的。

我还觉得你的程序假设了关于架构的几件事情:

  • sizof(int)==sizeof(memory address)
  • 环境栈实现的增长方向和机制

如果这些假设中有一个是不正确的,它永远不会起作用。

这似乎是一个非常艰难的工作任务。

有比更改代码控制流更容易的缓冲区溢出攻击示例。例如,您可能能够覆盖另一个数据片段,该数据片段应该受到用户保护(例如安全设置)。


2
数组越界索引正是此类攻击的工作原理。而这种攻击总是非常依赖于平台;它们取决于堆栈的布局方式,并且通常涉及一些汇编语言。通常的解决方案是将执行恶意操作的机器码放入缓冲区,然后用缓冲区的地址替换堆栈上的返回地址。 - James Kanze

0
你没有向我们展示带有a[i]地址的程序输出。我怀疑编译器正在执行类似于将栈上的数据对齐到16的操作。这可能会使返回地址比您预期的要远得多。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接