Every CVE Search/ Get API key

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

7.5 HighFix available

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

0.45%Up 0.3 points since Jun 11, 2026
90%50%10%1%

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 HighCVSS: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 HighCVSS: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/hooks rather 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 update CLI / dulwich clone --recurse-submodules CLI

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:

  1. path_str originates 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 .gitmodules blob via read_submodules, which also performs no validation.
  2. 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 HighCVSS: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 HighCVSS: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 HighCVSS: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 HighCVSS: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/hooks rather 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 update CLI / dulwich clone --recurse-submodules CLI

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:

  1. path_str originates 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 .gitmodules blob via read_submodules, which also performs no validation.
  2. 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 HighCVSS: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/hooks rather 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 update CLI / dulwich clone --recurse-submodules CLI

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:

  1. path_str originates 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 .gitmodules blob via read_submodules, which also performs no validation.
  2. 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 HighCVSS: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 HighCVSS: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

Affected packages
PackageAffectedFixed
dulwich
PyPI
0.23.2 up to 1.2.5
1.2.5
dulwich
TuxCare: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
dulwich
Debian: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 less
0.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
dulwich
Debian: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 less
1.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
dulwich
Debian: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 less
1.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
dulwich
PyPI
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 less
0.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
dulwich
Root:PyPI
before 1.1.0+aikido.5
1.1.0+aikido.51.0.0+aikido.4
dulwich
Ubuntu:16.04:LTS
All versions, plus listed versions
0.11.2-1ubuntu10.12.0-10.12.0-1build10.12.0-1build2
None known
dulwich
Ubuntu:18.04:LTS
All versions, plus listed versions
0.18.2-1build10.18.5-1
None known
dulwich
Ubuntu: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
Affected packages
PackageAffectedFixed
dulwich
Ubuntu: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
dulwich
Ubuntu:24.04:LTS
All versions, plus listed versions
0.21.6-10.21.6-1build10.21.6-1build2
None known
dulwich
Ubuntu:25.10
All versions, plus listed versions
0.22.7-1
None known
dulwich
Ubuntu:26.10
All versions, plus listed versions
1.1.0-31.2.12-1
None known
dulwich
Ubuntu:26.04:LTS
All versions, plus listed versions
0.22.7-10.24.2-21.1.0-2ubuntu11.1.0-3
None known

History

History
RecordedChangedSources
first indexedCVE 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

Show 4 moreShow less
This page doesn't list everything

Left out here: the rest of 3 source descriptions. The JSON record has them.

MoveEnter Open