Skip to content

ikarolaborda/CVE2026-42926

Repository files navigation

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:

chmod +x nginx_config_verify.sh run_lab_comparison.sh

Confirm PHP has cURL support:

php -m | grep -i curl

Confirm each NGINX binary can print its version:

/path/to/nginx -V

Lab Configuration

The provided nginx_vulnerable.conf contains the required test pattern:

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:

docker compose build

Start the upstream logger and vulnerable NGINX:

docker compose up -d upstream nginx

Confirm the bundled NGINX version:

docker compose exec nginx nginx -V

Run the validation script inside the Compose network:

docker compose --profile run run --rm runner

The runner uses these in-container arguments:

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:

./upstream_logs/

The NGINX service is also exposed to the host at:

http://localhost:8080/version

Stop and remove the lab containers:

docker compose down

Quick Start: Single Validation Run

Start the controlled upstream logger:

python3 upstream_frame_logger.py 8081 ./upstream_logs

In another terminal, start NGINX with the sample configuration:

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

Run the validation script:

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:

/path/to/nginx -s stop

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

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:

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:

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:

./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:

php cve_2026_42926_lab.php <target_url> <upstream_log_dir> <config_script> <nginx_binary> <nginx_config> <location> [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:

frames_<timestamp>.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:

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.

About

Controlled NGINX HTTP/2 frame injection lab for CVE-2026-42926 patch validation and defensive research

Topics

Resources

License

Stars

0 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors