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_INFO
、fdintPARTIAL_FILE
、fdintCOPY_FILE
、fdintCLOSE_FILE_INFO
、fdintNEXT_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文件。
发表评论
您还未登录,请先登录。
登录