GHSA-cjcg-cxmh-9wcr is a high-severity (CVSS 7.5) vulnerability in praxis-proxy. A fix is available for praxis-proxy — see the affected versions and patch details below.
Real-World Exposure
praxis-proxyReal-time download stats are indexed for npm and PyPI packages. This vulnerability affects crates.io packages — download data is not available via public APIs for these ecosystems.
Description
Summary
Multiple denial-of-service vulnerabilities have been discovered in HTTP/2 server implementations. All have been rated with a severity impact of Important. The vulnerabilities target HPACK, the header compression scheme in HTTP/2, where a small request can trigger large memory allocations on the server.
Details
Credit to the original researcher, I'm mostly just run their tool against the code base.
Security Bulletins: https://access.redhat.com/security/vulnerabilities/RHSB-2026-007 Exploit details: https://blog.calif.io/p/codex-discovered-a-hidden-http2-bomb
This bug was fixed in upstream pingora v0.8.1, but our fork (v0.8.2) is missing this important PR to set the default h2 options. (edited)
PoC
- Generate certificates
openssl req -x509 -newkey ec -pkeyopt ec_paramgen_curve:prime256v1 -keyout server.key -out server.crt -days 3650 -nodes -subj "/CN=localhost"
- Create praxis config as follow
listeners:
- name: web
address: "0.0.0.0:8443"
tls:
certificates:
- cert_path: /etc/praxis/server.crt
key_path: /etc/praxis/server.key
filter_chains: [main]
filter_chains:
- name: main
filters:
- filter: router
routes:
- path_prefix: "/"
host: "example.api.com"
cluster: backend
- filter: load_balancer
clusters:
- name: backend
endpoints:
- "httpbingo.org:443"
tls:
verify: false
- Start the container
docker run --name praxis --user $(id -u):$(id -g) -it --rm -p 8443:8443 -v ./config.yaml:/etc/praxis/config.yaml -v ./server.crt:/etc/praxis/server.crt -v ./server.key:/etc/praxis/server.key ghcr.io/praxis-proxy/praxis:0.5.1
- Check container memory
$ docker stats
CONTAINER ID NAME CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O PIDS
362cfa472792 praxis 0.00% 6.473MiB / 62.49GiB 0.01% 7.57kB / 126B 0B / 0B 22
- In another terminal run the attack
./hpack_bomb.py --host 127.0.0.1 --port 8443 -n 10
- Observer the container memory
98b040c5e5ad praxis 0.13% 687.1MiB / 62.49GiB 1.07% 41.6MB / 362kB 0B / 0B 23
Memory usage spiked to around 700MB, and even after the attack ended, the memory was not freed up.
- Patch the code to set h2options
diff --git a/protocol/src/http/pingora/handler/mod.rs b/protocol/src/http/pingora/handler/mod.rs
index dc684ad..fc59234 100644
--- a/protocol/src/http/pingora/handler/mod.rs
+++ b/protocol/src/http/pingora/handler/mod.rs
@@ -18,6 +18,7 @@ use std::{collections::HashMap, sync::Arc, time::Duration};
use arc_swap::ArcSwap;
use bytes::Bytes;
+use pingora_core::protocols::http::v2::server::H2Options;
use pingora_core::{Result, apps::HttpServerOptions, server::Server, services::listening::Service};
use pingora_proxy::{Session, http_proxy};
use praxis_core::{config::ABSOLUTE_MAX_BODY_BYTES, connectivity::Upstream};
@@ -151,6 +152,11 @@ where
let service_name = format!("http-proxy:{name}", name = listener.name);
let mut proxy = http_proxy(&server.configuration, handler);
proxy.server_options = Some(h2c_server_options());
+ let mut h2_options = H2Options::new();
+ h2_options.max_header_list_size(65536);
+ h2_options.max_concurrent_streams(32);
+ proxy.h2_options = Some(h2_options);
+
let mut service = Service::new(service_name, proxy);
if let Some(tx) = super::listener::add_listener(&mut service, listener)? {
cert_watcher_shutdowns.push(tx);
- Rerun the attack, the memory usage looks a lot better now
CONTAINER ID NAME CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O PIDS
b1c82abca409 praxis 0.04% 10.09MiB / 62.49GiB 0.02% 1.16MB / 23.4kB 950kB / 0B 23
Affected Packages
| Ecosystem | Package | Vulnerable range | Fix |
|---|---|---|---|
| 🦀crates.io | praxis-proxy | all versions | 0.5.2cargo update -p praxis-proxy --precise 0.5.2 |
Detection & mitigation playbook
Open-source dependencyDetect
Scan your dependency tree (package-lock.json, pnpm-lock.yaml, requirements.txt, go.sum, etc.) for praxis-proxy, including transitive dependencies — a direct dependency you never call can still pull in a vulnerable version.
Fix
Update praxis-proxy to 0.5.2 or later, then make sure no transitive (indirect) dependency still pins the vulnerable range — O3 confirms GHSA-cjcg-cxmh-9wcr is resolved across your whole dependency graph.
Workarounds
Cap what an attacker can consume: apply request size, rate and timeout limits in front of the affected component, and run it with memory and CPU limits so exhaustion degrades one worker rather than the whole service.
Frequently Asked Questions
Is GHSA-cjcg-cxmh-9wcr in your dependencies?
Find it across crates.io, including transitive dependencies.