Security Vulnerability Report
中文
CVE-2026-44471 CVSS 7.8 HIGH

CVE-2026-44471

Published: 2026-05-13 22:16:46
Last Modified: 2026-05-14 17:18:19

Description

gitoxide is an implementation of git written in Rust. Prior to 0.21.1, a malicious tree can be constructed that will, when checked out with gitoxide, permit writing an attacker-controlled symlink into any existing directory the user has write access to. During checkout, all symlink index entries are deferred and created after regular files using a single shared gix_worktree::Stack. Internally, this uses a gix_fs::Stack. gix_fs::Stack::make_relative_path_current() caches validated path prefixes: when the previously-processed leaf component exactly matches the leading component(s) of the next path, the leaf-to-directory transition at gix-fs/src/stack.rs invokes only delegate.push_directory(), never delegate.push(). In gix_worktree::stack::delegate::StackDelegate, when the state member is State::CreateDirectoryAndAttributesStack, Attributes::push_directory() only loads attributes (from the ODB, in the clone case), and does not perform any other checks. The on-disk symlink_metadata() check and unlink-on-collision live in StackDelegate::push()'s invocation of create_leading_directory(), which is therefore bypassed for the cached prefix. The final symlink is created with plain std::os::unix::fs::symlink, which follows symlinks in parent directories. Therefore, it's possible to provide a tree with duplicate symlink and directory entries that exploits this. This vulnerability is fixed in 0.21.1.

CVSS Details

CVSS Score
7.8
Severity
HIGH
CVSS Vector
CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H

Configurations (Affected Products)

No configuration data available.

Gitoxide < 0.21.1

PoC / Exploit Code

⚠ For Security Research Only
The following code is for security research and authorized testing only.
python
import os import subprocess # Proof of Concept for CVE-2026-44471 # This script demonstrates the setup for a malicious repository structure. # Exploitation requires gitoxide < 0.21.1 to checkout the repository. def create_exploit_repo(): repo_dir = "gitoxide_poc_cve_2026_44471" if os.path.exists(repo_dir): os.rmdir(repo_dir) os.makedirs(repo_dir) os.chdir(repo_dir) # Initialize git repo subprocess.run(["git", "init"]) # Create a file that will later be part of the directory structure # to trigger the leaf-to-directory caching issue. with open("legit_file", "w") as f: f.write("This is a legitimate file.") subprocess.run(["git", "add", "legit_file"]) subprocess.run(["git", "commit", "-m", "Initial commit"]) # To actually exploit CVE-2026-44471, one must manually craft Git tree objects # that contain duplicate entries where a directory is replaced by a symlink # or vice versa in a way that confuses the gix_fs::Stack cache. # Below is a conceptual representation. print(f"Repository initialized at {os.getcwd()}") print("To exploit: Manually craft tree objects with conflicting directory/symlink paths.") print("Then run: gitoxide checkout") if __name__ == "__main__": create_exploit_repo()

References

Raw JSON Data

JSON
{"cve": {"id": "CVE-2026-44471", "sourceIdentifier": "[email protected]", "published": "2026-05-13T22:16:46.057", "lastModified": "2026-05-14T17:18:18.640", "vulnStatus": "Awaiting Analysis", "cveTags": [], "descriptions": [{"lang": "en", "value": "gitoxide is an implementation of git written in Rust. Prior to 0.21.1, a malicious tree can be constructed that will, when checked out with gitoxide, permit writing an attacker-controlled symlink into any existing directory the user has write access to. During checkout, all symlink index entries are deferred and created after regular files using a single shared gix_worktree::Stack. Internally, this uses a gix_fs::Stack. gix_fs::Stack::make_relative_path_current() caches validated path prefixes: when the previously-processed leaf component exactly matches the leading component(s) of the next path, the leaf-to-directory transition at gix-fs/src/stack.rs invokes only delegate.push_directory(), never delegate.push(). In gix_worktree::stack::delegate::StackDelegate, when the state member is State::CreateDirectoryAndAttributesStack, Attributes::push_directory() only loads attributes (from the ODB, in the clone case), and does not perform any other checks. The on-disk symlink_metadata() check and unlink-on-collision live in StackDelegate::push()'s invocation of create_leading_directory(), which is therefore bypassed for the cached prefix. The final symlink is created with plain std::os::unix::fs::symlink, which follows symlinks in parent directories. Therefore, it's possible to provide a tree with duplicate symlink and directory entries that exploits this. This vulnerability is fixed in 0.21.1."}], "metrics": {"cvssMetricV31": [{"source": "[email protected]", "type": "Secondary", "cvssData": {"version": "3.1", "vectorString": "CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H", "baseScore": 7.8, "baseSeverity": "HIGH", "attackVector": "LOCAL", "attackComplexity": "LOW", "privilegesRequired": "NONE", "userInteraction": "REQUIRED", "scope": "UNCHANGED", "confidentialityImpact": "HIGH", "integrityImpact": "HIGH", "availabilityImpact": "HIGH"}, "exploitabilityScore": 1.8, "impactScore": 5.9}]}, "weaknesses": [{"source": "[email protected]", "type": "Secondary", "description": [{"lang": "en", "value": "CWE-59"}]}], "references": [{"url": "https://github.com/GitoxideLabs/gitoxide/security/advisories/GHSA-f89h-2fjh-2r9q", "source": "[email protected]"}, {"url": "https://github.com/GitoxideLabs/gitoxide/security/advisories/GHSA-f89h-2fjh-2r9q", "source": "134c704f-9b21-4f2e-91b3-4a467353bcc0"}]}}