CVE-2026-52726
Dulwich's submodule path traversal in porcelain.submodule_update / porcelain.clone(recurse_submodules=True) yields RCE via attacker-dropped .git/hooks payload
Dulwich is a pure-Python implementation of the Git file formats and protocols. Starting in version 0.23.2 and prior to version 1.2.5, dulwich.porcelain.submodule_update, and by extension porcelain.clone(..., recurse_submodules=True), materializes attacker-controlled submodule paths from a crafted upstream repository without path validation. A malicious .gitmodules plus a matching tree gitlink whose path is .git/hooks (or any other directory inside the parent repository's .git directory) causes the attacker's submodule tree contents to be written directly into the victim's .git/hooks/ directory, preserving executable mode bits. The dropped executables are then run by any subsequent git or dulwich command that invokes the matching hook, resulting in arbitrary code execution. This is the dulwich equivalent of the upstream Git fixes for CVE-2024-32002 / CVE-2024-32004, which were never propagated into dulwich's separately implemented submodule porcelain. Version 1.2.5 patches the issue.
Attack profile
How easy it is to exploit
- Attack vector
- Network
- Complexity
- Low
- Privileges needed
- None
- User interaction
- None
What an attacker can affect
- Confidentiality
- None
- Integrity
- None
- Availability
- High
Exploit likelihood
What each source reports
CVE List Score used
7.5
Dulwich's submodule path traversal in porcelain.submodule_update / porcelain.clone(recurse_submodules=True) yields RCE via attacker-dropped .git/hooks payload
Dulwich is a pure-Python implementation of the Git file formats and protocols. Starting in version 0.23.2 and prior to version 1.2.5, dulwich.porcelain.submodule_update, and by extension porcelain.clone(..., recurse_submodules=True), materializes attacker-controlled submodule paths from a crafted upstream repository without path validation. A malicious .gitmodules plus a matching tree gitlink whose path is .git/hooks (or any other directory inside the parent repository's .git directory) causes the attacker's submodule tree contents to be written directly into the victim's .git/hooks/ directory, preserving executable mode bits. The dropped executables are then run by any subsequent git or dulwich command that invokes the matching hook, resulting in arbitrary code execution. This is the dulwich equivalent of the upstream Git fixes for CVE-2024-32002 / CVE-2024-32004, which were never propagated into dulwich's separately implemented submodule porcelain. Version 1.2.5 patches the issue.
- Record
CVE-2026-52726- CVSS 3.1
- 7.5 High
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H - Weakness
- CWE-22
- Updated
- 2026-11-02
EUVDEUVD-2026-36195
7.5
Dulwich is a pure-Python implementation of the Git file formats and protocols. Starting in version 0.23.2 and prior to version 1.2.5, dulwich.porcelain.submodule_update, and by extension porcelain.clone(..., recurse_submodules=True), materializes attacker-controlled submodule paths from a crafted upstream repository without path validation. A malicious .gitmodules plus a matching tree gitlink whose path is .git/hooks (or any other directory inside the parent repository's .git directory) causes the attacker's submodule tree contents to be written directly into the victim's .git/hooks/ directory, preserving executable mode bits. The dropped executables are then run by any subsequent git or dulwich command that invokes the matching hook, resulting in arbitrary code execution. This is the dulwich equivalent of the upstream Git fixes for CVE-2024-32002 / CVE-2024-32004, which were never propagated into dulwich's separately implemented submodule porcelain. Version 1.2.5 patches the issue.
- Record
EUVD-2026-36195- CVSS 3.1
- 7.5 High
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H - Updated
- 2026-11-02
GitHub AdvisoriesGHSA-gfhv-vqv2-4544
7.5
Dulwich's submodule path traversal in porcelain.submodule_update / porcelain.clone(recurse_submodules=True) yields RCE via attacker-dropped .git/hooks payload
Summary
dulwich.porcelain.submodule_update, and by extension porcelain.clone(..., recurse_submodules=True), materializes attacker-controlled submodule paths from a crafted upstream repository without path validation. A malicious .gitmodules plus a matching tree gitlink whose path is .git/hooks (or any other directory inside the parent repository's .git directory) causes the attacker's submodule tree contents to be written directly into the victim's .git/hooks/ directory, preserving executable mode bits. The dropped executables are then run by any subsequent git or dulwich command that invokes the matching hook, resulting in arbitrary code execution.
This is the dulwich equivalent of the upstream Git fixes for CVE-2024-32002 / CVE-2024-32004, which were never propagated into dulwich's separately implemented submodule porcelain.
Affected
- Package:
dulwich(PyPI) - Affected versions:
>=0.23.2, <1.2.5 - Affected platforms: all (Linux, macOS, Windows). Exploitation does not require a case-insensitive or NTFS filesystem, because the path written is a literal
.git/hooksrather than a case- or short-name-aliased form.
Affected entry points:
dulwich.porcelain.submodule_update(repo, init=True, recursive=True)dulwich.porcelain.clone(source, target, recurse_submodules=True)dulwich submodule updateCLI /dulwich clone --recurse-submodulesCLI
Vulnerable code
The submodule path from the tree's gitlink entry (and matching .gitmodules) is consumed without validation in dulwich/porcelain/submodule.py.
The attacker-controlled path enters the loop from iter_cached_submodules (submodule.py#L154-L168):
for path, target_sha in submodules_to_update:
path_str = (
path.decode(DEFAULT_ENCODING) if isinstance(path, bytes) else path
)
submodule_name: bytes | None = None
for sm_path, sm_url, sm_name in read_submodules(gitmodules_path):
if sm_path == path:
submodule_name = sm_name
break
if not submodule_name:
continue
It flows unchecked into os.path.join and the filesystem (submodule.py#L187-L188):
submodule_path = os.path.join(r.path, path_str)
submodule_git_dir = os.path.join(r.controldir(), "modules", path_str)
Finally, the attacker tree's contents are materialized into that directory via build_index_from_tree with no validate_path_element argument, defaulting to the lax validator (submodule.py#L229-L234):
build_index_from_tree(
submodule_path,
sub_repo.index_path(),
sub_repo.object_store,
tree_id,
)
Three issues compound:
path_stroriginates from the parent repository's tree gitlink entry (attacker-controlled) and is never validated against.git,.., or other path-traversal patterns. The same value is read from the attacker-supplied.gitmodulesblob viaread_submodules, which also performs no validation.submodule_path = os.path.join(r.path, path_str)therefore resolves to an attacker-chosen directory anywhere on disk (e.g.<worktree>/.git/hooks). 3.…
- Record
GHSA-gfhv-vqv2-4544- CVSS 3.1
- 7.5 High
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H - Weakness
- CWE-22
- Updated
- 2026-11-02
NVD
7.5
Dulwich is a pure-Python implementation of the Git file formats and protocols. Starting in version 0.23.2 and prior to version 1.2.5, dulwich.porcelain.submodule_update, and by extension porcelain.clone(..., recurse_submodules=True), materializes attacker-controlled submodule paths from a crafted upstream repository without path validation. A malicious .gitmodules plus a matching tree gitlink whose path is .git/hooks (or any other directory inside the parent repository's .git directory) causes the attacker's submodule tree contents to be written directly into the victim's .git/hooks/ directory, preserving executable mode bits. The dropped executables are then run by any subsequent git or dulwich command that invokes the matching hook, resulting in arbitrary code execution. This is the dulwich equivalent of the upstream Git fixes for CVE-2024-32002 / CVE-2024-32004, which were never propagated into dulwich's separately implemented submodule porcelain. Version 1.2.5 patches the issue.
- Record
CVE-2026-52726- CVSS 3.1
- 7.5 High
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H - Weakness
- CWE-22
- Updated
- 2026-11-02
OSV
7.5
Dulwich's submodule path traversal in porcelain.submodule_update / porcelain.clone(recurse_submodules=True) yields RCE via attacker-dropped .git/hooks payload
Dulwich is a pure-Python implementation of the Git file formats and protocols. Starting in version 0.23.2 and prior to version 1.2.5, dulwich.porcelain.submodule_update, and by extension porcelain.clone(..., recurse_submodules=True), materializes attacker-controlled submodule paths from a crafted upstream repository without path validation. A malicious .gitmodules plus a matching tree gitlink whose path is .git/hooks (or any other directory inside the parent repository's .git directory) causes the attacker's submodule tree contents to be written directly into the victim's .git/hooks/ directory, preserving executable mode bits. The dropped executables are then run by any subsequent git or dulwich command that invokes the matching hook, resulting in arbitrary code execution. This is the dulwich equivalent of the upstream Git fixes for CVE-2024-32002 / CVE-2024-32004, which were never propagated into dulwich's separately implemented submodule porcelain. Version 1.2.5 patches the issue.
- Record
CVE-2026-52726- CVSS 3.1
- 7.5 High
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H - Weakness
- CWE-22
- Updated
- 2026-11-02
OSVDEBIAN-CVE-2026-52726
7.5
Dulwich is a pure-Python implementation of the Git file formats and protocols. Starting in version 0.23.2 and prior to version 1.2.5, dulwich.porcelain.submodule_update, and by extension porcelain.clone(..., recurse_submodules=True), materializes attacker-controlled submodule paths from a crafted upstream repository without path validation. A malicious .gitmodules plus a matching tree gitlink whose path is .git/hooks (or any other directory inside the parent repository's .git directory) causes the attacker's submodule tree contents to be written directly into the victim's .git/hooks/ directory, preserving executable mode bits. The dropped executables are then run by any subsequent git or dulwich command that invokes the matching hook, resulting in arbitrary code execution. This is the dulwich equivalent of the upstream Git fixes for CVE-2024-32002 / CVE-2024-32004, which were never propagated into dulwich's separately implemented submodule porcelain. Version 1.2.5 patches the issue.
- Record
DEBIAN-CVE-2026-52726- CVSS 3.1
- 7.5 High
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H - Updated
- 2026-11-02
Show 5 moreShow less
OSVGHSA-gfhv-vqv2-4544
7.5
Dulwich's submodule path traversal in porcelain.submodule_update / porcelain.clone(recurse_submodules=True) yields RCE via attacker-dropped .git/hooks payload
Summary
dulwich.porcelain.submodule_update, and by extension porcelain.clone(..., recurse_submodules=True), materializes attacker-controlled submodule paths from a crafted upstream repository without path validation. A malicious .gitmodules plus a matching tree gitlink whose path is .git/hooks (or any other directory inside the parent repository's .git directory) causes the attacker's submodule tree contents to be written directly into the victim's .git/hooks/ directory, preserving executable mode bits. The dropped executables are then run by any subsequent git or dulwich command that invokes the matching hook, resulting in arbitrary code execution.
This is the dulwich equivalent of the upstream Git fixes for CVE-2024-32002 / CVE-2024-32004, which were never propagated into dulwich's separately implemented submodule porcelain.
Affected
- Package:
dulwich(PyPI) - Affected versions:
>=0.23.2, <1.2.5 - Affected platforms: all (Linux, macOS, Windows). Exploitation does not require a case-insensitive or NTFS filesystem, because the path written is a literal
.git/hooksrather than a case- or short-name-aliased form.
Affected entry points:
dulwich.porcelain.submodule_update(repo, init=True, recursive=True)dulwich.porcelain.clone(source, target, recurse_submodules=True)dulwich submodule updateCLI /dulwich clone --recurse-submodulesCLI
Vulnerable code
The submodule path from the tree's gitlink entry (and matching .gitmodules) is consumed without validation in dulwich/porcelain/submodule.py.
The attacker-controlled path enters the loop from iter_cached_submodules (submodule.py#L154-L168):
for path, target_sha in submodules_to_update:
path_str = (
path.decode(DEFAULT_ENCODING) if isinstance(path, bytes) else path
)
submodule_name: bytes | None = None
for sm_path, sm_url, sm_name in read_submodules(gitmodules_path):
if sm_path == path:
submodule_name = sm_name
break
if not submodule_name:
continue
It flows unchecked into os.path.join and the filesystem (submodule.py#L187-L188):
submodule_path = os.path.join(r.path, path_str)
submodule_git_dir = os.path.join(r.controldir(), "modules", path_str)
Finally, the attacker tree's contents are materialized into that directory via build_index_from_tree with no validate_path_element argument, defaulting to the lax validator (submodule.py#L229-L234):
build_index_from_tree(
submodule_path,
sub_repo.index_path(),
sub_repo.object_store,
tree_id,
)
Three issues compound:
path_stroriginates from the parent repository's tree gitlink entry (attacker-controlled) and is never validated against.git,.., or other path-traversal patterns. The same value is read from the attacker-supplied.gitmodulesblob viaread_submodules, which also performs no validation.submodule_path = os.path.join(r.path, path_str)therefore resolves to an attacker-chosen directory anywhere on disk (e.g.<worktree>/.git/hooks). 3.…
- Record
GHSA-gfhv-vqv2-4544- CVSS 3.1
- 7.5 High
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H - Weakness
- CWE-22
- Updated
- 2026-11-02
OSVPYSEC-2026-2465
7.5
Dulwich's submodule path traversal in porcelain.submodule_update / porcelain.clone(recurse_submodules=True) yields RCE via attacker-dropped .git/hooks payload
Summary
dulwich.porcelain.submodule_update, and by extension porcelain.clone(..., recurse_submodules=True), materializes attacker-controlled submodule paths from a crafted upstream repository without path validation. A malicious .gitmodules plus a matching tree gitlink whose path is .git/hooks (or any other directory inside the parent repository's .git directory) causes the attacker's submodule tree contents to be written directly into the victim's .git/hooks/ directory, preserving executable mode bits. The dropped executables are then run by any subsequent git or dulwich command that invokes the matching hook, resulting in arbitrary code execution.
This is the dulwich equivalent of the upstream Git fixes for CVE-2024-32002 / CVE-2024-32004, which were never propagated into dulwich's separately implemented submodule porcelain.
Affected
- Package:
dulwich(PyPI) - Affected versions:
>=0.23.2, <1.2.5 - Affected platforms: all (Linux, macOS, Windows). Exploitation does not require a case-insensitive or NTFS filesystem, because the path written is a literal
.git/hooksrather than a case- or short-name-aliased form.
Affected entry points:
dulwich.porcelain.submodule_update(repo, init=True, recursive=True)dulwich.porcelain.clone(source, target, recurse_submodules=True)dulwich submodule updateCLI /dulwich clone --recurse-submodulesCLI
Vulnerable code
The submodule path from the tree's gitlink entry (and matching .gitmodules) is consumed without validation in dulwich/porcelain/submodule.py.
The attacker-controlled path enters the loop from iter_cached_submodules (submodule.py#L154-L168):
for path, target_sha in submodules_to_update:
path_str = (
path.decode(DEFAULT_ENCODING) if isinstance(path, bytes) else path
)
submodule_name: bytes | None = None
for sm_path, sm_url, sm_name in read_submodules(gitmodules_path):
if sm_path == path:
submodule_name = sm_name
break
if not submodule_name:
continue
It flows unchecked into os.path.join and the filesystem (submodule.py#L187-L188):
submodule_path = os.path.join(r.path, path_str)
submodule_git_dir = os.path.join(r.controldir(), "modules", path_str)
Finally, the attacker tree's contents are materialized into that directory via build_index_from_tree with no validate_path_element argument, defaulting to the lax validator (submodule.py#L229-L234):
build_index_from_tree(
submodule_path,
sub_repo.index_path(),
sub_repo.object_store,
tree_id,
)
Three issues compound:
path_stroriginates from the parent repository's tree gitlink entry (attacker-controlled) and is never validated against.git,.., or other path-traversal patterns. The same value is read from the attacker-supplied.gitmodulesblob viaread_submodules, which also performs no validation.submodule_path = os.path.join(r.path, path_str)therefore resolves to an attacker-chosen directory anywhere on disk (e.g.<worktree>/.git/hooks). 3.…
- Record
PYSEC-2026-2465- CVSS 3.1
- 7.5 High
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H - Updated
- 2026-11-02
OSVUBUNTU-CVE-2026-52726
7.5
Dulwich is a pure-Python implementation of the Git file formats and protocols. Starting in version 0.23.2 and prior to version 1.2.5, dulwich.porcelain.submodule_update, and by extension porcelain.clone(..., recurse_submodules=True), materializes attacker-controlled submodule paths from a crafted upstream repository without path validation. A malicious .gitmodules plus a matching tree gitlink whose path is .git/hooks (or any other directory inside the parent repository's .git directory) causes the attacker's submodule tree contents to be written directly into the victim's .git/hooks/ directory, preserving executable mode bits. The dropped executables are then run by any subsequent git or dulwich command that invokes the matching hook, resulting in arbitrary code execution. This is the dulwich equivalent of the upstream Git fixes for CVE-2024-32002 / CVE-2024-32004, which were never propagated into dulwich's separately implemented submodule porcelain. Version 1.2.5 patches the issue.
- Record
UBUNTU-CVE-2026-52726- CVSS 3.1
- 7.5 High
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H - Updated
- 2026-11-02
OSVCLSA-2026-1783299701 No score
TuxCare security update for dulwich (1 CVE)
TuxCare rebuilt dulwich to address 1 CVE. Fixed in 0.25.2.post5+tuxcare.
- Record
CLSA-2026-1783299701- Updated
- 2026-11-02
OSVROOT-APP-PYPI-CVE-2026-52726 No score
CVE-2026-52726 in dulwich - Patched by Root
Root has patched CVE-2026-52726 in the dulwich package for Root:PyPI. Multiple fixed versions available.
- Record
ROOT-APP-PYPI-CVE-2026-52726- Updated
- 2026-11-02
Affected products
Affected packages
| Package | Affected | Fixed |
|---|---|---|
dulwichPyPI |
0.23.2 up to 1.2.5 | 1.2.5 |
dulwichTuxCare:PyPI |
0.25.2 up to 0.25.2.post5+tuxcare, plus listed versions 0.25.2.post1+tuxcare0.25.2.post2+tuxcare0.25.2.post3+tuxcare0.25.2.post4+tuxcare |
0.25.2.post5+tuxcare |
dulwichDebian:12 |
All versions, plus listed versions 0.21.2-10.21.5-10.21.6-10.22.5-10.22.7-1 Show 16 moreShow less0.22.7-20.24.10-10.24.2-10.24.2-21.0.0-11.0.0-21.1.0-11.1.0-21.1.0-31.2.0-11.2.1-11.2.11-11.2.12-11.2.5-11.2.7-11.2.7-2 |
None known |
dulwichDebian:13 |
All versions, plus listed versions 0.22.7-10.22.7-20.24.10-10.24.2-10.24.2-2 Show 12 moreShow less1.0.0-11.0.0-21.1.0-11.1.0-21.1.0-31.2.0-11.2.1-11.2.11-11.2.12-11.2.5-11.2.7-11.2.7-2 |
None known |
dulwichDebian:14 |
before 1.2.5-1, plus listed versions 0.22.7-10.22.7-20.24.10-10.24.2-10.24.2-2 Show 7 moreShow less1.0.0-11.0.0-21.1.0-11.1.0-21.1.0-31.2.0-11.2.1-1 |
1.2.5-1 |
dulwichPyPI |
0.23.2 up to 1.2.5, plus listed versions 0.24.00.24.10.24.100.24.20.24.3 Show 16 moreShow less0.24.40.24.50.24.60.24.70.24.80.24.90.25.00.25.10.25.21.0.01.1.01.2.01.2.11.2.21.2.31.2.4 |
1.2.5 |
dulwichRoot:PyPI |
before 1.1.0+aikido.5 | 1.1.0+aikido.51.0.0+aikido.4 |
dulwichUbuntu:16.04:LTS |
All versions, plus listed versions 0.11.2-1ubuntu10.12.0-10.12.0-1build10.12.0-1build2 |
None known |
dulwichUbuntu:18.04:LTS |
All versions, plus listed versions 0.18.2-1build10.18.5-1 |
None known |
dulwichUbuntu:20.04:LTS |
All versions, plus listed versions 0.19.11-20.19.14-4ubuntu10.19.15-10.19.15-1build1 |
None known |
Show 5 moreShow less
| Package | Affected | Fixed |
|---|---|---|
dulwichUbuntu:22.04:LTS |
All versions, plus listed versions 0.20.15-1ubuntu10.20.31-1ubuntu10.20.31-1.10.20.31-1.1build1 |
None known |
dulwichUbuntu:24.04:LTS |
All versions, plus listed versions 0.21.6-10.21.6-1build10.21.6-1build2 |
None known |
dulwichUbuntu:25.10 |
All versions, plus listed versions 0.22.7-1 |
None known |
dulwichUbuntu:26.10 |
All versions, plus listed versions 1.1.0-31.2.12-1 |
None known |
dulwichUbuntu:26.04:LTS |
All versions, plus listed versions 0.22.7-10.24.2-21.1.0-2ubuntu11.1.0-3 |
None known |
History
| Recorded | Changed | Sources |
|---|---|---|
| first indexed | CVE List, EUVD, GitHub Advisories, NVD, OSV |
Dates are when Every CVE recorded each change. Upstream dates may differ.
In short
CVE-2026-52726 is a high vulnerability (CVSS 7.5) in Jelmer Dulwich, published June 10, 2026. No source lists public exploit code. EPSS puts the chance of exploitation in the next 30 days at 0.45%, the 39th percentile. Fixed in dulwich 1.2.5.
References
github.com/jelmer/dulwich/security/advisories/GHSA-gfhv-vqv2-4544
github.com/jelmer/dulwich/releases/tag/dulwich-1.2.5
nvd.nist.gov/vuln/detail/CVE-2026-52726
github.com/advisories/GHSA-gfhv-vqv2-4544
github.com/CVEProject/cvelistV5/tree/main/cves/2026/52xxx/CVE-2026-52726.json
security-tracker.debian.org/tracker/CVE-2026-52726
Left out here: the rest of 3 source descriptions. The JSON record has them.