GHSA-35mr-4567-66vg: Medium severity pip/dulwich vulnerability
Affected file dulwich/pack.py (Method: Pack.resolveobject)
Description / Summary A High-severity Denial of Service (DoS) vulnerability exists in the Pack.resolveobject method. When resolving an OFSDELTA object, the resolver calculates the base offset using baseoffset = objoffset - deltaoffset.
If a malicious packfile contains an OFSDELTA object where deltaoffset is 0, the calculation objoffset - 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 deltaoffset == 0, the resolver enters an infinite recursive loop, exhausting CPU resources and eventually crashing the process.
Vulnerable Code Breakdown (dulwich/pack.py): python elif objtype == OFSDELTA: deltaoffset = parsepackobjectoffsetat(...) baseoffset = objoffset - deltaoffset # VULNERABILITY: Self-reference if deltaoffset == 0 basetype, basedata = self.resolveobject(...) # 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 (!baseoffset) 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 OFSDELTA entry whose deltaoffset is 0 typeofsdelta = 6 header = bytes([(typeofsdelta << 4) | 0]) ofsbytes = bytes([0x00]) # deltaoffset = 0 body = zlib.compress(b'') raw = header + ofsbytes + 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.resolveobject to reject deltaoffset == 0: python if deltaoffset == 0: raise CorruptPacksFile("OFSDELTA has self-referential deltaoffset=0") 2. Recursion Depth: Implement a depth limit (e.g., MAXDELTADEPTH = 50) to prevent long, non-looping chains of deltas (OFS or REF).
Affected Software
Remediation
Recommended actions to resolve this vulnerability, in priority order.
- Upgrade
Upgrade
pip/dulwichto a version that resolves this vulnerability.Fixed in 1.2.9 - Compensating control
In dulwich/pack.py, update Pack.resolve_object to reject an OFS_DELTA object when delta_offset == 0, raising CorruptPacksFile instead of resolving the object as its own base.
- Compensating control
Implement a maximum delta-chain depth, such as MAX_DELTA_DEPTH = 50, in Pack.resolve_object to prevent excessively long OFS_DELTA or REF_DELTA chains.
Event History
Frequently Asked Questions
What must an attacker provide to trigger the denial of service?
The attacker must cause Dulwich to resolve a malicious packfile containing an OFS_DELTA object with a delta offset of 0. Resolving that object makes it refer to its own offset and recurse indefinitely.
Which operations can reach the vulnerable code path?
Any operation that walks packfiles can trigger the issue, including clone, fetch, cat-file, and internal Pack operations. Systems that process packfiles from untrusted or attacker-controlled repositories are therefore exposed to CPU exhaustion and a possible process crash.
What is the immediate mitigation if updating is not possible?
Avoid processing untrusted packfiles or repositories with Dulwich until an update can be applied. Restricting clone, fetch, and other packfile-walking operations to trusted sources reduces the opportunity for an attacker to supply the malformed OFS_DELTA object.
How can I tell whether a packfile is attempting to exploit this issue?
A triggering packfile contains an OFS_DELTA object whose parsed delta offset is 0. In affected behavior, resolving that object repeatedly resolves the same object offset, causing unbounded recursion, CPU exhaustion, and eventually a crash.