CVE-2020-1301 Windows SMB Authenticated远程代码执行漏洞分析说明
一、漏洞信息
1. 漏洞简述
- 漏洞名称:Windows SMB Authenticated Remote Code Execution Vulnerability
- 漏洞编号:CVE-2020-1301
- 漏洞类型:Integer Overflow
2. 组件概述
Microsoft Windows的所有版本均附带服务器消息块(SMB)协议的实现。 SMB是本机Windows网络框架,支持文件共享,网络打印,远程过程调用和其他功能。在Windows系统上,SMB协议通过附加的安全性,文件和磁盘管理支持扩展了CIFS协议。 通过各种SMB命令和子命令类型提供这些功能。
3. 漏洞利用
该漏洞利用需要攻击者拥有一个经过认证的账户,通过向目标server发送特制的SMBv1消息来触发漏洞。成功利用该漏洞的攻击者可以实现任意代码执行。
4. 漏洞影响版本
• Microsoft Windows 7
• Microsoft Windows 8.1
• Microsoft Windows 10
• Microsoft Windows RT 8.1
• Microsoft Windows Server 2008
• Microsoft Windows Server 2008 R2
• Microsoft Windows Server 2012
• Microsoft Windows Server 2012 (Server Core installation)
• Microsoft Windows Server 2012 R2
• Microsoft Windows Server 2012 R2 (Server Core installation)
• Microsoft Windows Server 2016
• Microsoft Windows Server 2016 (Server Core installation)
• Microsoft Windows Server 2019
• Microsoft Windows Server 2019 (Server Core installation)
• Microsoft Windows Server version 1803 (Server Core Installation)
• Microsoft Windows Server version 1903 (Server Core installation)
• Microsoft Windows Server version 1909 (Server Core installation)
• Microsoft Windows Server version 2004 (Server Core installation)
5. 解决方案
微软官方已针对该漏洞发布了安全更新补丁,补丁地址如下: https://portal.msrc.microsoft.com/en-us/security-guidance/advisory/CVE-2020-1301
二、漏洞复现
1. 环境搭建
靶机:win7_sp1_x86
攻击机:win10
靶机特定文件开启SMB共享,并创建对应的访问用户,用户权限任意。
2. 复现过程
在攻击机中运行poc:
python2 poc.py -t ip -u name -p passwd
靶机蓝屏:
三、漏洞分析
1. 漏洞基本信息
- 漏洞文件:
srv.sys
- 漏洞函数:
SrvSmbNtolctl()
- 漏洞参数:SMBv1协议
FSCTL_SIS_COPYFILE
请求中的SourceFileNameLength
和DestinationiFileNameLength
字段
- 漏洞对象:一个动态分配的buffer
2. 背景知识
SMB_COM_NT_TRACSACT
子命令扩展了SMB_COM_TRANSACTION2
命令的文件系统功能访问,允许传输非常大的参数和数据块。SMB_COM_NT_TRANSACT
消息可以超过单个SMB消息的最大大小(由MaxBufferSize
会话参数的值确定)。 在这种情况下,client将会使用一个或多个SMB_COM_NT_TRANSACT_SECONDARY
消息来传输Data
和Parameters
相关字节数据。
SMB_COM_NT_TRANSACT
命令请求消息与其他两种SMB的transaction请求message存在一定不同。 尽管有几个公共字段,但SMB_COM_NT_TRANSACT
消息会重新排列字段以提供更好的字节对齐。 其他transaction类型使用16位大小的字段来提供参数和数据的大小和偏移量, SMB_COM_NT_TRANSACT
使用32位大小的字段,主要目的就是为了更大的数据传输。 此外SMB_COM_NT_TRANSACT
还包含一个Function
字段,其中包含子命令代码。SMB_COM_NT_TRANSACT
结构如下:
// SMB_Parameters节:包含用于管理transaction本身的信息,还包含标志和设置信息,这些标志和设置信息为在服务器端执行操作提供了上下文。
SMB_Parameters
{
UCHAR WordCount;//指定参数块中SMB参数words的总数,该字段值必须大于或等于0x13
Words
{
UCHAR MaxSetupCount;//client在transaction reply中允许的最大setup字节数
USHORT Reserved1;//2字节的paddling数据,必须设置为0x0000,用于字节对齐
ULONG TotalParameterCount;//请求中传输的transaction parameters的byte数
ULONG TotalDataCount;//请求中传输的transaction data的byte数
ULONG MaxParameterCount;//client在transaction reply中允许的最大parameters字节数
ULONG MaxDataCount;//client在transaction reply中允许的最大data字节数
ULONG ParameterCount;//在当前SMB message中传输的transaction parameter字节数。如果transaction是单个的SMB_COM_NT_TRANSACT请求,该字段的值必须等于TotalParameterCount字段的值。如果该值比TotalParameterCount字段的值小,那么至少要存在一个SMB_COM_NT_TRANSACT_SECONDARY消息。
ULONG ParameterOffset;//transaction parameters的偏移量,从SMB_Header开始计算
ULONG DataCount;//在当前SMB message中传输的transaction data字节数
ULONG DataOffset;//transaction data的偏移量,从SMB_Header开始计算
UCHAR SetupCount;//transaction请求中包含的setup word数
USHORT Function;//transaction中的子命令,指明了server要进行的操作
USHORT Setup[SetupCount];//2字节word的数组,指明了transaction的上下文
}
}
//SMB_Data部分包含的parameter和data是服务器上transaction操作的输入
SMB_Data
{
USHORT ByteCount;// SMB_Data.Bytes 数组中的byte数
Bytes
{
UCHAR Pad1[];//填充字节数组
UCHAR NT_Trans_Parameters[ParameterCount];//transaction parameter字节
UCHAR Pad2[];//填充字节数组
UCHAR NT_Trans_Data[DataCount];//transaction data字节
}
}
3. 详细分析
1. 漏洞触发相关结构
本漏洞的触发主要与Function
字段相关,使用的子命令为NT_TRANSACT_IOCTL
。该子命令允许将文件系统控制和设备控制功能从客户端透明地传输到服务器,并用于向服务器发送特定于平台或特定于实现的信息,其结构如下:
NT_Trans_Parameters
{
}
NT_Trans_Data
{
UCHAR Data[TotalDataCount];
}
前面SMB_Parameters
部分:
字段 |
大小(byte) |
说明 |
WordCount |
1 |
UCHAR,值必须为0x17 |
Words |
46 |
USHORT的一个数组 |
TotalParameterCoun |
2 |
USHORT,值为0 |
MaxParameterCount |
2 |
USHORT,值为0 |
ParameterCount |
2 |
USHORT,值为0 |
SetupCount |
1 |
UCHAR,值必须为0x04 |
Function |
2 |
USHORT,值必须为0x0002(NT_TRANSACT_IOCTL) |
Setup
字段的格式如下:
字段 |
大小 (byte) |
说明 |
FunctionCode |
4 |
文件系统控件或设备控件(FSCTL / IOCTL)方法的控制代码 |
FID |
2 |
必须包含从先前成功的SMB打开命令获得的有效FID,提供的FID的类型由IsFsctl指定 |
IsFsctl |
1 |
如果命令是文件系统控制命令,而FID是文件系统控制设备,则此字段为TRUE。 |
IsFlags |
1 |
如果设置了位0,则该命令将应用于共享根句柄 |
与本漏洞相关的是FSCTL_SIS_COPYFILE
,code为0x90100。该请求消息会请求server使用single instance storage(SIS) fileter来copy文件,该消息包含一个SI_COPYFILE
data元素。 如果服务器上安装了SIS fileter,将通过创建SIS link的方式来替代实际复制文件数据的方式,将指定的源文件复制到指定的目标文件。 FSCTL_SIS_COPYFILE
请求结构如下:
字段 |
大小(byte) |
说明 |
SourceFileNameLength |
4 |
32位大小的unsigned integer,指明SouceFileName 的大小(包含结尾处的空字符)e |
DestinationFileNameLength |
4 |
32位大小的unsigned integer,指明DestinationFileName 的大小(包含结尾处的空字符) |
Flags |
4 |
32位大小的unsigned integer,包含0或多个flag值 |
SourceFileName |
variable |
以空值结尾的Unicode字符串,包含源文件名 |
DestinationFileName |
variable |
以空值结尾的Unicode字符串,包含目标文件名 |
2. 漏洞产生原因
Windows SMB server的内核驱动在处理NT_TRANSACT_IOCTL
子命令中的FSCTL_SIS_COPYFILE
命令请求时,由于验证不充分产生了一个整数溢出漏洞。
当server在处理NT_TRANSACT_IOCTL
命令请求时,会分配一个内核内存池用作缓冲区,来存储处理文件系统请求时产生的中间数据。其存储方式如下:
[absoluteSourceFileNameLength]
[absoluteDestinationFileNameLength]
[Flags]
[absoluteSourceFileName]
[absoluteDestinationFileName]
absoluteSourceFileName
和absoluteDestinationFileName
字符串与SMB连接的共享路径(取决于SMB_COM_TREE_CONNECT_ANDX
请求中的Path
字段)以及FSCTL_SIS_COPYFILE
请求中的各自SourceFileName
和DestinationFileName
字段串联在一起。在复制这些绝对文件名时就会产生漏洞。
为了将文件名创建到前面说到的内核内存池中,首先将共享路径复制到内存池中,然后检查共享文件名是否以'\'结尾。如果是,进一步检查SourceFileName
或DestinationFileName
的第一个字符是否位'\'或'\0';如果是,就使用memcpy
函数从SourceFileName
或DestinationFileName
的第二个字符开始复制,复制的长度为SourceFileNameLength - 2
或DestinationFileNameLength - 2
。漏洞触发主要由于没有检查这两个长度是否大于1。如果SourceFileNameLength
或者DestinationFileNameLength
的值为1,那么就会发生整数溢出,复制长度变为了0xffffffff,从而会溢出前面分配的内存池缓冲区。
3. 静态分析
此处使用的srv.sys
软件版本为:6.1.7601.17514。
首先将SI_COPYFILE
结构各字段值设置如下:
Field |
Value |
SourceFileNameLength |
0x0000000a |
DestinationFileNameLength |
0x00000001 |
Flags |
0x00000002 |
SourceFileName |
000000000000000000 |
DestinationFileName |
00 |
查看SrvSmbNtIoctl()
函数如下:
相关的关键代码在图中已做了注释,容易看出,整数溢出的位置有两处,都可以产生0xffffffff的值。该值随后传入到后续的内存分配代码:
在使用memcpy()
进行内存分配时,会尝试将0xffffffff字节从目标文件名(或者源文件名)复制到先前分配的SMB1缓冲区,因此会发生溢出崩溃。
4. 动态分析
栈回溯结果:
栈回溯结果显示关键函数在srv!SrvSmbNtIoctl+0x7a4()
,而该位置恰好为memcpy()
函数执行完返回的地址:
4. 源码分析
此处没有找到对应版本的源码,使用一个近似版本的源码:
copyFile = (PSI_COPYFILE)transaction->InData;
---------
source = copyFile->FileNameBuffer;
sourceLength = copyFile->SourceFileNameLength;
dest = source + (sourceLength / sizeof(WCHAR));
destLength = copyFile->DestinationFileNameLength;
----
if ( (sourceLength > bufferLength || sourceLength == 0 ) ||
(destLength > bufferLength || destLength == 0 ) ||
((FIELD_OFFSET(SI_COPYFILE,FileNameBuffer) + sourceLength + destLength) > bufferLength) ||
(*(source + (sourceLength/sizeof(WCHAR)-1)) != 0) ||
(*(dest + (destLength/sizeof(WCHAR)-1)) != 0) ) { //这里检查这两个字段是否为偶数
SrvSetSmbError( WorkContext, STATUS_INVALID_PARAMETER );
return SmbTransStatusErrorWithoutData;
}
----
addSlashToSource = 0;
addSlashToDest = 0;
if ( IS_UNICODE_PATH_SEPARATOR(*(prefix + (prefixLength/sizeof(WCHAR)-1))) ) {
if ( IS_UNICODE_PATH_SEPARATOR(*source) ) {
source++;
sourceLength -= sizeof(WCHAR); // sourceLength = 1 - 2 = 0xFFFFFFFF, 发生溢出
}
if ( IS_UNICODE_PATH_SEPARATOR(*dest) ) {
dest++;
destLength -= sizeof(WCHAR); // destLength = 1 – 2 = 0xFFFFFFFF, 发生溢出
}
} else {
if ( !IS_UNICODE_PATH_SEPARATOR(*source) ) {
----
RtlCopyMemory( p, source, sourceLength );
p += sourceLength;
RtlCopyMemory( p, prefix, prefixLength );
p += prefixLength;
if ( addSlashToDest != 0 ) {
*(PWCHAR)p = UNICODE_DIR_SEPARATOR_CHAR;
p += sizeof(WCHAR);
}
RtlCopyMemory( p, dest, destLength );
5. 攻击流量
执行poc,抓取到的流量如下所示:
6. PoC分析
该漏洞的poc代码已在互联网中公开,可自行网上进行搜索。此处给出关键字段的数值构造:
IoctlCode = 0x90100
setup = smb.pack('<L', IoctlCode)
setup += smb.pack('<H', fid)
setup += 'a' * 2
name = ''
param = ''
size = 10
data = smb.pack('<L', size) # SourceFileNameLength
#data = smb.pack('<L',1)
#data += smb.pack('<L', size)
data += smb.pack('<L', 1) # DestinationFileNameLength
data += smb.pack('<L', 0x00000002) # Flags
data += '\x00' * (size-1) # SourceFileName (variable)
data += '\x00' # DestinationFileName (variable)
data += '\x00\x00'
data += '\x41' * 16
data += '\x42' * 16
data += '\x43' * 16
data += '\x44' * 16
data += 'Exploit me! ;-)'
上面触发漏洞的关键数据的构造中使用的是SourceFileNameLength
为0x0000000a,DestinationFileNameLength
为1的情况。如前面静态分析所知,也可以使用SourceFileNameLength
为1,DestinationFileNameLength
为0x0000000a的组合来触发漏洞。因为关键点是执行-2
操作来触发整数溢出。
四、漏洞检测和防御
1. 漏洞检测
这种漏洞的检测一般很难做到无损检测,目前还没有很好的检测思路,大家可以互相交流一下。
2. 漏洞防御
1. 防御思路
- 主机端:最安全的方式就是打补丁,可以写热补丁,这个跟CVE-2020-0796的补丁方式个人感觉应该是差不多的。暂时没有做补丁分析,所以可能后续进行热补丁开发补充。
- IPS:直接检测两个字段是不是有一个=1即可,目前来看只有=1时可以稳定触发漏洞。
2. 漏洞利用评估
造成crash的实现很简单,但是想实现稳定rce还是有很大难度,目前来看主要因素是分配的缓冲区位于内核,很难精准控制和适配。所以该漏洞总体评估威胁中等。
五、参考链接
[1]. https://airbus-cyber-security.com/diving-into-the-smblost-vulnerability-cve-2020-1301
[2]. 微软SMB协议官方文档
六、注意
论坛内部共享,未经允许,请勿转载,谢谢