Severity by source
AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:L
Primary rating from GitHub Advisory · only source for this CVE.
CVSS VectorGitHub Advisory
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:L
Lifecycle Timeline
3DescriptionGitHub Advisory
Summary
Gotenberg blocks certain ExifTool tag names like FileName and Directory to stop attackers from renaming or moving files on the server. But ExifTool allows a longer form of the same tag - System:FileName - which does the exact same thing. Gotenberg only checks if the tag is exactly FileName, so System:FileName slips right through and ExifTool happily renames the file. No login is needed. One HTTP request is enough.
This bypasses the fix from GHSA-qmwh-9m9c-h36m.
Details
Think of it like a nightclub bouncer with a blocklist of banned names. The blocklist says "Block anyone named John." A person shows up and says "I'm Mr. John." The bouncer checks - "Mr. John" is not "John" - so he lets them in. But inside the club, everyone knows Mr. John IS John.
That's exactly what happens here:
The blocklist (exiftool.go line 275-280) blocks these tag names:
FileName
Directory
HardLink
SymLinkThe check (exiftool.go line 295-301) compares what the user sent against this list:
if strings.EqualFold(key, tag) { // is "System:FileName" equal to "FileName"?
delete(metadata, key) // no - so it's NOT deleted
}System:FileName is not equal to FileName (one is 16 characters, the other is 8), so it passes through.
But ExifTool treats them as the same thing. In ExifTool, System: is just a group prefix - like a folder name before the tag. System:FileName and FileName both mean "rename this file." The ExifTool docs say: *"A tag name may include leading group names separated by colons."*
Why the colon is allowed: The key validation regex (exiftool.go line 31) explicitly permits colons:
var safeKeyPattern = regexp.MustCompile(`^[a-zA-Z0-9\-_.:]+$`)
// ^ colon is allowedSo the full chain is:
- Attacker sends
System:FileName→ passes the regex (colon is allowed) System:FileName→ passes the blocklist (it's not equal toFileName)- ExifTool receives
System:FileName→ treats it asFileName→ renames the file
Bonus finding: The FilePermissions tag is not in the blocklist at all. Sending {"FilePermissions": "rwxrwxrwx"} tells ExifTool to chmod the file, and nothing stops it.
PoC
Setup - start Gotenberg with default settings:
docker run -d --name gotenberg-poc -p 3000:3000 gotenberg/gotenberg:8Create a folder inside the container where we'll move the file to:
docker exec gotenberg-poc mkdir -p /tmp/evilSend the attack - one curl command:
curl -X POST http://localhost:3000/forms/pdfengines/metadata/write \
-F 'files=@any-pdf-file.pdf' \
-F 'metadata={"System:FileName":"stolen.pdf","System:Directory":"/tmp/evil"}'This returns HTTP 404 because the file got moved before the server could return it.
Check that the file actually moved:
docker exec gotenberg-poc ls -la /tmp/evil/Result:
-rw-r--r-- 1 gotenberg gotenberg 17789 Apr 13 07:40 stolen.pdfThe file is sitting in /tmp/evil/stolen.pdf. It was renamed from its random UUID name to stolen.pdf and moved out of the temporary directory - exactly what the blocklist was supposed to prevent.
Proof that the existing blocklist works for bare names (control test):
curl -X POST http://localhost:3000/forms/pdfengines/metadata/write \
-F 'files=@any-pdf-file.pdf' \
-F 'metadata={"FileName":"stolen.pdf","Directory":"/tmp/evil"}'This returns HTTP 500 - the bare FileName tag was correctly blocked. Only the System:FileName variant gets through.
Other ways to exploit the same bug:
system:filename(lowercase) - also works because ExifTool is case-insensitivesystem:directory- moves the file to any writable folderFilePermissions- changes the file's permissions (this tag is simply missing from the blocklist entirely)
Every endpoint that accepts the metadata field is affected, including /forms/chromium/convert/html, /forms/libreoffice/convert, /forms/pdfengines/merge, and all other conversion routes.
Impact
Any person who can send HTTP requests to Gotenberg (no login needed by default) can:
- Move files anywhere inside the container by using
System:Directory - Rename files to anything by using
System:FileName - Change file permissions by using
FilePermissions(this tag is not blocked at all) - Break the service for other users - when a file gets moved mid-request, the server returns 404 errors
In real-world deployments where Gotenberg shares a Docker volume with other services (which is common), an attacker can drop a PDF file with controlled content into that shared folder - potentially affecting whatever service reads files from there.
AnalysisAI
Remote unauthenticated attackers can bypass ExifTool tag blocklist in Gotenberg 8.x via group-prefixed tag names (e.g., 'System:FileName' instead of 'FileName'), enabling arbitrary file renaming, relocation, and permission modification within the container filesystem. One HTTP request exploits this input validation bypass (CWE-20) to circumvent protections from a prior security fix (GHSA-qmwh-9m9c-h36m). The vulnerability affects all metadata-accepting endpoints in Gotenberg's default configuration, which typically runs without authentication. No public exploit code is confirmed, but a detailed proof-of-concept is published in the GitHub advisory (GHSA-62p3-hvxx-fxg4). CVSS 8.2 reflects network vector with no authentication required, though real-world impact depends on container isolation and shared volume configurations.
Technical ContextAI
Gotenberg is a Docker-powered stateless API for converting HTML/Office documents to PDF, written in Go (github.com/gotenberg/gotenberg/v8). It integrates ExifTool for metadata manipulation. The vulnerability lies in exiftool.go's validation logic: the blocklist uses exact string comparison (strings.EqualFold) against bare tag names like 'FileName', 'Directory', 'HardLink', and 'SymLink', but ExifTool's tag specification allows group prefixes (e.g., 'System:FileName') where the group name and tag are separated by a colon. Gotenberg's regex validator explicitly permits colons in tag names (^[a-zA-Z0-9\-_.:]+$), allowing prefixed variants to pass validation. Since ExifTool internally normalizes 'System:FileName' to 'FileName' at execution time, attackers supply semantically equivalent tags that bypass blocklist checks but trigger dangerous filesystem operations. The root cause is CWE-20 (Improper Input Validation) - the filter operates on syntactic form rather than semantic meaning, creating a classic evasion scenario analogous to SQL injection's use of encoding bypasses. Additionally, 'FilePermissions' is entirely absent from the blocklist, enabling chmod operations without evasion techniques.
RemediationAI
No vendor-released patched version is confirmed in available data - the advisory lists 'fixed in: None' as of analysis time. Monitor https://github.com/gotenberg/gotenberg/security/advisories/GHSA-62p3-hvxx-fxg4 for patch announcements. Until a fix is available, apply these compensating controls: (1) Disable all metadata-accepting endpoints at the reverse proxy or API gateway level if metadata writing is not required - this blocks all attack vectors but sacrifices metadata functionality. (2) Implement strict authentication and authorization before Gotenberg endpoints to limit attacker pool (default configuration has no authentication) - reduces risk from 'anyone on the network' to 'authenticated users only' but does not prevent attacks by malicious insiders or compromised credentials. (3) Run Gotenberg in isolated containers with read-only root filesystems where feasible, mounting only necessary directories as writable with minimal permissions - limits file manipulation scope but may interfere with legitimate ExifTool operations depending on workload. (4) Deploy network segmentation to restrict Gotenberg access to trusted internal services only - prevents public internet exploitation but does not defend against lateral movement post-compromise. (5) Monitor filesystem activity in Gotenberg containers for unexpected file renames, moves to non-standard paths, or permission changes using auditd or container security tools - provides detection but not prevention. Each mitigation trades functionality or operational complexity for security; combination of controls 2, 3, and 4 offers best risk reduction until patching.
An issue was discovered in Appsmith before 1.52. Rated critical severity (CVSS 9.8), this vulnerability is remotely expl
runc through version 1.0-rc6 (used in Docker before 18.09.2) contains a container escape vulnerability that allows attac
Netmaker makes networks with WireGuard. Rated high severity (CVSS 7.5), this vulnerability is remotely exploitable, no a
Unauthenticated remote code execution in Marimo ≤0.20.4 allows attackers to execute arbitrary system commands via the `/
The News & Blog Designer Pack - WordPress Blog Plugin - (Blog Post Grid, Blog Post Slider, Blog Post Carousel, Blog Post
Docker 1.3.2 allows remote attackers to execute arbitrary code with root privileges via a crafted (1) image or (2) build
Remote code execution in NocoBase Workflow Script Node (npm @nocobase/plugin-workflow-javascript) allows authenticated l
Docker Desktop Community Edition before 2.1.0.1 allows local users to gain privileges by placing a Trojan horse docker-c
Vasion Print (formerly PrinterLogic) Virtual Appliance Host prior to version 25.2.169 and Application prior to version 2
An issue in Plone Docker Official Image 5.2.13 (5221) open-source software that could allow for remote code execution du
Tandoor Recipes is an application for managing recipes, planning meals, and building shopping lists. Rated critical seve
Unauthenticated remote code execution in 9router (npm package) versions 0.4.30 through 0.4.36 allows network-adjacent at
Same weakness CWE-20 – Improper Input Validation
View allSame technique Information Disclosure
View allShare
External POC / Exploit Code
Leaving vuln.today
EUVD-2026-30307
GHSA-62p3-hvxx-fxg4