
Lauffähiger Proof-of-Concept-Reproduzierer für CVE-2026-95629, eine Path-Traversal-Schwachstelle in Apache Camel camel-docling, bei der der output-path-Header unverändert an das --output-Argument von docling weitergegeben wird.
--output unverändertLauffähiger Proof-of-Concept-Reproduzierer für die Apache-Camel-Schwachstelle, bei der DoclingProducer, der das externe docling-Tool im CLI-Modus (dem Standard) ausführt, den Wert des Headers CamelDoclingOutputFilePath unverändert an das --output-Flag von docling weitergab: nicht normalisiert, ein absoluter Pfad oder ein Pfad mit ..-Segmenten wurde unverändert akzeptiert, und keine Endpoint-Option konnte ihn einschränken.
| Laufzeitumgebung | Verzeichnis | Stack |
|---|---|---|
| Camel Spring Boot | camel-spring-boot/ | Spring Boot 3.5.13 + camel-docling 4.22.1 |
| Camel Quarkus | camel-quarkus/ | Camel Quarkus 3.39.0 (Quarkus 3.39.0, Camel 4.22.0) |
Docling schreibt das Konvertierungsergebnis — Markdown, HTML, JSON oder Text — in dieses Verzeichnis, benennt die Datei nach der Eingabedatei, und der Producer entfernt anschließend nur sein eigenes temporäres Verzeichnis, sodass die Datei an ihrem Platz verbleibt. Eine Partei, die den Header setzen kann, könnte daher Konvertierungsausgaben in jedem Verzeichnis erzeugen lassen, in das der Camel-Prozess schreiben kann.
cd camel-spring-boot # or: cd camel-quarkus
mvn clean package
docker compose up -d --build
until [ "$(curl -s http://localhost:8080/exploit/ready)" = "Started" ]; do sleep 1; done
curl -s http://localhost:8080/exploit/attack
docker compose down
Auf einem betroffenen Build (identisch auf beiden Runtimes):
1) No header at all - the producer picks its own temporary directory
argv docling received: --to md --ocr-lang en --output /tmp/docling-<uuid> /tmp/docling-<uuid>/docling-<n>.tmp
exchange outcome: ok
2) An ABSOLUTE path of the attacker's choosing
header CamelDoclingOutputFilePath = /tmp/cve-2026-95629-attacker-chosen
argv docling received: ... --output /tmp/cve-2026-95629-attacker-chosen ...
3) A path with .. segments - watch whether they survive into the argv below
header CamelDoclingOutputFilePath = /tmp/cve-2026-95629-attacker-chosen/../cve-2026-95629-escaped
argv docling received: ... --output /tmp/cve-2026-95629-attacker-chosen/../cve-2026-95629-escaped ...
4) CONTROL - asking for the same thing through CamelDoclingCustomArguments instead
REJECTED by Camel: IllegalArgumentException: Custom argument '--output' is not allowed because the
output directory is managed by the producer. Use the CamelDoclingOutputFilePath header or endpoint
configuration instead.
Files now sitting in directories the route never chose:
/tmp/cve-2026-95629-attacker-chosen/document.md : true
/tmp/cve-2026-95629-escaped/document.md : true
>>> PROVEN: ...
Probe 4 ist die Form des Problems. Die durch den Fix für
CVE-2026-40047 hinzugefügte Validierung benutzerdefinierter Argumente lehnt --output und -o in
CamelDoclingCustomArguments ab und weist Aufrufer an, das Ausgabeverzeichnis stattdessen mit diesem Header zu wählen —
somit war der eine Wert, der entscheidet, wohin docling schreibt, der eine Wert, der die Kommandozeile erreichte, ohne dass
die auf benutzerdefinierte Argumente angewandten Prüfungen griffen.
Der Exchange selbst schlägt nach den Probes 2 und 3 fehl („failed afterwards"), weil der Producer dann die konvertierte Ausgabe in seinem eigenen temporären Verzeichnis sucht und sie dort nicht findet. Das ist kosmetisch: Der Schreibvorgang ist bereits erfolgt, was die Dateiauflistung am Ende zeigt.
Keine docling-Installation erforderlich. Die Option doclingCommand des Endpoints verweist auf einen aufzeichnenden Stellvertreter für
die CLI, der sowohl den ihm übergebenen Argumentvektor aufzeichnet als auch eine Datei in das von --output benannte Verzeichnis schreibt, so wie es docling tut. Der echte DoclingProducer und die echte addOutputDirectoryArguments() laufen.
Nur der CLI-Modus verwendet diesen Header; der docling-serve-Modus nicht.
CamelDoclingOutputFilePath gehört zum Camel-Header-Namespace, den Consumer, die Camels
Standard-Header-Filterung anwenden — etwa die JMS- und Kafka-Consumer —, aus eingehenden Nachrichten entfernen. Das
Problem wird dort erreicht, wo eine Route extern beeinflusste Werte in den Header weiterleitet oder wo ein Consumer
eingehende Header ohne diese Filterung abbildet.
Der Inhalt der geschriebenen Datei ist die Konvertierung des in der Nachricht enthaltenen Dokuments, wird also von demjenigen geprägt, der dieses Dokument liefert, ohne bytegenau geschrieben zu werden; wenn das Dokument im Nachrichtenkörper ankommt, speichert der Producer es unter einem zufälligen temporären Namen, den die Ausgabedatei dann trägt.
| Eigenschaft | Wert |
|---|---|
| Komponente | camel-docling (DoclingProducer.addOutputDirectoryArguments()) |
| CWE | CWE-22 (Improper Limitation of a Pathname to a Restricted Directory) |
| Schweregrad | Mittel |
| Angriffsvektor | Ein absolutes oder ..-enthaltendes Verzeichnis im Header CamelDoclingOutputFilePath |
| Auswirkung | Konvertierungsausgabe in einem beliebigen Verzeichnis erstellt, in das der Camel-Prozess schreiben kann |
| Betroffene Versionen | Ab 4.15.0 vor 4.18.5, ab 4.19.0 vor 4.22.2 |
| Behobene Versionen | 4.18.5, 4.22.2, 4.23.0 |
| JIRA | CAMEL-24894 |
| Danksagung | Zhixi „Jace" Sun, unabhängiger Sicherheitsforscher |
Advisory: https://camel.apache.org/security/CVE-2026-95629.html
Verwandtes Problem in derselben Komponente, dieselbe Release-Welle: CVE-2026-95628 (absolute Pfade werden als Werte benutzerdefinierter Argumente akzeptiert).
Der Header erhält nun dieselbe Normalisierung und optionale Eingrenzung auf ein Basisverzeichnis wie Eingabepfade, über
resolveWithinOutputBaseDirectory(). Aber die vom Fix hinzugefügte Endpoint-Option outputBaseDirectory ist
standardmäßig nicht gesetzt, und ihre eigene Dokumentation besagt, dass bei leerem Wert keine Verzeichnisbeschränkung angewendet
und der Header-Wert nur normalisiert wird.
Verifiziert gegen 4.22.1 → 4.22.2, mit nicht gesetztem outputBaseDirectory:
3) A path with .. segments - watch whether they survive into the argv below
argv docling received: ... --output /tmp/cve-2026-95629-escaped ... <- normalized
Files now sitting in directories the route never chose:
/tmp/cve-2026-95629-attacker-chosen/document.md : true <- still written
/tmp/cve-2026-95629-escaped/document.md : true
>>> PARTLY CLOSED: the header was normalized before reaching --output ... It can still name any directory
>>> the Camel process can write to, because the outputBaseDirectory option the fix added is UNSET by
>>> default ... Set outputBaseDirectory on every docling endpoint whose CamelDoclingOutputFilePath header
>>> can carry a value influenced from outside the deployment; upgrading alone is not enough.
Der Treiber meldet diesen Zustand separat, anstatt ihn als behoben zu bezeichnen, da dies der Zustand ist, in dem die meisten
Deployments direkt nach einem Upgrade landen werden. Setzen Sie nach dem Upgrade outputBaseDirectory auf jedem
docling-Endpunkt, dessen Header einen von außen beeinflussbaren Wert tragen kann.