CVEReports
Reports
CVEReports

Automated vulnerability intelligence platform. Comprehensive reports for high-severity CVEs generated by AI.

Product

  • Home
  • Reports
  • Sitemap
  • RSS Feed

Company

  • About
  • Privacy Policy
  • Terms of Service

© 2026 CVEReports. All rights reserved.

Powered by Google Gemini & CVE Feed

|
•

CVE-2022-24433
CVSS 8.1|EPSS 0.93%

Git Rekt: The Simple-Git Command Injection Deep Dive

Alon Barad
Alon Barad
Software Engineer•January 2, 2026•6 min read
PoC Available

Executive Summary (TL;DR)

Versions of `simple-git` prior to 3.3.0 fail to sanitize input in the `fetch()` function. Attackers can inject Git flags (specifically `--upload-pack`) to achieve Remote Code Execution (RCE). The fix involves a regex blocklist against this specific flag.

A critical argument injection vulnerability in the popular `simple-git` Node.js library allows attackers to execute arbitrary system commands via the `.fetch()` method. By abusing Git's `--upload-pack` flag, malicious inputs can trick the underlying git binary into executing shell commands.

The Hook: Wrappers, Convenience, and Catastrophe

We all love wrappers. Why write twenty lines of child_process.spawn boilerplate when you can just npm install simple-git and pretend the operating system doesn't exist? It's the classic developer efficiency trap: abstracting away the underlying shell commands until you forget they are dangerous.

simple-git is a titan in the Node ecosystem. It is the go-to library for managing git repositories programmatically. CI/CD pipelines, automated deployment tools, and developer CLI utilities rely on it to handle the heavy lifting of version control. But here is the problem with abstraction: it requires trust. You trust the library to take your string, remote_repo_url, and hand it safely to the git binary.

CVE-2022-24433 breaks that trust. It turns out that simple-git was a little too simple. It took user input and concatenated it directly into the argument list for the system's git command. It’s the software equivalent of handing a loaded gun to a toddler and hoping they only aim at the target. When you control the arguments passed to a binary, you often control the execution flow of the binary itself. And in the case of Git, that flow allows for some truly spectacular fireworks.

The Flaw: Argument Injection vs. Command Injection

Let's get technical. Most people hear "Command Injection" and think of semicolons and pipes (e.g., ; rm -rf /). That is Shell Command Injection, where you break out of the command context to execute a new, separate shell command.

This vulnerability is different. This is Argument Injection (CWE-88). We aren't breaking out of the git command; we are subverting it. The simple-git library constructs a command array that looks something like ['fetch', remote, branch]. If an attacker supplies a malicious remote string, it gets pushed into that array. The developer assumed the input would be a URL or a remote name (like origin).

But Git is an incredibly complex beast with hundreds of flags. If your input starts with a hyphen (-), Git interprets it as an option, not a positional argument. The "Smoking Gun" here is the --upload-pack option. This flag tells Git: "Hey, when you talk to the remote server, use this executable to handle the protocol."

If we set --upload-pack to a shell command, Git obliges and executes it. The flaw isn't that simple-git executes a shell; the flaw is that simple-git allows the user to define which executable Git uses for its internal operations.

The Code: A Tale of Missing Sanity Checks

Let's look at the crime scene. In versions prior to 3.3.0, the fetchTask function was naively trusting. It treated input as data, never suspecting it might be code.

The Vulnerable Code:

// src/lib/tasks/fetch.ts (Pre-patch)
export function fetchTask(remote: string, branch: string, customArgs: string[]): StringTask<FetchResult> {
   const commands = ['fetch', ...customArgs];
   // ERROR: Direct appending of user input without validation
   if (remote && branch) {
      commands.push(remote, branch);
   }
   return { commands, format: 'utf-8', parser: parseFetchResult };
}

Do you see the lack of fear? The lack of cynical bounds checking? It just pushes remote and branch right onto the stack. If I pass --upload-pack=calc.exe as the remote, it goes straight to the system call.

The Fix (v3.3.0):

To fix this, the maintainers implemented a specific check for the dangerous flag. They didn't rewrite the architecture; they just added a bouncer at the door.

// src/lib/tasks/fetch.ts (Patched)
function disallowedCommand(command: string) {
   // Regex to catch the specific exploit vector
   return /^--upload-pack(=|$)/.test(command);
}
 
export function fetchTask(remote: string, branch: string, customArgs: string[]) {
   const commands = ['fetch', ...customArgs];
   if (remote && branch) {
      commands.push(remote, branch);
   }
 
   // The new sanity check
   const banned = commands.find(disallowedCommand);
   if (banned) {
      return configurationErrorTask(`git.fetch: potential exploit argument blocked.`);
   }
   // ...
}

[!NOTE] This is a blocklist approach. While effective against this specific CVE, blocklists are historically fragile. If Git introduces a new dangerous flag tomorrow, this code becomes vulnerable again.

The Exploit: Weaponizing the Protocol

So, how do we ruin a sysadmin's day with this? We need an application that accepts a remote URL or branch name from a user and passes it to .fetch(). This is common in tools that sync repositories or check for updates.

The Attack Chain:

  1. Recon: Identify a target using simple-git < 3.3.0. A GitHub bot or a CI runner is a prime candidate.
  2. Payload Construction: We want to run id or touch a file to prove access. We format this into the --upload-pack flag.
    • Payload: --upload-pack="touch /tmp/pwned"
  3. Delivery: Submit this string as the "Remote URL".
  4. Execution: The application constructs the command: git fetch --upload-pack="touch /tmp/pwned" origin.

Here is how the flow looks execution-wise:

This was famously demonstrated in the HackTheBox machine "FormulaX". In that scenario, the vulnerability allowed attackers to gain an initial foothold on the server by manipulating a repository configuration feature. It turned a mundane "Check for Updates" button into a remote shell.

[!WARNING] This exploit works even if the git command fails later. The --upload-pack command is executed before the fetch actually completes, because Git needs to establish the transport channel first.

The Impact: Why You Should Care

This is a High Severity (8.1) vulnerability for a reason. We are talking about Remote Code Execution (RCE) with the privileges of the Node.js process.

If this code is running in a Docker container, the attacker effectively owns the container. If it is running on a developer's machine (e.g., via an Electron app or a CLI tool), the attacker has access to source code, SSH keys, and valid credentials.

Because simple-git is often used in trusted environments (CI pipelines, backend automation), the privileges associated with the process are often higher than a typical web server. The attacker doesn't just get a shell; they get a shell in the heart of your infrastructure.

Mitigation: Patching the Hole

The remediation path is straightforward, but critical. The patch in version 3.3.0 specifically targets the Argument Injection vector used in .fetch().

Primary Fix:

  • Upgrade immediately: Run npm install simple-git@latest. Ensure you are on version 3.3.0 or higher.

Secondary Defense:

  • Input Validation: Never trust user input to start with a standard alphanumeric character. If you expect a URL, validate it as a URL. If you expect a branch name, ensure it matches a strict regex (e.g., ^[a-zA-Z0-9/_-]+$).
  • Reject Hyphens: Explicitly reject any input starting with - if it is intended to be a positional argument.

It is worth noting that while this CVE covered .fetch(), subsequent vulnerabilities (CVE-2022-25860, CVE-2022-25912) found similar issues in .clone() and .pull(). This reinforces the need to keep dependencies updated aggressively. Security is a moving target, and static libraries are sitting ducks.

Official Patches

GitHubPull Request containing the fix

Fix Analysis (1)

Technical Appendix

CVSS Score
8.1/ 10
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H
EPSS Probability
0.93%
Top 24% most exploited

Affected Systems

Node.js applications using simple-git < 3.3.0CI/CD pipelines using vulnerable simple-git versionsElectron apps utilizing simple-git for repo management

Affected Versions Detail

ProductAffected VersionsFixed Version
simple-git
steveukx
< 3.3.03.3.0
AttributeDetail
CWE IDCWE-88 (Argument Injection)
CVSS v3.18.1 (High)
Attack VectorNetwork (Input to .fetch)
Affected Componentsimple-git .fetch() method
Key Flag--upload-pack
Exploit StatusPoC Available / Verified in CTFs

MITRE ATT&CK Mapping

MITRE ATT&CK Mapping

T1202Indirect Command Execution
Execution
T1059Command and Scripting Interpreter
Execution
CWE-88
Improper Neutralization of Argument Delimiters in a Command ('Argument Injection')

The software constructs a string for a command from trusted and untrusted data but does not properly neutralize argument delimiters, allowing the injection of new arguments.

Exploit Resources

Known Exploits & Detection

HackTheBoxExploitation in the FormulaX machine using --upload-pack
SnykOriginal disclosure and Proof of Concept

Vulnerability Timeline

Vulnerability Timeline

Vulnerability Disclosed by Snyk
2022-03-11
CVE-2022-24433 Assigned
2022-03-11
Patch v3.3.0 Released
2022-03-11
Documented usage in HTB FormulaX
2024-08-31

References & Sources

  • [1]NVD Entry
  • [2]GitHub Advisory
  • [3]Snyk Advisory
Related Vulnerabilities
CVE-2022-25860CVE-2022-25912

Subscribe to updates

Get the latest CVE analysis reports delivered to your inbox.

Attack Flow Diagram

Press enter or space to select a node. You can then use the arrow keys to move the node around. Press delete to remove it and escape to cancel.
Press enter or space to select an edge. You can then press delete to remove it or escape to cancel.