Skip to content

[BUG] Command Injection in install_droid() via Unescaped Path Interpolation #143

@EnthusiasticTech

Description

@EnthusiasticTech

Project

vgrep

Description

The install_droid() function in src/cli/install.rs constructs shell command strings by directly interpolating file paths without any escaping or sanitization. When the generated settings.json is processed by Factory Droid, paths containing shell metacharacters (;, |, &, $(), backticks, quotes) are interpreted as commands, leading to arbitrary code execution.

The vulnerability exists because:

  1. The home directory path is obtained from dirs::home_dir() which reflects the $HOME environment variable
  2. This path is directly interpolated into a command string using format!()
  3. The resulting string is stored in Factory Droid's settings.json as a hook command
  4. When Factory Droid executes the hook, the shell interprets metacharacters in the path

Affected Files

  • src/cli/install.rs (lines 379-389, 398-409)

Evidence

Vulnerable Code - SessionStart Hook:

    if let Some(arr) = session_start {
        let hook_entry = serde_json::json!({
            "matcher": "startup|resume",
            "hooks": [{
                "type": "command",
                "command": format!("python3 \"{}\"", watch_py.display()),  // VULNERABLE!
                "timeout": 10
            }]
        });

Vulnerable Code - SessionEnd Hook:

    if let Some(arr) = session_end {
        let hook_entry = serde_json::json!({
            "hooks": [{
                "type": "command",
                "command": format!("python3 \"{}\"", kill_py.display()),  // VULNERABLE!
                "timeout": 10
            }]
        });

Path Construction (shows full attack surface):

    let watch_py = hooks_dir.join("vgrep_watch.py");
    let kill_py = hooks_dir.join("vgrep_watch_kill.py");

Where hooks_dir derives from:

    let hooks_dir = factory_dir.join("hooks").join("vgrep");

And factory_dir derives from:

    let factory_dir = home_dir()?.join(".factory");

Error Message

Debug Logs

System Information

Bounty Version: 0.1.0
OS: Ubuntu 24.04 LTS
CPU: AMD EPYC-Genoa Processor (8 cores)
RAM: 15 GB

Screenshots

No response

Steps to Reproduce

Method 1: Environment Variable Manipulation

# Create a malicious home directory path
mkdir -p '/tmp/test"; touch /tmp/PWNED; echo "/'
mkdir -p '/tmp/test"; touch /tmp/PWNED; echo "/.factory'

# Run install with manipulated HOME
HOME='/tmp/test"; touch /tmp/PWNED; echo "/' cargo run -- install droid

# Check if settings.json contains unescaped path
cat '/tmp/test"; touch /tmp/PWNED; echo "/.factory/settings.json' | grep command

# Verify command injection potential
ls -la /tmp/PWNED  # File exists if Factory Droid executed the hook

Method 2: Inspect Generated Configuration

# Normal installation
cargo run -- install droid

# Examine the generated settings.json
cat ~/.factory/settings.json | jq '.hooks'

# Look for string-based command (vulnerable) vs array-based (safe)
# Vulnerable: "command": "python3 \"/path/to/script.py\""
# Safe:      "command": ["python3", "/path/to/script.py"]

Method 3: Unit Test

#[test]
fn test_command_injection_in_install_droid() {
    // Simulate a malicious path
    let malicious_home = "/home/user\"; rm -rf /; echo \"";
    let hooks_path = format!("{}/.factory/hooks/vgrep/vgrep_watch.py", malicious_home);
    
    // This is what the vulnerable code produces
    let vulnerable_command = format!("python3 \"{}\"", hooks_path);
    
    // The command now contains shell metacharacters that will be interpreted
    assert!(vulnerable_command.contains("\"; rm -rf /;"));
    
    // Expected: shell interprets ; as command separator
    // Result: rm -rf / executes with user privileges
}

Expected Behavior

The install_droid() function should:

  1. Properly escape or sanitize paths before interpolating them into command strings
  2. Use array-based command execution instead of shell string interpolation
  3. Validate that paths don't contain shell metacharacters
  4. Generate safe hook configurations that cannot be exploited

Actual Behavior

Paths are directly interpolated into command strings without any escaping. If a user's home directory or any component of the path contains shell metacharacters, those characters will be interpreted by the shell when Factory Droid executes the hook.

Additional Context

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingvalidValid issuevgrep

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions