SOAPwn

.NET SOAP Client Proxy Abuse

Executive Summary

The SOAPwn vulnerability class impacts the .NET Framework SOAP client stack, specifically how SOAP proxies generated from WSDL files handle transport protocols. By design, .NET’s HttpWebClientProtocol is expected to operate over HTTP(S). However, 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 (for example, using ServiceDescriptionImporter) and subsequently invoke generated SOAP proxy methods, attackers can weaponize this behavior to achieve arbitrary file writes, NTLM credential relaying, and in many real-world cases remote code execution. The issue is systemic: it affects the .NET Framework itself rather than a single product, and Microsoft has chosen not to patch it, placing responsibility on application developers to implement their own mitigations.


Technical Deep Dive – Root Cause and Exploitation

Root Cause: Unsafe WebRequest Handling in .NET SOAP Clients

The fundamental issue resides in the .NET Framework’s HttpWebClientProtocol.GetWebRequest() implementation:

protected override WebRequest GetWebRequest(Uri uri)
{
    WebRequest webRequest = base.GetWebRequest(uri); // [1]
    HttpWebRequest httpWebRequest = webRequest as HttpWebRequest; // [2]
    if (httpWebRequest != null)
    {
        httpWebRequest.UserAgent = this.UserAgent;
        httpWebRequest.AllowAutoRedirect = this.allowAutoRedirect;
        httpWebRequest.AutomaticDecompression = (this.enableDecompression ? DecompressionMethods.GZip : DecompressionMethods.None);
        httpWebRequest.AllowWriteStreamBuffering = true;
        httpWebRequest.SendChunked = false;
        if (this.unsafeAuthenticatedConnectionSharing != httpWebRequest.UnsafeAuthenticatedConnectionSharing)
        {
            httpWebRequest.UnsafeAuthenticatedConnectionSharing = this.unsafeAuthenticatedConnectionSharing;
        }
        if (this.proxy != null)
        {
            httpWebRequest.Proxy = this.proxy;
        }
        if (this.clientCertificates != null && this.clientCertificates.Count > 0)
        {
            httpWebRequest.ClientCertificates.AddRange(this.clientCertificates);
        }
        httpWebRequest.CookieContainer = this.cookieJar;
    }
    return webRequest; // [3]
}

Key properties of this logic:

  • [1] base.GetWebRequest(uri) returns a protocol-specific WebRequest

  • For file:// URIs, this is a FileWebRequest

  • [2] The invalid cast to HttpWebRequest fails silently

  • [3] The original WebRequest is returned without scheme validation

As a result, SOAP client code intended for HTTP(S) transparently accepts non-HTTP transports.


PoC || GTFO

Umbraco 8.18.15 (Authenticated) - WIP

Requirements

  • Low-privileged user with Editor role

  • Access to Forms → Data Sources

Attack Chain

  1. Import attacker-controlled WSDL via Forms Data Source

  2. Generated proxy embeds a file:// SOAP endpoint

  3. Form submission invokes proxy method

  4. SOAP request is written to disk via FileWebRequest

Notes

  • SOAP 1.1 only

  • Proxy import confirmed

  • Execution via Forms is partially restricted (WIP)

Can import:

Should be able to import following WSDL file..

.. to generate following proxy method:


PowerShell – Arbitrary File Write → RCE

PowerShell uses the .NET SOAP client stack when importing web services.

Attack Chain

  1. Import malicious SOAP proxy

  2. Trigger method invocation

  3. SOAP payload written to profile.ps1

  4. New PowerShell session loads profile

  5. Arbitrary code execution

PowerShell – NTLM Relay

Attack Chain

  1. SOAP endpoint uses UNC path (file://///attacker/share)

  2. Proxy invocation triggers SMB authentication

  3. NTLM challenge/response captured

  4. Credentials relayed to another host

PowerShell WSDL file used:

Last updated