Full Report
WSO2 API manage is an API gateway and lifecycle management platform. It's similar to Kong. The API gateway gateway takes in HTTP requests and forwards them to the backend API servers. WSO2 adds authentication, rate limiting and easy deployment. This code is built on top of Apache Synapse, a Java mediation framework with several customization. The 404 not found page was using a templating engine. the page itself was defined in a Synapse 'sequence' file in XML with the text containing the URL path as input. Like any templating engine, there is logic to replace the placeholder with the real value. This logic tried to see if the input is valid XML by creating an XML document with it. If it's not, then the data is escaped. What's the issue here? The input is not escaped in XML! This leads to a classic template injection. What's weird though is that this turns into eXternal XML Entity (XXE) injection. Since the import happens twice (once in verification of the data and once in adding to the sequence file), the second time will always fail. Practically, this means that the exploitation of this must be blind. The exploitable parameter is the path of the URL. So, the author needed to create valid XML that was also a valid URL path. To do this, tabs must be used instead of spaces. Although this should technically be illegal, the server allows it; encoding the spaces with %20 didn't work either because it won't be decoded before it hits the XML parser. Another issue arises: the DTD thinks this is an absolute URL path. So, the path itself needs to include a URL at the beginning of it. The actual URL being used for the XXE is nested inside of this. The exploit is super funky looking: GET /http://whatever/ HTTP/1.1 The payload above will reach out to the web server http://evil.com:8080. So, what does this mean with blind XXE? In Java, you can include a file, such as /etc/passwd and send the contents of the file as FTP commands. This is only possible in older versions of Java; in newer versions, URLs cannot have newlines in them, which prevents this from working. In WSO2 API Manager 2.1.0, the reflection above was fixed via not returning the path in the text because of reflected XSS. Lolz - there was something lurking way deeper! The isXML XXE was not patched until years later. Without the 404, it required adding a custom page to the API gateway that used the payloadFactory type. Most developer docs and Stack Overflow posts were vulnerable to this issue though. In 3.0.0, a service to transform XML requests into JSON was added. It takes a POST request with an XML document and forwards the request to the backend service. Several of the inputs are templated; hence, they are passed to the vulnerable isXML function. This creates a new universal path exploit on the project. In 3.1.0, the XXE bug was fixed but readded back in 4.0.0. Finally, for version 4.3.0, the vulnerability is fixed for good. Overall, a great post! The history of the issue and exploitability on different versions was interesting to read about.
Analysis Summary
# Vulnerability: Blind XXE via Payload Factory Template Injection in WSO2
## CVE Details
- **CVE ID:** CVE-2025-2905
- **CVSS Score:** 9.8 (Critical) - *Estimated based on remote unauthenticated file read capability*
- **CWE:** CWE-611 (Improper Restriction of XML External Entity Reference), CWE-94 (Improper Control of Generation of Code 'Code Injection')
## Affected Systems
- **Products:** WSO2 API Manager, and other WSO2 products utilizing the WSO2-Synapse mediation framework.
- **Versions:**
- WSO2 API Manager ≤ 2.1.0 (Fixed in 2.1.0 via XSS patch, but the underlying root cause remained)
- WSO2 API Manager 3.0.0 (Introduced new universal path)
- WSO2 API Manager 4.0.0 (Vulnerability re-introduced)
- **Configurations:** Default installations using the `PayloadFactory` mediator in Synapse sequences or the `WorkflowCallbackService`.
## Vulnerability Description
The vulnerability exists in the `isXML` function within the `PayloadFactoryMediator.java` of WSO2’s fork of Apache Synapse. When the gateway processes a templated response (such as a 404 page or a transformation service), it attempts to determine if the input argument is valid XML.
To perform this check, the engine creates an XML document from the user-supplied input. Because this process fails to disable DTDs or external entity resolution, it is susceptible to **eXternal XML Entity (XXE)** injection. Although the second stage of the transformation often fails, the initial "validation" stage successfully triggers the external entity resolution, resulting in a **Blind XXE** flaw.
## Exploitation
- **Status:** PoC available; discussed in research series.
- **Complexity:** Medium (Requires specific formatting like tabs instead of spaces and nested URLs to bypass parser restrictions).
- **Attack Vector:** Network (Unauthenticated).
### Exploitation Details:
1. **404 Vector:** In older versions, a GET request to a non-existent path containing an XML payload could trigger the flaw.
2. **Workflow Vector:** In version 3.0.0+, a POST request to `/services/WorkflowCallbackService` with a malicious XML payload in the `status` or `description` fields can trigger the flaw.
3. **Java Version Impacts:** On older Java versions, attackers can utilize the FTP protocol to exfiltrate file contents (e.g., `/etc/passwd`). Newer Java versions restrict newlines in URLs, limiting exfiltration options.
## Impact
- **Confidentiality:** High (Arbitrary local file read through out-of-band exfiltration).
- **Integrity:** None.
- **Availability:** Low (Potential for SSRF or resource exhaustion).
## Remediation
### Patches
- **Version 4.3.0:** Reported as the definitive fix version where the vulnerability is resolved for good.
- **Version 3.1.0:** Contained a temporary fix before the regression in 4.0.0.
### Workarounds
- **Sequence Modification:** Manually edit `main.xml` or custom sequence files to remove `$1` (the request path) from the 404 error template to prevent reflection.
- **Service Disablement:** Disable the `WorkflowCallbackService` if not required for business operations.
## Detection
- **Indicators of Compromise:**
- Unusual HTTP GET requests containing XML tags (`<`, `>`, `!ENTITY`) in the URL path.
- Outbound connections (especially via FTP or HTTP) from the WSO2 Gateway to unknown external IPs.
- POST requests to `/services/WorkflowCallbackService` containing DTD or ENTITY keywords.
- **Detection Methods:** Web Application Firewall (WAF) rules targeting XML signatures within URL paths or non-XML POST bodies.
## References
- **Vendor Advisory:** hxxps://lokvica[.]com/discoveries/CVE-2025-2905/
- **Research Post:** hxxps://crnkovic[.]dev/wso2-404-to-arbitrary-file-read/