Share
## https://sploitus.com/exploit?id=EADB77CD-DA9A-5377-820A-B7574B833CE4
# CVE-2026-42926 NGINX HTTP/2 Frame Injection Lab

A controlled cybersecurity lab for validating and comparing behavior related to
**CVE-2026-42926**, an **HTTP/2 frame injection** issue affecting specific NGINX
versions when a vulnerable proxy configuration is used.

This repository is intended for **defensive research, patch validation,
configuration auditing, and controlled lab reproduction only**.

> Classification: HTTP/2 frame injection
> Affected versions: NGINX `1.29.4` through `1.30.0`
> Fixed versions: NGINX `1.30.1+` / `1.31.0+`

## Safety Notice

Use this project only in an isolated lab environment that you own or are
explicitly authorized to test.

Do not run this against third-party systems, public infrastructure, shared
environments, or production services without written authorization.

Recommended isolation:

- Local VM
- Disposable container
- Private test host
- Non-routable lab network

## What This Lab Does

The lab checks whether a target NGINX binary and configuration match the
conditions needed to reproduce the issue, sends a crafted request to the test
location, and inspects a controlled upstream logger for evidence that injected
HTTP/2 frame-like bytes reached the upstream side.

The normal validation pattern is:

1. Run the same vulnerable proxy configuration with a vulnerable NGINX build.
2. Run the same vulnerable proxy configuration with a patched NGINX build.
3. Compare the upstream evidence and script verdicts.

Expected result:

- Vulnerable build: injection evidence may be observed.
- Patched build: no injection evidence should be observed.

## Repository Contents

| File | Description |
| --- | --- |
| `README.md` | Project documentation. |
| `LICENSE` | MIT license. |
| `Dockerfile` | Builds a self-contained lab image with NGINX `1.29.4`, PHP CLI/cURL, Python, and the project scripts. |
| `docker-compose.yml` | Starts the vulnerable NGINX service, upstream frame logger, and an optional validation runner. |
| `cve_2026_42926_lab.php` | Main lab validation script. It checks version/config preconditions, sends the crafted request, inspects upstream logs, and returns a verdict. |
| `nginx_vulnerable.conf` | Sample NGINX configuration containing the vulnerable proxying pattern used for both vulnerable and patched comparison runs. |
| `docker/nginx_vulnerable.docker.conf` | Docker-specific NGINX config using the same vulnerable pattern and Compose service discovery. |
| `nginx_config_verify.sh` | Helper that verifies whether a target NGINX config contains the required vulnerable proxy pattern. |
| `upstream_frame_logger.py` | Controlled raw HTTP/2 upstream logger used to capture and inspect frames received from NGINX. |
| `run_lab_comparison.sh` | Orchestrates vulnerable-vs-patched comparison runs. |
| `.dockerignore` | Keeps generated logs and IDE metadata out of Docker build context. |

## Requirements

- Linux or macOS shell environment
- `bash`
- `python3`
- `php`
- PHP cURL extension
- NGINX test binaries for the versions you want to compare
- Permission to bind the configured NGINX listen port

For the containerized workflow:

- Docker
- Docker Compose v2

The sample config listens on port `80`, which commonly requires root privileges.
For an unprivileged local lab, change `listen 80;` in `nginx_vulnerable.conf` to
an available high port such as `8080`, then use the matching target URL in the
PHP command.

## Setup

Make the shell scripts executable:

```bash
chmod +x nginx_config_verify.sh run_lab_comparison.sh
```

Confirm PHP has cURL support:

```bash
php -m | grep -i curl
```

Confirm each NGINX binary can print its version:

```bash
/path/to/nginx -V
```

## Lab Configuration

The provided `nginx_vulnerable.conf` contains the required test pattern:

```nginx
location /exploit {
    proxy_pass http://127.0.0.1:8081;
    proxy_http_version 2;
    proxy_set_body $request_body;
    proxy_set_header Host $host;
    proxy_set_header Content-Length $content_length;
}
```

Important details:

- `proxy_http_version 2` enables HTTP/2 proxying to the upstream logger.
- `proxy_set_body $request_body` uses a client-controlled request body.
- `client_max_body_size 20m` allows the crafted 16 MiB request body used by the
  validation script.
- The upstream logger listens on `127.0.0.1:8081` by default.

The Docker-specific config in `docker/nginx_vulnerable.docker.conf` keeps the
same vulnerable proxy pattern but listens on container port `8080` and proxies
to the Compose service name `upstream:8081`.

## Docker Quick Start: NGINX 1.29.4 Lab

The Dockerfile builds NGINX `1.29.4` from source and installs the PHP/Python
tooling required by the lab. Compose then runs three services from the same
image:

- `upstream`: raw HTTP/2 frame logger
- `nginx`: vulnerable NGINX `1.29.4` using `docker/nginx_vulnerable.docker.conf`
- `runner`: one-shot PHP validation command

Build the lab image:

```bash
docker compose build
```

Start the upstream logger and vulnerable NGINX:

```bash
docker compose up -d upstream nginx
```

Confirm the bundled NGINX version:

```bash
docker compose exec nginx nginx -V
```

Run the validation script inside the Compose network:

```bash
docker compose --profile run run --rm runner
```

The runner uses these in-container arguments:

```text
php /lab/cve_2026_42926_lab.php \
  http://nginx:8080/exploit \
  /lab/upstream_logs \
  /lab/nginx_config_verify.sh \
  /usr/local/nginx/sbin/nginx \
  /lab/docker/nginx_vulnerable.docker.conf \
  /exploit
```

Generated upstream logs are written to the host directory:

```text
./upstream_logs/
```

The NGINX service is also exposed to the host at:

```text
http://localhost:8080/version
```

Stop and remove the lab containers:

```bash
docker compose down
```

## Quick Start: Single Validation Run

Start the controlled upstream logger:

```bash
python3 upstream_frame_logger.py 8081 ./upstream_logs
```

In another terminal, start NGINX with the sample configuration:

```bash
/path/to/nginx -c "$PWD/nginx_vulnerable.conf"
```

Run the validation script:

```bash
php cve_2026_42926_lab.php \
  http://localhost/exploit \
  ./upstream_logs \
  ./nginx_config_verify.sh \
  /path/to/nginx \
  "$PWD/nginx_vulnerable.conf" \
  /exploit
```

Stop NGINX after the run:

```bash
/path/to/nginx -s stop
```

If you changed NGINX to listen on another port, update the first argument. For
example:

```bash
php cve_2026_42926_lab.php http://localhost:8080/exploit ./upstream_logs ./nginx_config_verify.sh /path/to/nginx "$PWD/nginx_vulnerable.conf" /exploit
```

## Quick Start: Vulnerable vs Patched Comparison

Set paths to the two NGINX binaries and run the comparison harness:

```bash
VULNERABLE_NGINX=/usr/local/nginx_1.29.4/sbin/nginx \
PATCHED_NGINX=/usr/local/nginx_1.30.1/sbin/nginx \
bash run_lab_comparison.sh
```

Optional configuration overrides:

```bash
VULNERABLE_NGINX=/path/to/vulnerable/nginx \
PATCHED_NGINX=/path/to/patched/nginx \
VULNERABLE_CONFIG="$PWD/nginx_vulnerable.conf" \
PATCHED_CONFIG="$PWD/nginx_vulnerable.conf" \
bash run_lab_comparison.sh
```

The comparison script writes:

- `vulnerable_result.txt`
- `patched_result.txt`
- `upstream_logs_vulnerable/`
- `upstream_logs_patched/`

## Manual Configuration Check

Use `nginx_config_verify.sh` directly when you only want to inspect whether a
configuration contains the vulnerable pattern:

```bash
./nginx_config_verify.sh /path/to/nginx "$PWD/nginx_vulnerable.conf" /exploit
```

The helper checks for:

- `proxy_http_version 2`
- `proxy_set_body` with a variable
- `client_max_body_size` of at least 16 MiB

## Script Arguments

`cve_2026_42926_lab.php` accepts positional arguments:

```text
php cve_2026_42926_lab.php       [version_url]
```

Defaults:

| Argument | Default |
| --- | --- |
| `target_url` | `http://localhost/exploit` |
| `upstream_log_dir` | `./upstream_logs` |
| `config_script` | `./nginx_config_verify.sh` |
| `nginx_binary` | `nginx` |
| `nginx_config` | `/etc/nginx/nginx.conf` |
| `location` | `/exploit` |
| `version_url` | `http://localhost/version` |

## Verdicts and Exit Codes

`cve_2026_42926_lab.php` returns one of three verdicts:

| Verdict | Meaning | Exit code |
| --- | --- | --- |
| `positive` | Frame injection evidence was observed and correlated to the run. | `0` |
| `negative` | Preconditions were met and no injection evidence was observed. | `1` |
| `inconclusive` | One or more preconditions or evidence checks failed. | `2` |

Positive evidence requires the script to correlate the run marker, observed
upstream frame header, injected-frame flag, and affected NGINX version.

## Output Artifacts

The upstream logger writes JSON files named like:

```text
frames_.json
```

Each log contains parsed HTTP/2 frame metadata, payload excerpts, byte offsets,
injection detection flags, and run-correlation fields.

The comparison harness stores separate log directories for vulnerable and
patched runs so evidence from the two executions does not mix.

## Troubleshooting

If the PHP script reports that the config helper is not executable, run:

```bash
chmod +x nginx_config_verify.sh
```

If the request returns `413 Request Entity Too Large`, increase
`client_max_body_size` to at least `16m`; the sample config uses `20m`.

If no upstream logs are created, check that:

- `upstream_frame_logger.py` is running.
- NGINX is proxying to `127.0.0.1:8081`.
- The target URL points to the configured NGINX listener.
- The request reached the `/exploit` location.

If NGINX fails to bind to port `80`, either run it with appropriate privileges
in a lab environment or change the config to a high port such as `8080`.

If the comparison result is inconclusive, inspect `vulnerable_result.txt`,
`patched_result.txt`, and the corresponding upstream log directories for the
failed precondition.

## License

This project is licensed under the MIT License. See `LICENSE` for details.