CVE-2020-1300:Windows CAB文件RCE漏洞分析

阅读量    109642 |

分享到: QQ空间 新浪微博 微信 QQ facebook twitter

 

0x00 前言

微软Windows中存在一个目录遍历漏洞,漏洞根源在于未正确过滤CAB文件中文件路径,目前在官方支持周期内的未打补丁的所有Windows版本都受该漏洞影响。

远程攻击者可以诱使用户打开精心构造的文件或者安装远程打印机来利用该漏洞。成功利用该漏洞后,攻击者可以在SYSTEM安全上下文中执行任意代码。

 

0x01 漏洞分析

Cabinet(CAB)是微软发明的一种归档文件格式,用于支持无损数据压缩以及嵌入式数字证书。该格式被广泛运用在Windows平台上,支持各种应用程序。

CAB文件中包含一个cabinet头(CFHEADER),随后为一个或多个cabinet目录(CFFOLDER)、一个或多个cabinet文件(CFFILE)以及压缩文件数据(CFDATA)。CFDATA中包含的压缩文件数据可以使用几种压缩格式来存储,具体可参考对应的CFFOLDER结构(参考链接)。CFHEADER结构的具体格式如下:

图1. CFHEADER结构

CFFOLDER条目紧跟在CFHEADER结构后面,每个CFFOLDER条目都包含该cabinet文件中存储的某个目录或者部分目录的信息。每个目录中可以包含多个文件,每个文件都存在对应的一个CFFILE条目。在CAB文件中,CFFILE条目位于CFFOLDER条目后面,可以通过CFHEADER结构中的coffFiles字段来定位。CFFILE条目的数量由CFHEADER结构中的cFiles字段来指定。CFFILE条目的具体格式如下所示:

图2. CFFILE条目格式

其中szName字段是以NULL结尾的字符串,用来指定文件名。CFDATA条目位于CFFILE条目之后,包含文件内容。

微软提供了Cabinet API,可以在Windows平台上处理Cabinet文件,许多微软应用会使用该API。为了从CAB文件中提取出所有文件,应用程序通常会使用FDICopy函数,指定回调函数来处理提取过程中的所有事件。比如,当打印机相关的应用程序在处理CAB文件时,动态链接库localspl.dll以及PrintBrmEngine.exe可执行文件中会使用回调函数NCabbingLibrary::FdiCabNotify()。该函数会在提取过程中处理多种类型的通知,比如fdintCABINET_INFOfdintPARTIAL_FILEfdintCOPY_FILEfdintCLOSE_FILE_INFOfdintNEXT_CABINET以及fdintENUMERATE。在本文中,我们重点关注的是fdintCOPY_FILE类型,当系统开始处理cabinet文件中的每个文件时就会调用该通知类型,以便应用程序拷贝或者跳过该文件。

在处理CAB文件时,有多个微软应用中都存在目录遍历漏洞,包括Print Spooler应用以及Print Management Console应用(printmanagement.msc)。这些应用共享NCabbingLibrary::FdiCabNotify()函数的相同代码,以便提取CAB文件中的所有文件。每当Cabinet API处理CAB文件中的CFFILE以及对应的CFDATA条目时,就会向回调函数NCabbingLibrary::FdiCabNotify()发送fdintCOPY_FILE通知,附带从这些条目中提取的所有信息。这个漏洞的根源在于未对CFFILE条目中的szName字段进行输入验证。当受影响的函数处理fdintCOPY_FILE函数时,会将szName字段作为文件名来传递。随后该函数会将文件名与临时文件夹的路径拼接起来,生成绝对路径。临时文件夹的路径如下所示:

%userprofiles%\AppData\Local\[Some UUID]\

受影响的函数会根据..\的形式来检查szName字段中是否包含目录遍历行为,然而却没有考虑到../的替代场景,后者同样适用于Windows平台。如果CAB文件CFFILE条目中的szName字段格式如下所示,那么提取出的文件将会被写入正确路径之外:

../../../../../../some_file_name

此外,由于Print Spooler应用运行在SYSTEM安全上下文中,因此攻击者可以在目标的任意位置上写入任意文件。

为了利用该漏洞,远程攻击者可以诱导受害者打开精心构造的文件或者安装远程打印机。成功利用漏洞后,攻击者可以在SYSTEM安全上下文中执行任意代码。

 

0x02 源代码分析

我们从PrintBrmEngine.exe v10.0.18362.894中反编译出如下代码:

INT_PTR __cdecl NCabbingLibrary::FdiCabNotify(FDINOTIFICATIONTYPE fdint, PFDINOTIFICATION pfdin)
{
    int v2; // edi
    int *v3; // ebx
    int v4; // esi
    int v5; // eax
    NCabbingLibrary *v7; // [esp+0h] [ebp-18h]
    unsigned __int16 v8; // [esp+0h] [ebp-18h]
    const unsigned __int16 *v9; // [esp+0h] [ebp-18h]
    const char *v10; // [esp+4h] [ebp-14h]
    unsigned __int16 v11; // [esp+4h] [ebp-14h]
    const unsigned __int16 *v12; // [esp+4h] [ebp-14h]
    unsigned __int16 **v13; // [esp+8h] [ebp-10h]
    unsigned __int16 v14; // [esp+8h] [ebp-10h]
    int *v15; // [esp+8h] [ebp-10h]
    int v16; // [esp+14h] [ebp-4h]

    v2 = 0;
    v3 = (int *)pfdin->pv;
    if ( fdint )
    {
    switch ( fdint )
        {
        case 1:
            if ( WPP_GLOBAL_Control != &WPP_GLOBAL_Control && *((_BYTE *)WPP_GLOBAL_Control + 28) & 1 )
                WPP_SF_(0x17u, &WPP_3bc88d3e245431a1040d03fff65d5493_Traceguids, *((_QWORD *)WPP_GLOBAL_Control + 2));
            break;

        // Handling notification fdintCOPY_FILE
        case 2:
            if ( WPP_GLOBAL_Control != &WPP_GLOBAL_Control && *((_BYTE *)WPP_GLOBAL_Control + 28) & 4 )
                WPP_SF_sD(*((_QWORD *)WPP_GLOBAL_Control + 2), (int)pfdin->psz1, pfdin->cb);
            v4 = NCabbingLibrary::AnsiToUnicodeWithAlloc(v7, v10, v13);
            if ( v4 >= 0 )
            {
                v5 = *v3;
                v16 = -1;
                // Directory Traversal happens here
                v4 = NCabbingLibrary::ProcessCopyFile(
                    *(const unsigned __int16 **)(v5 + 4),
                    0,
                    (NCabbingLibrary *)&v16,
                    v9,
                    v12,
                    v15);
                operator delete(0);
                v2 = v16; }
                v3[1] = v4;
                break;

        /* snip */

int __userpurge NCabbingLibrary::ProcessCopyFile@<eax>(...) {
    const unsigned __int16 *v6; // esi
    const unsigned __int16 *v7; // ebx
    int v8; // edi
    NCabbingLibrary *v9; // ecx
    unsigned __int16 v10; // ax
    int v11; // eax
    const unsigned __int16 *v13; // [esp+0h] [ebp-1Ch]
    NCabbingLibrary *v14; // [esp+0h] [ebp-1Ch]
    unsigned int v15; // [esp+4h] [ebp-18h]
    int v16; // [esp+4h] [ebp-18h]
    int v17; // [esp+8h] [ebp-14h]
    int v18; // [esp+10h] [ebp-Ch]
    wchar_t *v19; // [esp+14h] [ebp-8h]

    v6 = a1;
    v18 = 1735554163;
    v7 = a2;
    v19 = &NCoreLibrary::TString::gszNullState;
    v8 = NCoreLibrary::TString::Update((NCoreLibrary::TString *)&v18, a1);
    if ( v8 >= 0 )
    {
        v8 = NCoreLibrary::TString::Cat((NCoreLibrary::TString *)&v18, v7);
        if ( v8 >= 0 )
        {
            v9 = (NCabbingLibrary *)(v6 + 1);
            do
            {
                v10 = *v6;
                ++v6; }
                while ( v10 );
                // create the file here
                v8 = NCabbingLibrary::CreateFullPath(((char *)v6 - (char *)v9) >> 1, v19, v9, v13, v15, v17); if ( v8 >= 0 )
                {
                    v11 = __wopen(v19, 33538, 384);
                    *(_DWORD *)this = v11;
                    if ( v11 == -1 )
                    {

        /* snip */

int __userpurge NCabbingLibrary::CreateFullPath@<eax>(...)
{
    const unsigned __int16 *v6; // ebx
    int v7; // edi
    unsigned int v8; // esi
    WCHAR *v9; // esi
    int v10; // eax
    WCHAR *v11; // ebx
    const wchar_t *i; // esi
    wchar_t *v13; // esi
    int v14; // eax
    unsigned int v16; // [esp+0h] [ebp-1Ch]
    NCoreLibrary *v17; // [esp+0h] [ebp-1Ch]
    const unsigned __int16 *v18; // [esp+4h] [ebp-18h]
    unsigned int v19; // [esp+10h] [ebp-Ch]
    const unsigned __int16 *lpPathName; // [esp+18h] [ebp-4h]

    v6 = a2;
    v19 = a1;
    v7 = -2147024809;
    if ( a2 )
    {
        v8 = wcslen(a2);
        if ( a1 < v8 )
        {
            v9 = (WCHAR *)operator new(2 * (v8 + 1));
            v7 = v9 != 0 ? 0 : -2147024882;
            if ( v9 )
            {
                v10 = StringCchCopyW((size_t)v6, v16, v18);
                v11 = v9;
                v7 = v10;
                for ( i = &v9[v19]; i; i = v13 + 1 )
                {
                    if ( v7 < 0 )
                        break;
                    lpPathName = i;
                    // The code only checks '' not '/' v13 = _wcschr(i, '\');
                    if ( !v13 )
                        break;
                    *v13 = 0;
                    v14 = wcscmp(lpPathName, L"..");
                    if ( v14 )
                        v14 = v14 < 0 ? -1 : 1;
                    if ( v14 )
                    {
                        if ( !CreateDirectoryW(v11, 0) && GetLastError() != 183 )
                            v7 = NCoreLibrary::GetLastErrorAsHResult(v17);
                    }
                    else
                    {
                        v7 = -2147024891;
                        if ( WPP_GLOBAL_Control != &WPP_GLOBAL_Control && *((_BYTE *)WPP_GLOBAL_Control + 28) & 4 )
                            WPP_SF_S(
                                0xAu,
                                &WPP_d3250bbb49db3f5a80bda4d2e9ea5016_Traceguids,
                                *((_QWORD *)WPP_GLOBAL_Control + 2),
                                (int)lpPathName);
                        }
                        *v13 = 92;
                }
                operator delete(v11);
            }
        }
    }
    return v7;
}

为了触发该漏洞,攻击者需要将构造的CAB文件投递至目标主机,或者使用恶意CAB文件创建一个远程打印机。在CAB文件中,CFFILE记录中的szName字段必须包含多个../字符串。当受影响的某个应用程序解析恶意CAB文件时,就会触发该漏洞。

 

0x03 补丁情况

微软在6月的补丁日中修复了该漏洞。根据官方公告,微软“更正了Windows对cabinet文件的处理过程”,此外没有提供关于补丁的更多信息。尽管目前还未看到攻击者对该漏洞的利用情况,用户还是应当及时打补丁,否则不要轻易打开CAB文件。

分享到: QQ空间 新浪微博 微信 QQ facebook twitter
|推荐阅读
|发表评论
|评论列表
加载更多