一、溢出目标

         无论是在windows下还是在linux下,溢出攻击基本上都是以控制计算机的执行路径为目标,而x86下执行哪条指令是由eip寄存器来控制的,所以如果能够修改eip寄存器的值,就可以修改计算机的执行路径。 所以溢出的目标:控制eip寄存器。

二、关于call/ret

      但是如何修改eip寄存器呢?在汇编指令中有一个和eip寄存器紧密相关的指令ret,ret就可以理解为pop eip。而ret指令在正常函数调用的时候都会执行的。

      我们来看下linux中函数调用的例子:

#include <stdio.h>
int swap(int *a, int *b)
{        
       int t;        
       t = *a;        
       *a = *b;        
       *b = t;
}
int main()
{        
       int a;        
       int b;        
       a = 6;        
       b = 8;        
       //printf("%d %d/n",a,b);        
       swap(&a,&b);        
       //printf("%d %d/n",a,b);        
       return ;
}


      下面是objdump –d 得到的 汇编指令。

08048344 <swap>: 
8048344:       55                      push  %ebp 
8048345:       89 e5                   mov    %esp,%ebp 
8048347:       83 ec 10                sub    $0x10,%esp 
804834a:       8b 45 08                mov    0x8(%ebp),%eax 
804834d:       8b 00                   mov    (%eax),%eax 
804834f:       89 45 fc                mov    %eax,-0x4(%ebp) 
8048352:       8b 45 0c                mov    0xc(%ebp),%eax 
8048355:       8b 10                   mov    (%eax),%edx 
8048357:       8b 45 08                mov    0x8(%ebp),%eax 
804835a:       89 10                   mov    %edx,(%eax) 
804835c:       8b 55 0c                mov    0xc(%ebp),%edx 
804835f:       8b 45 fc                mov    -0x4(%ebp),%eax 
8048362:       89 02                   mov    %eax,(%edx) 
8048364:       c9                      leave 
8048365:       c3                      ret
08048366 <main>: 
8048366:       8d 4c 24 04             lea    0x4(%esp),%ecx 
804836a:       83 e4 f0                and    $0xfffffff0,%esp 
804836d:       ff 71 fc                pushl  -0x4(%ecx) 
8048370:       55                      push   %ebp 
8048371:       89 e5                   mov    %esp,%ebp 
8048373:       51                      push   %ecx 
8048374:       83 ec 18                sub    $0x18,%esp 
8048377:       c7 45 f8 06 00 00 00    movl   $0x6,-0x8(%ebp) 
804837e:       c7 45 f4 08 00 00 00    movl   $0x8,-0xc(%ebp) 
8048385:       8d 45 f4                lea    -0xc(%ebp),%eax 
8048388:       89 44 24 04             mov    %eax,0x4(%esp) 
804838c:       8d 45 f8                lea    -0x8(%ebp),%eax 804838f:       89 04 24                mov    %eax,(%esp) 
8048392:       e8 ad ff ff ff          call   8048344 <swap> 
8048397:       83 c4 18                add    $0x18,%esp 
804839a:       59                      pop    %ecx 
804839b:       5d                      pop    %ebp 
804839c:       8d 61 fc                lea    -0x4(%ecx),%esp 
804839f:       c3                      ret


 我们用图来解释一下在函数调用时都发生了什么。

绘图1

      下面是几个有用的结论:

      1、当被调用函数开始执行时堆栈由高地址到低地址依次是:被调用函数的参数(参数n,参数n-1……,参数0),eip的原始值,ebp的原始值,被调用函数自己的局部变量。这些东东不一定是连续存储的。

      2、被调用函数的局部变量的地址我们是可以知道的,保存eip原始值的位置就在局部变量的不远处。

      3、当被调用函数返回时,由ret指令恢复现场,把堆栈中的eip的原始值交还给eip。

      那么,我们只要找到堆栈中eip原始值的存储位置,把他改成shellcode的地址就OK了。

三、一个例子

      这个例子是当main函数(也是被别的函数调用的,只不过名字比较特殊)返回时,我们把eip改掉,让他来执行/bin/ls。这段代码在arch linux是可以执行的。

char shellcode[] =
    "\xeb\x1f\x5e\x89\x76\x09\x31\xc0\x88\x46\x08\x89\x46\x0d\xb0\x0b"
    "\x89\xf3\x8d\x4e\x09\x8d\x56\x0d\xcd\x80\x31\xdb\x89\xd8\x40\xcd"
    "\x80\xe8\xdc\xff\xff\xff\x2f\x62\x69\x6e\x2f\x6c\x73\x00\xc9\xc3";

int main ()
{
    int buf[1]; 
    buf[7] = (int) shellcode; 
    return 0; 
}


四、关于堆栈保护

         但是上面的代码在redhat下面运行就会报段错误。因为redhat使用了堆栈保护技术,就是数据段的数据尤其是堆栈段的数据是不能被执行。

         我们可以执行首先执行 sudo echo 0 | tee /proc/sys/kernel/exec-shield。

         再运行上面的那个例子,不出意外的话应该溢出成功了:在redhat下堆栈保护的开关是由/proc/sys/kernel/exec-shield这个文件控制的。

         在实际攻击时,还有其他巧妙的方法绕过Linux不可执行堆栈保护。

GitHub 加速计划 / li / linux-dash
10.39 K
1.2 K
下载
A beautiful web dashboard for Linux
最近提交(Master分支:2 个月前 )
186a802e added ecosystem file for PM2 4 年前
5def40a3 Add host customization support for the NodeJS version 4 年前
Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐