RCE par métadonnées PDF Gotenberg, exploitée dans la nature (CVE-2026-42589)
Un saut de ligne dans une clé de métadonnées PDF exécute du Perl sur les Gotenberg non authentifiés avant 8.31.0 ; faille exploitée par sept groupes.
En bref
- Une faille de Gotenberg avant 8.31.0 permet à un attaquant non authentifié d'exécuter des commandes système via le endpoint de métadonnées PDF. Un saut de ligne dans une clé JSON de métadonnées coupe l'entrée d'ExifTool en une nouvelle ligne d'arguments, et le drapeau
-ifinjecté évalue des expressions Perl. - Gotenberg est une API Docker de conversion PDF dépassant 101 millions de pulls Docker Hub. Toute instance avant 8.31.0 avec le port 3000 joignable est exposée, car le endpoint n'exige aucune authentification.
- VulnCheck a enregistré 1 358 événements d'exploitation depuis 128 adresses IP en 30 jours, dans au moins sept groupes d'attaquants. L'avis GitHub note la faille Critical CVSS v3.1 9,8, et le catalogue KEV de la CISA ne la liste pas au 2026-10-30.
- Passez à Gotenberg 8.31.0 ou plus et recréez le conteneur. Le correctif est publié depuis avril 2026. Voir « Comment corriger ? » pour ce que 8.31.0 change d'autre, et « Comment vérifier si j'ai été compromis ? » pour les indicateurs de VulnCheck.
Suis-je concerné ?
Vous êtes exposé quand toutes ces conditions sont réunies :
- Votre instance exécute Gotenberg avant 8.31.0, publié le 2026-04-17.
- Le port d'API, 3000 par défaut, est joignable par l'attaquant. L'avis GitHub note que c'est courant dans les déploiements internes et cloud.
- Le endpoint
/forms/pdfengines/metadata/writeest activé. Il fait partie du module pdfengines et aucune authentification ne le protège.
| Branche | Versions affectées | Version corrigée |
|---|---|---|
| Enregistrement CVE-2026-42589 (cve.org) | Avant 8.31.0 | 8.31.0 (2026-04-17) |
Le tag Docker Hub gotenberg/gotenberg:8 visait 8.29.1 lors du signalement de la faille début avril 2026, donc les instances tirant le tag majeur à ce moment étaient exposées.
Vérifiez ce qui tourne réellement, pas ce que vous avez tiré en dernier :
# Running instance: the API reports its own version
curl -s http://your-gotenberg-host:3000/version
# Container: which image the running container uses
docker inspect <container> --format '{{.Config.Image}}'
# Image tag pinned in the deployment file
grep -R "gotenberg/gotenberg:" docker-compose.yml k8s/ 2>/dev/null
Comment corriger ?
- Pointez la référence d'image vers 8.31.0 ou plus, par exemple
gotenberg/gotenberg:8.31.0ou le tag8courant. - Recréez le conteneur, ne vous contentez pas de tirer l'image :
docker compose up -d --force-recreate gotenberg, ou le déploiement équivalent sous Kubernetes. - Vérifiez le résultat avec
curl -s http://your-gotenberg-host:3000/version. - Restreignez le port 3000 aux appelants qui en ont besoin. Le endpoint n'a jamais exigé d'authentification, donc le placement réseau reste le contrôle durable.
Sachez ce que 8.31.0 change d'autre avant de sauter des versions mineures. La version durcit aussi les URL sortantes en rejetant loopback, RFC1918 et autres adresses non publiques, donne à --webhook-deny-list des valeurs par défaut bloquant les plages internes, et cesse de publier les images thecodingmachine/gotenberg. Les flux qui récupèrent des actifs internes ou appellent des webhooks internes peuvent nécessiter des options après la mise à jour.
Aucune solution de contournement de configuration n'existe pour la faille de métadonnées elle-même. Le chemin de code vulnérable s'exécute avant toute option.
Quelle est la cause racine ?
Gotenberg transmet à ExifTool les clés de métadonnées fournies par l'appelant sans rejeter les caractères de contrôle : un saut de ligne dans une clé brise le protocole stdin d'ExifTool, ligne par ligne, en lignes d'arguments choisies par l'attaquant.
Gotenberg convertit des documents en PDF via Chromium headless et LibreOffice. Son module pdfengines expose /forms/pdfengines/metadata/write, qui accepte un fichier PDF et un objet JSON de métadonnées. Le gestionnaire remet chaque clé et valeur à ExifTool via la bibliothèque go-exiftool.
La bibliothèque go-exiftool écrit chaque paire comme une ligne du stdin d'ExifTool :
fmt.Fprintln(e.stdin, "-"+k+"="+str)
L'appel fmt.Fprintln ajoute un saut de ligne après chaque écriture. Quand la clé JSON de l'appelant contient elle-même \n, le json.Unmarshal de Go le conserve comme saut de ligne littéral dans la clé de la map, et l'écriture se coupe en plusieurs lignes. ExifTool lit chaque ligne comme un argument distinct. Une clé Title\n-if\nsystem('cmd')||1\n-Comment devient la séquence d'arguments -Title, -if, system('cmd')||1, -Comment=x.
Le drapeau -if d'ExifTool évalue son argument comme une expression Perl. system('cmd') exécute cmd comme le processus Gotenberg. L'avis crédite S-Senhaji du signalement.
Le module savait déjà que certaines clés étaient dangereuses. Une liste dangerousTags supprimait les clés FileName et Directory, mais elle employait la correspondance exacte et ne retirait ni ne rejetait les caractères de contrôle : l'injection par saut de ligne passait donc sans encombre.
Comment la faille est-elle exploitée ?
La preuve de concept de l'avis tient en une requête HTTP non authentifiée. Elle écrit la sortie de id dans un fichier du serveur :
curl -s -o /dev/null -w "HTTP:%{http_code}" \
-X POST http://TARGET:3000/forms/pdfengines/metadata/write \
-F 'files=@sample.pdf;type=application/pdf' \
--form-string $'metadata={"Title\\n-if\\nsystem(\'id>/tmp/pwned\')||1\\n-Comment": "x"}'
# → HTTP:200
# On server: uid=1001(gotenberg) gid=1001(gotenberg) groups=1001(gotenberg),0(root)
- L'attaquant envoie en POST un PDF valide et un objet JSON de métadonnées dont la clé contient des sauts de ligne intégrés.
- Gotenberg désérialise le JSON ; la clé garde ses sauts de ligne littéraux.
- go-exiftool écrit la clé dans le stdin d'ExifTool, la coupant en lignes d'arguments.
- ExifTool traite
-if system('cmd')||1comme une expression Perl et exécutecmd. - Le serveur répond HTTP 200 avec un PDF valide, donc la requête ressemble à une conversion normale pour la supervision.
La variante d'exfiltration de l'avis encode la sortie de commande en base64 et l'envoie vers un écouteur HTTP hors bande, ce qui maintient l'échange hors des journaux du serveur.
La télémétrie d'octobre de VulnCheck montre ce qu'en font de vrais opérateurs. Un groupe a testé l'exécution avec id, relu la sortie dans les métadonnées Subject du PDF de réponse via l'opérateur -Subject<= d'ExifTool, puis téléchargé un reverse shell Go nommé cache_base depuis trois miroirs indépendants. Le repli Node.js compte : les conteneurs Gotenberg embarquent Node.js pour Chromium headless, donc l'attaquant garde un téléchargeur même sans wget ni curl.
Quel est le flux de l'attaque ?
En une phrase : l'attaquant envoie une clé de métadonnées contenant des sauts de ligne, la clé se coupe en lignes d'arguments sur le stdin d'ExifTool, le drapeau -if injecté évalue un appel Perl system, et le serveur exécute la commande en répondant un PDF d'apparence normale.
contenant \n"] --> B["json.Unmarshal garde
le saut de ligne"] B --> C["go-exiftool écrit la clé
dans le stdin d'ExifTool"] C --> D["Le saut de ligne coupe la ligne
en nouvelles lignes d'arguments"] D --> E["-if évalue
system('cmd')||1
comme du Perl"] E --> F["La commande tourne comme le
processus Gotenberg"] F --> G["HTTP 200 avec un PDF valide :
aucun signal d'erreur"]
Que peut faire un attaquant ?
L'attaquant exécute des commandes système comme le processus Gotenberg. La sortie de l'avis montre un processus uid=1001(gotenberg) gid=1001(gotenberg) groups=1001(gotenberg),0(root) : un utilisateur sans privilège, avec le groupe root. Le vecteur CVSS est CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H, noté 9,8.
Les conditions sont minimes : accès réseau au port 3000, pas d'identifiants, pas d'interaction. La réponse est une conversion réussie, donc rien dans l'échange HTTP ne signale l'attaque.
La suite dépend de l'opérateur. VulnCheck attribue l'activité observée à au moins sept groupes aux objectifs différents : balayage de masse, cryptominage, reverse shells Python, et le reverse shell Go cache_base. The Hunter's Ledger, cité par VulnCheck, a rapporté une campagne de cryptominage ayant confirmé environ 200 instances Gotenberg exploitables et installé XMRig, sous le nom polkitd, sur environ 150 d'entre elles.
Pourquoi le correctif fonctionne-t-il ?
Le correctif tient en trois commits de la version 8.31.0, tous dans le même module exiftool. Le premier rejette toute clé qui n'est pas un nom de tag simple :
// safeKeyPattern matches legitimate ExifTool tag names: alphanumeric,
// hyphens, underscores, colons, and periods. Rejects control characters
// (especially \n) that would inject stdin arguments via go-exiftool's
// line-based protocol.
var safeKeyPattern = regexp.MustCompile(`^[a-zA-Z0-9\-_.:]+$`)
Chaque clé de l'objet de métadonnées doit désormais correspondre à safeKeyPattern avant d'atteindre go-exiftool. Un saut de ligne, un retour chariot ou tout autre caractère de contrôle échappe à la correspondance et la requête est rejetée en HTTP 400.
Le deuxième commit étend le même contrôle aux valeurs :
func validateMetadataValue(key, value string) error {
if strings.ContainsAny(value, "\n\r\x00") {
return fmt.Errorf("write PDF metadata with ExifTool: invalid metadata value for key %q (contains control character): %w", key, gotenberg.ErrPdfEngineMetadataValueNotSupported)
}
return nil
}
Le troisième retire les préfixes de groupe ExifTool comme System: avant la vérification des tags dangereux, de sorte que System:FileName ne la contourne plus, et ajoute FilePermissions à cette liste de blocage. Ensemble, les trois ferment le chemin d'injection d'arguments dans les clés et les valeurs, ce que les notes de version décrivent comme bloquant la contrebande d'arguments et l'abus de pseudo-tags de système de fichiers.
Comment vérifier si j'ai été compromis ?
VulnCheck publie des indicateurs pour le groupe cache_base. Vérifiez-les tous :
- Indicateurs réseau : contact avec
210.170.22.208(Japon résidentiel, source d'attaque observée),39.105.65.32(serveur de charge Gitea, Alibaba Cloud Pékin),132.145.123.143(hôte de charge, Oracle Cloud Tokyo),5.161.100.63(C2 d'un second implant, Hetzner), et les domainesubuntu-repo.strangled[.]net,linux-x86-tcpudp.strangled[.]net,cheeva.strangled[.]net. - Traces de requêtes : requêtes POST vers
/forms/pdfengines/metadata/writedont le JSON de métadonnées contient des sauts de ligne intégrés. Les charges observées employaient des clés commençant parTitle\n-if\net le fichier temporaire/tmp/nx_cmd.txt. - Traces sur l'hôte : un fichier verrou masqué nommé
.cached_db.lock, l'implantcache_base, et la variable d'environnementPYTHONUNBUFFERED=1posée par l'implant Go. - Empreintes d'implant :
cache_baseMD5f5a756d13697cdfa69ed795d4468339d,cache_arm64MD5c31cd755293844f8ac0802d2c438e701,cache_mipsMD52b85605660343399a650b8c3745bccb0. - Indicateurs d'autres groupes : XMRig tournant sous le nom de processus
polkitd, et téléchargements de4thepool_miner.sh,banner_homepage_800x800.jpgdepuis un bucket S3, ou de charges depuis185.132.53.158:81/anonymus/client.
Un hit sur le seul endpoint de métadonnées est une piste, pas une preuve : des clients légitimes écrivent aussi des métadonnées PDF. Le saut de ligne dans la clé, le fichier temporaire et le fichier verrou sont les signaux discriminants.
Chronologie
- 2026-04-06 au 2026-04-13 : Le mainteneur pose les trois commits de durcissement ExifTool sur la branche 8.31.0.
- 2026-04-17 : Gotenberg 8.31.0 sort avec l'assainissement des métadonnées et le durcissement SSRF.
- 2026-04-30 : L'avis GitHub GHSA-rqgh-gxv4-6657 est publié, avec une preuve de concept fonctionnelle.
- 2026-05-14 : L'enregistrement CVE-2026-42589 est publié avec la plage affectée.
- 2026-06-26 : VulnCheck ajoute la faille à son propre catalogue de type KEV. La CISA ne l'a pas ajoutée au 2026-10-30.
- 2026-08-31 : Un groupe XMRig frappe la faille. Le plus ancien sondage observé par VulnCheck, depuis des adresses Telecom Italia en rotation, avait couru de fin juin à début août.
- 2026-10-19 au 2026-10-24 : Le balayeur Contabo
5.189.138.115génère 364 événements, plus d'un quart du total sur 30 jours. - 2026-10-26 : VulnCheck publie son analyse de l'exploitation et de l'implant
cache_base.
FAQ
Pourquoi aucun scanner n'a-t-il détecté la faille avant l'exploitation ?
La faille a un CVE et un avis GitHub, mais un scanner ne signale que ce qui est inventorié. Une image Docker n'est pas une dépendance de paquet : les scanners de dépendances ne la voient pas, et le conteneur continue de convertir des PDFs normalement. Le endpoint de version est la vérification fiable.
La faille est-elle dans ExifTool ou dans Gotenberg ?
L'avis la place dans Gotenberg. Le comportement -if d'ExifTool est documenté, et le protocole ligne à ligne de go-exiftool est le design de la bibliothèque. Gotenberg contrôle ce qui entre dans ce protocole, et la liste noire qu'il portait déjà montre que les mainteneurs acceptaient cette responsabilité ; le correctif est entièrement dans le code de Gotenberg.
Que fait l'implant cache_base ?
C'est un binaire Go épuré de 5,2 Mo qui prend un verrou, interroge ipinfo.io pour localiser la victime, se connecte à un des deux C2 et relie un shell entre la connexion et /bin/sh. Il n'a pas de persistance propre, mais sa boîte à outils contient un chien de garde qui le relance.
Mon instance est interne. Suis-je quand même une cible ?
Les déploiements internes et cloud laissent souvent le port 3000 joignable, comme le note l'avis, et une seule fenêtre de 30 jours a compté 1 358 événements depuis 128 adresses. Un placement réseau interne réduit qui peut atteindre le port ; il ne retire pas la faille. Corrigez malgré tout.
Dois-je faire des rotations après correction ?
Si les vérifications de compromission correspondent, traitez l'hôte comme compromis : révoquez tout ce que le processus Gotenberg pouvait lire, y compris les identifiants de service et les clés d'API atteignables depuis son environnement, et redéployez depuis l'image corrigée plutôt que de nettoyer sur place.
Trouvez les serveurs Gotenberg exposés avec Ostorlab
La découverte de surface d'attaque d'Ostorlab inventorie vos hôtes et services exposés sur Internet, pour repérer ceux qui exposent une API de conversion de documents et vérifier leurs versions avant un attaquant.
Références
- Avis GitHub GHSA-rqgh-gxv4-6657, CVE-2026-42589, CVSS v3.1 9,8, publié le 2026-04-30, avec la cause racine et la preuve de concept : https://github.com/gotenberg/gotenberg/security/advisories/GHSA-rqgh-gxv4-6657 (consulté le 30 octobre 2026)
- Enregistrement CVE-2026-42589, versions affectées avant 8.31.0, publié le 2026-05-14 : https://www.cve.org/CVERecord?id=CVE-2026-42589 (consulté le 30 octobre 2026)
- VulnCheck, « Cache Me If You Can: Unpacking a Go Implant and Its Toolkit », Jacob Baines, 26 octobre 2026, les comptages d'exploitation, les sept groupes et les indicateurs : https://www.vulncheck.com/blog/cache-me-if-you-can (consulté le 30 octobre 2026)
- Notes de version Gotenberg 8.31.0, publiées le 2026-04-17, l'assainissement ExifTool et les changements cassants : https://github.com/gotenberg/gotenberg/releases/tag/v8.31.0 (consulté le 30 octobre 2026)
- Commit correctif 405f1069c026, « fix(exitool) [sic]: prevent control characters », 2026-04-06 : https://github.com/gotenberg/gotenberg/commit/405f1069c026 (consulté le 30 octobre 2026)
- Commit correctif 55d19522a816, « fix(exiftool): prevent line breaks », 2026-04-11 : https://github.com/gotenberg/gotenberg/commit/55d19522a816 (consulté le 30 octobre 2026)
- Commit correctif 46e190970fed, « fix(exiftool): remove System: prefixes », 2026-04-13 : https://github.com/gotenberg/gotenberg/commit/46e190970fed (consulté le 30 octobre 2026)
- Dépôt Gotenberg, 13 478 étoiles au 30 octobre 2026 : https://github.com/gotenberg/gotenberg (consulté le 30 octobre 2026)
- Gotenberg sur Docker Hub, 101 471 977 pulls au 30 octobre 2026 : https://hub.docker.com/r/gotenberg/gotenberg (consulté le 30 octobre 2026)
- The Hunter's Ledger, la campagne XMRig contre environ 200 instances Gotenberg exploitables, citée par VulnCheck : https://the-hunters-ledger.com/reports/gotenberg-rce-cryptomining-107-175-69-137/ (consulté le 30 octobre 2026)
- Flux JSON du catalogue KEV de la CISA, vérifié pour CVE-2026-42589 le 30 octobre 2026 (absent) : https://www.cisa.gov/sites/default/files/feeds/known_exploited_vulnerabilities.json (consulté le 30 octobre 2026)