Hacktricks-skills soap-threadlocal-auth-bypass

How to identify and exploit SOAP/JAX-WS ThreadLocal authentication bypass vulnerabilities in Java web services. Use this skill whenever the user mentions SOAP endpoints, JAX-WS handlers, authentication bypass, ThreadLocal, WebLogic, JBoss, GlassFish, or any SOAP-based Java web service security testing. Also trigger when users are investigating SOAP header-based authentication, middleware authentication caching, or Java EE security handler chains. This skill covers reconnaissance, exploitation, and validation of ThreadLocal-based authentication bypass attacks.

install
source · Clone the upstream repo
git clone https://github.com/abelrguezr/hacktricks-skills
manifest: skills/pentesting-web/soap-jax-ws-threadlocal-auth-bypass/SKILL.MD
source content

SOAP/JAX-WS ThreadLocal Authentication Bypass

This skill helps you identify and exploit authentication bypass vulnerabilities in SOAP-based Java web services where handlers cache authenticated subjects in

ThreadLocal
variables without clearing them when authentication headers are missing.

Understanding the Vulnerability

The Root Cause

Some Java middleware handlers store the authenticated

Subject
/
Principal
in a static
ThreadLocal
and only refresh it when a proprietary SOAP header arrives. Because application servers like WebLogic, JBoss, and GlassFish recycle worker threads, dropping that header causes the last privileged
Subject
processed by the thread to be silently reused.

Why this happens:

public boolean handleMessage(SOAPMessageContext ctx) {
    if (!outbound) {
        SOAPHeader hdr = ctx.getMessage().getSOAPPart().getEnvelope().getHeader();
        SOAPHeaderElement e = findHeader(hdr, subjectName);
        if (e != null) {
            SubjectHolder.setSubject(unmarshal(e));
        }
        // BUG: Subject is never cleared when header is missing!
    }
    return true;
}

The handler only sets the

Subject
when the custom header is present, but never clears it when absent. This means the previous request's context survives on reused threads.

The Attack Pattern

  1. Trigger authenticated context - Send requests with valid authentication headers to populate the
    ThreadLocal
    cache on worker threads
  2. Flood with header-less requests - Send well-formed SOAP bodies without the authentication header
  3. Catch the reuse - When a header-less request lands on a thread that previously executed a privileged action, it inherits that
    Subject
  4. Execute privileged operations - Access protected endpoints like user managers, credential managers, or admin functions

Reconnaissance

Step 1: Map SOAP Endpoints

Start by enumerating the target to locate SOAP endpoints:

  • Check reverse proxy and routing rules for hidden SOAP trees that may block
    ?wsdl
    but accept POSTs
  • Look for common SOAP paths:
    /services/*
    ,
    /jaxws/*
    ,
    /soap/*
    ,
    /webservice/*
  • Map these alongside your overall web methodology

Step 2: Analyze Application Artifacts

If you have access to EAR/WAR/EJB artifacts:

# Unpack and inspect
unzip *.ear
# Look for:
# - application.xml (service definitions)
# - web.xml (handler chain configurations)
# - @WebService annotations in Java sources
# - LoginHandlerChain.xml or similar handler configs

Key things to find:

  • Handler class names (e.g.,
    LoginHandler
    ,
    AuthHandler
    )
  • SOAP header QNames (e.g.,
    mySubjectHeader
    ,
    authToken
    )
  • Backing EJB names (e.g.,
    UserManager
    ,
    CredentialManager
    )

Step 3: Recover WSDL

If metadata is missing:

  • Brute-force likely
    ServiceName?wsdl
    paths
  • Temporarily relax lab proxies to capture WSDL
  • Import recovered WSDL into tools like Burp Suite Wsdler to generate baseline envelopes

Step 4: Review Handler Sources

Look for

ThreadLocal
patterns in handler code:

  • Search for
    SubjectHolder.setSubject()
    or similar patterns
  • Check if the
    ThreadLocal
    is ever cleared when authentication headers are missing or malformed
  • Look for static
    ThreadLocal
    declarations that persist across requests

Exploitation

Phase 1: Establish Baseline

Send a valid request with the proprietary authentication header to learn:

  • Normal response codes
  • Error messages for invalid tokens
  • Expected SOAP envelope structure
  • Required namespaces and formatting

Phase 2: Craft Header-Less Requests

Resend the same SOAP body while omitting the authentication header:

  • Keep the XML well-formed
  • Respect all required namespaces
  • Ensure the handler exits cleanly (don't trigger parsing errors)

Example request:

POST /ac-iasp-backend-jaxws/UserManager HTTP/1.1
Host: target
Content-Type: text/xml;charset=UTF-8

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
                  xmlns:jax="http://jaxws.user.frontend.iasp.service.actividentity.com">
  <soapenv:Header/>
  <soapenv:Body>
    <jax:findUserIds>
      <arg0></arg0>
      <arg1>spl*</arg1>
    </jax:findUserIds>
  </soapenv:Body>
</soapenv:Envelope>

Phase 3: Flood for Thread Reuse

Loop the header-less request with high parallelism:

  • Use multiple concurrent connections
  • Reuse Keep-Alive connections to maximize thread reuse probability
  • Target large worker pools
  • Monitor responses for privileged data or success indicators

Why parallelism matters: The more concurrent requests you send, the higher the probability that some will land on threads that previously executed privileged actions.

Phase 4: Execute Privileged Operations

Once you observe privileged responses, escalate:

  • Access user management endpoints
  • Create administrator accounts
  • Import credentials
  • Access credential managers
  • Perform any operation that requires elevated privileges

Real-World Case Study: HID ActivID/IASP (HID-PSA-2025-002)

Vulnerability Details

Synacktiv discovered that JAX-WS

LoginHandler
in ActivID 8.6–8.7 sets
SubjectHolder.subject
when a
mySubjectHeader
SOAP header is present or when console/SSP traffic authenticates, but never clears it when the header is absent.

Exploitation Pattern

  1. Trigger authenticated context on many threads:

    • Spam
      /ssp
      endpoint
    • Log into
      /aiconsole
      as admin in another browser tab
    • Generate authenticated traffic to populate the thread pool
  2. Flood header-less SOAP bodies:

    • Target
      /ac-iasp-backend-jaxws/UserManager
      or other EJB-backed JAX-WS endpoints
    • Use high parallelism
    • Each hit that reuses an "infected" thread executes with elevated
      Subject
  3. Repeat until privileged responses:

    • Reuse Keep-Alive connections
    • Maximize thread reuse probability
    • Monitor for admin-level responses

Handler Flow

  • LoginHandlerChain.xml
    LoginHandler.handleMessage()
    unmarshals
    mySubjectHeader
    and stores the
    Subject
    in
    SubjectHolder
    (a static
    ThreadLocal
    )
  • ProcessManager.triggerProcess()
    later injects
    SubjectHolder.getSubject()
    into business processes
  • Missing headers leave stale identities intact

In-Field PoC

The advisory demonstrates a two-step SOAP abuse:

  1. First
    getUsers
    to leak information
  2. Then
    createUser
    +
    importCredential
    to plant a rogue admin when the privileged thread hits

Validating the Vulnerability

Development/Debug Environment

Attach JDWP to watch the

ThreadLocal
contents:

# Start with JDWP
-agentlib:jdwp=transport=dt_socket,server=y,address=5005,suspend=n

# Monitor ThreadLocal before and after each call
# Confirm that unauthenticated requests inherit prior administrator Subject

Production Environment

Use JFR (Java Flight Recorder) or BTrace to dump

SubjectHolder.getSubject()
per request:

  • Verify header-less reuse is occurring
  • Confirm the
    Subject
    persists across requests
  • Document the thread reuse pattern

Common Indicators

Look for these signs that a SOAP endpoint may be vulnerable:

  • SOAP handlers that only set authentication context when headers are present
  • Static
    ThreadLocal
    variables in handler chains
  • No explicit clearing of authentication state
  • Application servers with thread pooling (WebLogic, JBoss, GlassFish)
  • Custom authentication headers that are optional or can be omitted
  • SOAP endpoints that accept requests without authentication headers

Mitigation Recommendations

If you're securing a vulnerable system:

  1. Always clear ThreadLocal on request completion - Use
    finally
    blocks or interceptor cleanup
  2. Validate authentication on every request - Don't rely on cached state
  3. Use request-scoped authentication - Bind authentication to the request lifecycle
  4. Clear ThreadLocal in handler chains - Explicitly reset state when headers are missing
  5. Implement proper thread-local cleanup - Use
    ThreadLocal.remove()
    in finally blocks

References