Hunting server-side and edge-side includes: when a directive is reflected
Two different tiers can turn reflected text into an executed directive. On the origin, a server with
server-side includes enabled scans documents for directives and will read a file or run a command when it
finds one, so untrusted input reflected into such a document and interpreted as a directive becomes file
disclosure or command execution. At the edge, a cache, content-delivery network, or reverse proxy may
parse edge-side include tags in responses and act on them, fetching a URL, splicing a fragment, or reading
a request variable, so a reflected tag becomes server-side request forgery into the internal network, a
poisoned shared cache, or a page that copies the victim's cookie back to the attacker. The edge variant is
insidious: the application developer never wrote include handling and cannot see it in the origin code,
because a separate tier performs it. You find both by locating reflection into processed documents and
determining which processor sits in the path.
When to use
- Untrusted input is reflected into a page, template, or fragment served by an include-capable server.
- A cache, content-delivery network, or reverse proxy in front of the origin may parse edge-side includes.
- Response surfaces (headers, error pages, cached fragments) echo input into documents processed downstream.
Scope check
Test include injection only against systems you own or are authorized to assess, on non-production
infrastructure, because a confirming directive can read server files, reach internal services, or poison a
cache shared by other users. Prefer benign, observable probes over destructive commands, and coordinate
before touching a shared cache. If you can't name the authorization, stop.
The loop
Establish that an include processor is actually in the path first. Determine whether the origin has
server-side includes enabled for the served document type, or whether a downstream cache, delivery
network, or proxy parses edge-side include tags. This is the false-positive killer: if no processor
interprets includes, reflected directive text is inert markup and there is no bug. Name the processor
and the tier that runs it before treating any reflection as a lead.
Find the reflection point. Locate where untrusted input is echoed into a document the processor will
scan: page body, a template fragment, an error page, a header value copied into a cached response.
Confirm the input reaches the document before the processor runs, not after.
Check for neutralization before the processor. Read whether the reflected value is entity-encoded,
whether the include syntax's delimiters are stripped or escaped, and whether the document type is one
the processor ignores. Encoding that turns the directive's brackets into inert entities before the
processor sees them closes the class; determine whether it is applied on this path.
Test the server-side directive impact. Where the origin processes includes, probe with a benign
directive that reflects an observable value (a document variable, a size) and, only within scope,
assess whether file-include and command-exec directives are permitted. The reachable impact is exactly
the directive set the server allows.
Test the edge-side directive impact. Where the edge parses tags, probe whether an include tag makes
the edge fetch a URL you control or an internal address (request forgery), whether the result is stored
in a shared cache (poisoning that affects other users), and whether request variables such as cookies
can be read into the page (token theft). Confirm which behaviors the specific edge honors.
Confirm and record. Confirm server-side by reflecting a benign directive that returns an observable
value, and edge-side by causing an interaction with a host you control or an observable cache change, on
an isolated instance or an isolated cache key. Kill the lead if no include processor is in the path, if
the reflected value is encoded so directive delimiters are inert before processing, or if the processor
ignores the served document type. Record the tier, the processor, the reflection point, the directive,
and the effect. Set kill_reason when killing.
Where include injection leaks
- The edge processor is invisible to the origin. The application code has no include handling, so a
source-only review of the origin misses an edge-side vulnerability performed by a separate tier.
- Reflection into cached responses is shared. A directive spliced into a response stored under a shared
cache key affects every user who receives that cached entry.
- Headers and error pages are reflection points too. Input echoed into a header the edge copies into the
body, or into a verbose error page, reaches the processor just as a normal page does.
- Edge includes reach the internal network. An include tag that fetches a URL runs from the edge or
origin's vantage, making it a request-forgery primitive against internal services.
- Encoding must precede the processor. Output encoding applied after the include tier has already run
does not help; the neutralization has to happen before the directive is scanned.
Worked example (a confirm and a kill)
Confirm. A delivery tier in front of the application parses edge-side include tags, and a search
page reflects the query into the response body without encoding the tag delimiters. A query containing an
include tag pointed at a host the tester controls causes the edge to fetch that host, and pointing it at
an internal metadata address returns internal data into the page; the response is stored under a shared
cache key. Confirmed edge-side include injection to server-side request forgery and cache poisoning,
high, remediation = disable edge-side include parsing on responses built from user input, or
entity-encode tag delimiters at the origin before the response reaches the edge.
Kill. An error page reflects the requested path, but the origin has server-side includes disabled for
the served type and no cache or proxy in the path parses edge-side includes; the value is additionally
entity-encoded. A reflected directive renders as visible inert text. Killed, kill_reason = "no
include processor runs on this path at either tier and the delimiters are entity-encoded, so the reflected
directive is never interpreted."
Rationalizations to reject
- "We do not use server-side includes." -> The origin may not, but a cache or delivery network in front of
it can parse edge-side includes; confirm the whole path, not just the application server.
- "The tag just shows up as text." -> That means this document type or tier does not process it; verify the
processor, because a different surface or content type may be parsed.
- "Output is encoded." -> Only if the encoding runs before the include processor; encoding applied after the
edge has already interpreted the tag is too late.
- "It is only reflected, not stored." -> Reflection into a response cached under a shared key becomes stored
for every user who reads that cache entry.
- "Internal URLs are not reachable." -> An edge-side fetch runs from inside the perimeter; that is exactly
the request-forgery vantage that reaches internal services.
Executing this in practice
You need the served document types and whether the origin has includes enabled, the presence and behavior
of any cache, delivery network, or proxy that parses edge-side includes, every reflection point into
processed documents, and the encoding applied before each processor. For each reflection, decide which tier
interprets includes, whether delimiters survive to the processor, and what a directive could read, fetch,
or poison. Reading the config and code shows the processor and encoding; a benign directive probe shows
whether it is interpreted.
Related
testing-web-cache-attacks - the shared-cache poisoning that an edge-side include can cause is that
skill's core concern; the include tag is one way input reaches a cached response.
hunting-reflected-and-stored-xss - the same reflection points feed a browser sink there and an include
processor here; check both interpreters for a given reflection.
hunting-host-header-and-url-parsing-trust - an edge-side fetch is a request-forgery primitive; that skill
covers where such internal requests are trusted.
adjudicating-taint-paths - use it to connect the reflection to the processor across the origin and edge
tiers.
- FINDING-SCHEMA.md - source = the reflected untrusted input, sink = the include
or exec directive the processor evaluates, evidence = a benign directive returning an observable value or
an out-of-band interaction on an isolated instance.
1---2name: hunting-server-side-and-edge-side-includes3description: Hunt server-side include and edge-side include injection, where untrusted input is reflected into a document that a processor later interprets for include and exec directives. When an origin server has server-side includes enabled, a reflected directive can read a server file or run a command. When a cache, content-delivery network, or proxy in front of the origin parses edge-side include tags, a reflected tag can fetch an internal URL for server-side request forgery, poison a shared cache, or copy a victim's cookie into a page. The edge case is easy to miss because the origin developer never sees the processor that evaluates the tag. Use when reflected input reaches a document that an include-capable server or edge tier processes. The reflected input is the source, the include or exec directive the processor evaluates is the sink, and the resulting file read, command, request forgery, or cache poisoning is the bug.4license: MIT5---67# Hunting server-side and edge-side includes: when a directive is reflected89Two different tiers can turn reflected text into an executed directive. On the origin, a server with10server-side includes enabled scans documents for directives and will read a file or run a command when it11finds one, so untrusted input reflected into such a document and interpreted as a directive becomes file12disclosure or command execution. At the edge, a cache, content-delivery network, or reverse proxy may13parse edge-side include tags in responses and act on them, fetching a URL, splicing a fragment, or reading14a request variable, so a reflected tag becomes server-side request forgery into the internal network, a15poisoned shared cache, or a page that copies the victim's cookie back to the attacker. The edge variant is16insidious: the application developer never wrote include handling and cannot see it in the origin code,17because a separate tier performs it. You find both by locating reflection into processed documents and18determining which processor sits in the path.1920## When to use2122- Untrusted input is reflected into a page, template, or fragment served by an include-capable server.23- A cache, content-delivery network, or reverse proxy in front of the origin may parse edge-side includes.24- Response surfaces (headers, error pages, cached fragments) echo input into documents processed downstream.2526## Scope check2728Test include injection only against systems you own or are authorized to assess, on non-production29infrastructure, because a confirming directive can read server files, reach internal services, or poison a30cache shared by other users. Prefer benign, observable probes over destructive commands, and coordinate31before touching a shared cache. If you can't name the authorization, stop.3233## The loop34351. **Establish that an include processor is actually in the path first.** Determine whether the origin has36 server-side includes enabled for the served document type, or whether a downstream cache, delivery37 network, or proxy parses edge-side include tags. This is the false-positive killer: if no processor38 interprets includes, reflected directive text is inert markup and there is no bug. Name the processor39 and the tier that runs it before treating any reflection as a lead.40412. **Find the reflection point.** Locate where untrusted input is echoed into a document the processor will42 scan: page body, a template fragment, an error page, a header value copied into a cached response.43 Confirm the input reaches the document before the processor runs, not after.44453. **Check for neutralization before the processor.** Read whether the reflected value is entity-encoded,46 whether the include syntax's delimiters are stripped or escaped, and whether the document type is one47 the processor ignores. Encoding that turns the directive's brackets into inert entities before the48 processor sees them closes the class; determine whether it is applied on this path.49504. **Test the server-side directive impact.** Where the origin processes includes, probe with a benign51 directive that reflects an observable value (a document variable, a size) and, only within scope,52 assess whether file-include and command-exec directives are permitted. The reachable impact is exactly53 the directive set the server allows.54555. **Test the edge-side directive impact.** Where the edge parses tags, probe whether an include tag makes56 the edge fetch a URL you control or an internal address (request forgery), whether the result is stored57 in a shared cache (poisoning that affects other users), and whether request variables such as cookies58 can be read into the page (token theft). Confirm which behaviors the specific edge honors.59606. **Confirm and record.** Confirm server-side by reflecting a benign directive that returns an observable61 value, and edge-side by causing an interaction with a host you control or an observable cache change, on62 an isolated instance or an isolated cache key. Kill the lead if no include processor is in the path, if63 the reflected value is encoded so directive delimiters are inert before processing, or if the processor64 ignores the served document type. Record the tier, the processor, the reflection point, the directive,65 and the effect. Set `kill_reason` when killing.6667## Where include injection leaks6869- **The edge processor is invisible to the origin.** The application code has no include handling, so a70 source-only review of the origin misses an edge-side vulnerability performed by a separate tier.71- **Reflection into cached responses is shared.** A directive spliced into a response stored under a shared72 cache key affects every user who receives that cached entry.73- **Headers and error pages are reflection points too.** Input echoed into a header the edge copies into the74 body, or into a verbose error page, reaches the processor just as a normal page does.75- **Edge includes reach the internal network.** An include tag that fetches a URL runs from the edge or76 origin's vantage, making it a request-forgery primitive against internal services.77- **Encoding must precede the processor.** Output encoding applied after the include tier has already run78 does not help; the neutralization has to happen before the directive is scanned.7980## Worked example (a confirm and a kill)8182> **Confirm.** A delivery tier in front of the application parses edge-side include tags, and a search83> page reflects the query into the response body without encoding the tag delimiters. A query containing an84> include tag pointed at a host the tester controls causes the edge to fetch that host, and pointing it at85> an internal metadata address returns internal data into the page; the response is stored under a shared86> cache key. **Confirmed** edge-side include injection to server-side request forgery and cache poisoning,87> `high`, remediation = disable edge-side include parsing on responses built from user input, or88> entity-encode tag delimiters at the origin before the response reaches the edge.89>90> **Kill.** An error page reflects the requested path, but the origin has server-side includes disabled for91> the served type and no cache or proxy in the path parses edge-side includes; the value is additionally92> entity-encoded. A reflected directive renders as visible inert text. **Killed**, `kill_reason` = "no93> include processor runs on this path at either tier and the delimiters are entity-encoded, so the reflected94> directive is never interpreted."9596## Rationalizations to reject9798- *"We do not use server-side includes."* -> The origin may not, but a cache or delivery network in front of99 it can parse edge-side includes; confirm the whole path, not just the application server.100- *"The tag just shows up as text."* -> That means this document type or tier does not process it; verify the101 processor, because a different surface or content type may be parsed.102- *"Output is encoded."* -> Only if the encoding runs before the include processor; encoding applied after the103 edge has already interpreted the tag is too late.104- *"It is only reflected, not stored."* -> Reflection into a response cached under a shared key becomes stored105 for every user who reads that cache entry.106- *"Internal URLs are not reachable."* -> An edge-side fetch runs from inside the perimeter; that is exactly107 the request-forgery vantage that reaches internal services.108109## Executing this in practice110111You need the served document types and whether the origin has includes enabled, the presence and behavior112of any cache, delivery network, or proxy that parses edge-side includes, every reflection point into113processed documents, and the encoding applied before each processor. For each reflection, decide which tier114interprets includes, whether delimiters survive to the processor, and what a directive could read, fetch,115or poison. Reading the config and code shows the processor and encoding; a benign directive probe shows116whether it is interpreted.117118## Related119120- `testing-web-cache-attacks` - the shared-cache poisoning that an edge-side include can cause is that121 skill's core concern; the include tag is one way input reaches a cached response.122- `hunting-reflected-and-stored-xss` - the same reflection points feed a browser sink there and an include123 processor here; check both interpreters for a given reflection.124- `hunting-host-header-and-url-parsing-trust` - an edge-side fetch is a request-forgery primitive; that skill125 covers where such internal requests are trusted.126- `adjudicating-taint-paths` - use it to connect the reflection to the processor across the origin and edge127 tiers.128- [FINDING-SCHEMA.md](../../FINDING-SCHEMA.md) - source = the reflected untrusted input, sink = the include129 or exec directive the processor evaluates, evidence = a benign directive returning an observable value or130 an out-of-band interaction on an isolated instance.