Share
## https://sploitus.com/exploit?id=80C64604-85E6-5DC4-B136-D905228B262F
# camel-kafka kafka.OVERRIDE_TOPIC Header Injection Reproducer (CVE-2026-49098)

This project demonstrates a **message-header injection** in Apache Camel's `camel-kafka` component, tracked as
**CVE-2026-49098**. `KafkaProducer.evaluateTopic()` reads the `kafka.OVERRIDE_TOPIC` header to choose the target
topic; when present it **overrides the endpoint's configured topic**:

```java
// KafkaProducer.evaluateTopic (affected 4.18.2)
Object overrideTopic = message.removeHeader(KafkaConstants.OVERRIDE_TOPIC);   // "kafka.OVERRIDE_TOPIC"
String overrideTopicString = ...tryConvertTo(String.class, overrideTopic);
if (overrideTopicString != null) {
    return overrideTopicString;              // attacker-chosen topic
}
String topic = configuration.getTopic();
if (topic != null) {
    return topic;                            // the intended, configured topic
}
```

The header constant `OVERRIDE_TOPIC` has the plain value `kafka.OVERRIDE_TOPIC`. **The important nuance that sets
this apart from the other rename-family CVEs:** camel-kafka *does* ship a `KafkaHeaderFilterStrategy` that filters
`kafka.*` โ€” but only on the **Kafka โŸท Exchange** serialization boundary. It does **not** cover the upstream HTTP
consumer's boundary, and the upstream `HttpHeaderFilterStrategy` blocks only the `Camel*` namespace. So a
`kafka.*` header arriving from `from(platform-http).to(kafka:...)` passes straight through, unfiltered.

In such a route, any HTTP client can therefore supply `kafka.OVERRIDE_TOPIC` and **move the record onto an
arbitrary Kafka topic** โ€” injecting an attacker-forged message into a sensitive/privileged topic that a critical
downstream service consumes (integrity), reading nothing but writing where it should not. `kafka.OVERRIDE_TIMESTAMP`
(timestamp backdating) and `kafka.PARTITION_KEY` (partition targeting) are injectable the same way.

This PoC demonstrates the impact as **cross-topic message injection (CWE-20 โ†’ CWE-74)**: an attacker injects a
forged command into a privileged `account-commands` topic the route was never meant to write to.

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

## Vulnerability Summary

| Property | Value |
|----------|-------|
| **Component** | `camel-kafka` |
| **Affected Class** | `org.apache.camel.component.kafka.KafkaProducer#evaluateTopic` reading `KafkaConstants.OVERRIDE_TOPIC` (`"kafka.OVERRIDE_TOPIC"`) |
| **CWE** | CWE-20 (Improper Input Validation) / CWE-74 (Injection) |
| **Impact** | Redirect / inject a produced record onto an arbitrary Kafka topic (cross-topic injection, integrity) |
| **Preconditions** | A route bridges an HTTP consumer into a `kafka:` 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-23584 (PR [apache/camel#23602](https://github.com/apache/camel/pull/23602)) |
| **Credit** | Yu Bao (PayPal) |

> The fix renames the twelve `kafka.*` header constants to the `CamelKafka*` convention (for example
> `kafka.OVERRIDE_TOPIC` โ†’ `CamelKafkaOverrideTopic`), so they are filtered on the HTTP boundary like every other
> Camel control header. This is a **breaking change** โ€” routes that set these via raw header names must be
> updated (see the 4.x upgrade guide rename table). Same family as CVE-2025-27636, CVE-2026-46454 and
> CVE-2026-49097.

## Why no Kafka broker is needed

The vulnerability is entirely in `KafkaProducer.evaluateTopic()`; the broker is only transport. camel-kafka has a
`KafkaClientFactory` SPI, so this reproducer supplies a factory that returns a Kafka **`MockProducer`** (an
official test double from `kafka-clients`). The **real** camel-kafka producer runs unchanged; the MockProducer's
`history()` records the target topic of every produced record. No broker is contacted.

## The victim route

```java
from("platform-http:/feedback")
    .to("kafka:user-feedback?brokers=broker:9092&kafkaClientFactory=#mockFactory");
```

A "feedback" endpoint pinned to the low-privilege `user-feedback` topic. There is no topic parameter in the HTTP
API โ€” the author assumes the client cannot choose the topic. The attacker sets `kafka.OVERRIDE_TOPIC` and the
record lands on a topic of the attacker's choosing.

## Repository layout

```
CVE-2026-49098/
โ”œโ”€โ”€ pom.xml                 # camel-platform-http + camel-kafka 4.18.2 (kafka-clients 3.9.2)
โ”œโ”€โ”€ Dockerfile
โ”œโ”€โ”€ docker-compose.yml      # single self-contained service
โ”œโ”€โ”€ README.md
โ””โ”€โ”€ src/main/
    โ”œโ”€โ”€ java/com/example/
    โ”‚   โ”œโ”€โ”€ Application.java
    โ”‚   โ”œโ”€โ”€ MockKafkaClientFactory.java  # KafkaClientFactory returning a Kafka MockProducer (no broker)
    โ”‚   โ”œโ”€โ”€ KafkaFactoryConfig.java      # registers it as #mockFactory
    โ”‚   โ”œโ”€โ”€ VictimRoute.java             # platform-http:/feedback -> kafka:user-feedback
    โ”‚   โ””โ”€โ”€ ExploitController.java       # attacker: injects kafka.OVERRIDE_TOPIC=account-commands
    โ””โ”€โ”€ resources/
        โ””โ”€โ”€ application.properties
```

## Prerequisites

- Docker and Docker Compose
- Java 17+ and Maven 3.8+

## Reproduction Steps

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

### Expected output

```
=== CVE-2026-49098 โ€” camel-kafka kafka.OVERRIDE_TOPIC header injection (cross-topic injection) ===

Route intent: POST /feedback -> Kafka topic 'user-feedback' (fixed in the endpoint config)

1) Legitimate POST /feedback (no kafka.OVERRIDE_TOPIC header)
     record published to topic: user-feedback
     value: Great product, thanks!

2) Injected POST /feedback with header 'kafka.OVERRIDE_TOPIC: account-commands'
     record published to topic: account-commands
     value: {"action":"GRANT_ADMIN","account":"attacker@evil.example"}

>>> PROVEN: an inbound HTTP header (kafka.OVERRIDE_TOPIC) passed the Camel HTTP header filter
>>> and overrode the producer's configured topic, injecting an attacker-forged command into a
>>> privileged Kafka topic the route was never meant to write to (cross-topic injection): true
```

## Recommended Fix

Upgrade to **4.14.8 / 4.18.3 / 4.21.0** (CAMEL-23584). After upgrading, routes that set the topic (or timestamp,
partition, key) via a header must use the `CamelKafka*` names โ€” review the 4.x upgrade guide rename table, as this
is a breaking change.

## Mitigation

Until upgrading, strip the camel-kafka control headers from any untrusted ingress before the `kafka:` producer
(for example `removeHeaders("kafka.*")`), and set the topic from a trusted source.

## 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.