Listen to this Post
How the mentioned CVE works (approx. 20 lines):
The vulnerability resides in gix_submodule::File::update(), which determines if a submodule’s `update` value from `.gitmodules` can be a shell command (e.g., update = !touch /tmp/pwned). The API intends to return `Err(CommandForbiddenInModulesConfiguration)` unless the command originates from a trusted source like .git/config. However, the guard is flawed: it checks whether any section with the submodule name exists from a non-.gitmodules source, but does not verify that the actual `update` value came from that trusted section.
An attacker first supplies a benign `.gitmodules` (no `update` key). The victim runs git submodule init, which writes `url` and `active = true` into `.git/config` – without copying any `update` key. Later, the attacker pushes a malicious commit that adds `update = !cmd` to .gitmodules. The victim pulls this change. Now `.git/config` has a trusted section (with only `url` and active), while `.gitmodules` has the attacker’s command.
When `gix::Submodule::update()` is called, the lookup reads `submodule.
In contrast, `git submodule update` on the same repository aborts with fatal: invalid value for 'submodule.sub.update'. The vulnerable code was introduced in commit 6a2e6a4. Any downstream application that calls `Submodule::update()` and trusts `Update::Command` as safe is vulnerable to arbitrary command execution.
DailyCVE form:
Platform: Gitoxide library
Version: before patch
Vulnerability: Submodule update bypass
Severity: Critical
date: 2025-04-07
Prediction: 2025-05-15
Analytics under What Undercode Say:
Unit test confirming bypass cargo test -p gix-submodule security_bypass -- --nocapture End-to-end reproduction git clone /tmp/evil-repo victim cd victim && git submodule init cd ../evil-repo && echo 'update = !touch /tmp/pwned' >> .gitmodules git commit -am "malicious" cd ../victim && git pull cargo run --bin show_submodule_update
// Vulnerable code snippet (access.rs lines 168-193)
let value: Update = self.config.string(format!("submodule.{name}.update"))?;
if let Update::Command(cmd) = &value {
let has_value_from_foreign_section = self.config.sections_by_name("submodule")
.into_iter().flatten()
.any(|s| s.header().subsection_name() == Some(name) && !std::ptr::eq(s.meta(), ours));
if !has_value_from_foreign_section { return Err(CommandForbidden...); }
}
Exploit:
Attacker needs a repository with a submodule that the victim has initialized once (e.g., via git submodule init). After victim runs init, attacker adds `update = !malicious_command` to `.gitmodules` and pushes. Victim pulls. Any tool using `gix::Submodule::update()` will then return Ok(Some(Update::Command("malicious_command"))), leading to RCE when that command is executed.
Protection from this CVE:
- Upgrade gix/gitoxide to a patched version once available (fixes should compare the source of the `update` value, not just existence of any trusted section).
- Avoid calling `Submodule::update()` on submodules that have ever been initialized from untrusted remotes.
- Use `git` CLI instead of gitoxide for submodule operations until patch is applied.
- Implement manual validation: after obtaining
Update, check whether the effective value originates from `.git/config` by re-reading from specific origin.
Impact:
Direct: Any downstream application (CI integrations, IDE plugins, forge tools) using `gix` that relies on `Submodule::update()` and then executes the returned command will suffer arbitrary shell command injection. Indirect: Tools that implement their own `init` (writing `url` to .git/config) create the vulnerable state without needing a `pull` sequence. The `gix` CLI itself is not directly exploitable because it lacks a submodule update command, but the public API is exposed and documented as secure.
🎯Let’s Practice Exploiting & Learn Patching For Free:
Sources:
Reported By: github.com
Extra Source Hub:
Undercode

