cbcvebase.
CVE-2026-44829
published 2026-05-29

CVE-2026-44829: Gotenberg has path traversal in zip entry name via Windows-style separators in upload filename ### Summary `filepath.Base` on the Linux container does not…

high
Gotenberg has path traversal in zip entry name via Windows-style separators in upload filename

### Summary
`filepath.Base` on the Linux container does not strip backslashes (`\`), because `\` is only a path separator on Windows. A multipart filename like `..\..\..\..\Windows\System32\evil.pdf` survives Gotenberg's input sanitisation and lands verbatim as the zip entry name when a multi-output route returns its result as a zip (e.g. `/forms/pdfengines/split`). Windows zip extractors interpret `\` as a path separator and write the file outside the extraction directory.

### Details
`pkg/modules/api/context.go:434, 472`:
```go
filename := norm.NFC.String(filepath.Base(fh.Filename))
```
On Linux, `filepath.Base("..\\..\\..\\..\\Windows\\System32\\evil.pdf")` returns the same string verbatim — there are no `/` separators to find. The original filename then flows to `ctx.diskToOriginal` (`pkg/modules/api/context.go:459, 393`) and through `pkg/modules/pdfengines/routes.go:287-322` (`SplitPdfStub`), which builds:
```go
originalNameNoExt := strings.TrimSuffix(originalName, filepath.Ext(originalName))
newOriginal := fmt.Sprintf("%s_%d.pdf", originalNameNoExt, i)
ctx.RegisterDiskPath(newPath, newOriginal)
```
Finally `pkg/modules/api/context.go:617-642` constructs the zip via `archives.FilesFromDisk` + `archives.Zip{}.Archive`. `mholt/[email protected]/archives.go:155-184` (`nameOnDiskToNameInArchive`) returns `path.Join(rootInArchive, "")` — the map value verbatim.

### Suggested fix
```diff
- filename := norm.NFC.String(filepath.Base(fh.Filename))
+ filename := sanitizeFilename(fh.Filename)
+
+ func sanitizeFilename(name string) string {
+ if i := strings.LastIndexAny(name, "/\\"); i >= 0 {
+ name = name[i+1:]
+ }
+ name = norm.NFC.String(name)
+ // Optional belt-and-braces:
+ name = strings.ReplaceAll(name, "..", "_")
+ name = strings.Map(func(r rune) rune {
+ if r /dev/null
docker run -d --rm --name gotenberg-audit -p 3000:3000 gotenberg/gotenberg:8.32.0
i=0; until [ "$(cur

Affected

1 ranges
VendorProductVersion rangeFixed in
github.comgotenberg_gotenberg_v8>= 0 < 8.33.08.33.0
Stop checking back — get the weekly exploitation signal.

Every Monday: what got weaponized or added to CISA KEV in the last seven days — each CVE cross-linked to its PoC, Nuclei template, and detection rule. Free, one email a week, unsubscribe in one click.