Sploitus

Exploit for CVE-2026-69243

githubexploit Β· 2026-08-04

Exploit Code

README119 lines
## https://sploitus.com/exploit?id=0FC311AA-83B9-57D6-9823-AAA621291851
# CVE-2026-69243 β€” aiohttp Request Smuggling (CWE-444)

Request smuggling via rejected WebSocket upgrade in aiohttp   
```

Or copy-paste the Python version (zero dependencies, stdlib only):

```bash
curl -O https://raw.githubusercontent.com/user/cve-2026-69243/main/poc.py
python3 poc.py   
```

Both produce **byte-identical** payloads. If the lab is running:

```bash
python3 poc.py nginx-upgrade 80 backend-vuln
```

Expected output: 1 HTTP response (`WebSocket upgrade rejected`). Then verify:

```bash
# Backend processed 2 requests (/ws + smuggled /admin):
docker logs backend-vuln | grep -c '"path".*"/admin"'

# Nginx only logged 1 request (the /ws):
docker exec nginx-upgrade cat /logs/nginx-upgrade.access.log | grep -c '/admin'
```

The backend count > 0 and Nginx count = 0 means CWE-444 split
confirmed. This PoC sends a single TCP segment β€” the body is the
smuggled request; Nginx treats it as body, aiohttp treats it as a
second request.

## What this proves

- **Parser confusion**: aiohttp 3.14.1 `_http_parser.pyx` returns 2 (skip body)
  on upgrade detection before the body is consumed (line ~863). Body bytes
  remain in `_message_tail` and are fed back to the parser in `web_protocol.py`
  `finish_response` (line ~771).
- **CWE-444 split**: The frontend sees 1 request with body; the backend sees
  2 pipelined requests. Request-count mismatch in logs.
- **Access-control bypass**: When Nginx has `location /admin { deny all; }`,
  the smuggled `/admin` still reaches the backend because Nginx routing
  decisions are made on the outer request only.
- **No app-level fix**: `await request.read()` returns 0 bytes on upgrade
  requests in 3.14.1 β€” the body is withheld below the handler layer.

## Full lab

```bash
git clone https://github.com/user/cve-2026-69243
cd cve-2026-69243/cve-2026-69243-lab
docker compose up -d

# Run the PoC:
docker compose run --rm --entrypoint /app/poc attacker nginx-upgrade 80 backend-vuln
```

Services:

| Container | Purpose |
|-----------|---------|
| `backend-vuln` | aiohttp 3.14.1 (vulnerable), READ_BODY=false |
| `backend-vuln-read` | aiohttp 3.14.1, READ_BODY=true (proves handler can't help) |
| `backend-patched` | aiohttp 3.14.2 (fixed) |
| `nginx-upgrade` | Forwards upgrade headers (`deny all` on /admin) |
| `nginx-default` | No upgrade forwarding (neutralizes the bug) |
| `nginx-strip` | `Connection ""` strip (neutralizes the bug) |
| `attacker` | Rust binaries: reproduce, fase2, poc |

Full Phase 1-3 findings in `findings/`.

## Limitations (honest)

- **Proxy must forward upgrade headers.** Nginx configs that send
  `Connection: close` to the backend or strip `Connection`/`Upgrade`
  headers are not vulnerable. Only the "WebSocket upgrade map" config
  (common in production when WebSocket support is needed) exposes the split.
- **Smuggled response is absorbed by the proxy.** The attacker receives
  only the outer `/ws` response through the proxy. Evidence of smuggling
  is in the backend logs, not in the attacker response. This is a blind
  smuggling primitive.
- **Chunked variant:** `Transfer-Encoding: chunked` framing works equally
  (tested in lab; use `--chunked` flag). The parser skips the body
  regardless of framing mode.
- **Requires an endpoint that rejects WebSocket upgrades.** The handler
  must return a non-`WebSocketResponse`. Most apps that don't use
  WebSockets on a route will reject by default (framework returns 404
  or falls through to the next handler).

## Files

```
poc/                       # Rust cargo project
β”œβ”€β”€ Cargo.toml
β”œβ”€β”€ src/main.rs            # CLI binary
β”œβ”€β”€ src/lib.rs             # Library + unit tests
β”œβ”€β”€ tests/parity.rs        # Cross-language payload parity test
└── fuzz/                  # cargo-fuzz targets
poc.py                     # Python PoC (copy-paste from blog)
.github/workflows/
β”œβ”€β”€ ci.yml                 # Build, test, clippy, parity, integration, fuzz
└── release.yml            # Cross-compile + GitHub Release
cve-2026-69243-lab/        # Docker lab
└── findings/              # Research notes (Phase 1-3)
```

## Detection

See `findings/fase3-deteccao.md` for deployable log queries:

1. Request-count mismatch: backend log count > frontend log count for same
   remote IP + time window.
2. Backend request without frontend match: `/admin` (or any restricted path)
   in backend log with no corresponding entry in frontend access log.
3. Upgrade rejection + immediate pipelined request: `/ws` with `Upgrade`
   header followed by different path from same remote IP within  header-size` on WebSocket endpoints: body present on
   an upgrade request.