Low watch CVSS 5.4 chrome Input Validation 📄 Reporter bug report 🔧 Commit mapped

Overview

Low
Severity
5.4
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactInsufficient data validation in DevTools
DescriptionInsufficient data validation in DevTools
ComponentDevTools
Sub-componentPlatform>DevTools>Network
Bug ClassInput Validation
Action tierwatch — logic/policy bug — security-relevant, no memory-unsafety
Computed by PatchGap's deterministic exploitability ruler from the fixing commit. patch-now = RCE-class & attacker-reachable in a default config, or exploited in the wild; watch = real security signal below that bar; noise = no security relevance.
Tracker406631048
Fix commit5381a527929f (devtools/devtools-frontend) +29/-1
CWECWE-1021
CVSS vectorCVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:L/I:L/A:N
CISA KEVNot listed
CreditedAmeen Basha M K
Reported2025-03-27 · bounty $1000 (per vendor advisory)
Fix landed2025-05-21 (public git)
Disclosed2025-06-24
Patch gap34 days — fix public in source before the advisory

Background

"Copy as cURL"
A DevTools Network panel feature that serializes a captured request into a shell curl command, escaping header/cookie values so they can be pasted into a terminal.
Platform-specific escaping
generateCurlCommand emits either a POSIX (unix) or Windows (win) command, each with its own quoting/escaping rules; the changed code is the Windows-side string escaper.
CR vs. LF vs. CRLF
Carriage return (\r), line feed (\n), and the \r\n pair are distinct control characters; on Windows cmd.exe, an unescaped newline can terminate the current command and start a new one.
Regex alternation `|`
In a JavaScript replace pattern, /\r?\n|\r/ matches a LF (optionally preceded by CR) or a lone CR, whereas /\r?\n/ matches only sequences ending in LF.

Root Cause Analysis

The Windows curl-command escaper neutralized newlines with .replace(/\r?\n/g, '^\n\n'), a pattern that matches a lone \n or a \r\n pair but never a sole carriage return (\r) that is not followed by \n. A header/cookie value containing a bare \r therefore passed through the escaper unescaped and was embedded directly into the generated Windows command string.

The fix changes the pattern to .replace(/\r?\n|\r/g, '^\n\n'), adding a |\r alternative so a standalone CR is also replaced with the caret-escaped newline sequence, closing the gap. This is a tightening of input escaping: previously-unescaped control characters are now escaped. The regression test confirms the intent, checking that a value like query=evil\r & cmd /c calc.exe is properly escaped in both unix and win output.

Key insight
The Windows newline-escaping regex \r?\n failed to match a lone carriage return, letting a bare \r in a header value survive into the copied curl command unescaped; adding the |\r alternation makes the escaper handle sole CRs like other newlines.

Attack Path

  1. Plant a malicious header value An attacker gets a request captured in DevTools to carry a header/cookie value containing a bare carriage return followed by command text, e.g. query=evil\r & cmd /c calc.exe (as in the regression test).
  2. Victim copies as cURL (Windows) The user invokes “Copy as cURL” for the win platform; the pre-patch escaper leaves the lone \r unescaped in the produced command string.
  3. Command breaks on paste When pasted into cmd.exe, the unescaped carriage return acts as a line/command boundary, so the trailing attacker-controlled text (& cmd /c calc.exe) is interpreted as a separate command rather than data — a command-injection into the pasted shell command.
  4. Execution in the user's shell The injected command runs with the user’s privileges in their terminal; this requires the user to copy and paste the crafted command, and further impact is bounded by what that shell can do (inferred).

Impact Assessment

An attacker who controls a header/cookie value that a victim later copies via DevTools “Copy as cURL” on Windows could inject shell commands into the copied string, so pasting it into cmd.exe executes attacker-chosen commands with the user’s privileges. The advisory classifies this as insufficient data validation in DevTools (bug 406631048) and does not report in-the-wild exploitation; exploitation depends on user interaction (opening DevTools, copying, and pasting the command), and the concrete calc.exe-style execution is illustrated by the test but any broader escalation is (inferred).

Files Changed

  • front_end/panels/network/NetworkLogView.test.ts
  • front_end/panels/network/NetworkLogView.ts

Audit Directions

  • Incomplete newline/control-char patterns
    Search escaping/sanitization code for \r?\n, \n, or [\r\n]-style patterns that omit a lone \r (or other CR/LF/CRLF combinations) when building shell, HTTP, or log strings.
  • Command/text generation from network-controlled data
    Audit other DevTools “Copy as …” and export features (fetch, PowerShell, HAR) that serialize attacker-influenced headers/values into shell or code strings.
  • Platform-divergent escaping
    Compare unix vs. win (and other) escaping branches for the same generator to ensure each branch neutralizes the full set of control characters, since gaps often exist in only one branch.
From 5381a527929f5a5842d00a57830950a195ba2488 Mon Sep 17 00:00:00 2001
From: Danil Somsikov <dsv@chromium.org>
Date: Wed, 21 May 2025 01:44:06 -0700
Subject: [PATCH] Also handle sole carriage return character (CR) when escaping curl command.

Bug: 406631048
Change-Id: I947dbf54d290ada4424bfbfcc817e1e7244bf438
Reviewed-on: https://chromium-review.googlesource.com/c/devtools/devtools-frontend/+/6573473
Auto-Submit: Danil Somsikov <dsv@chromium.org>
Commit-Queue: Danil Somsikov <dsv@chromium.org>
Reviewed-by: Benedikt Meurer <bmeurer@chromium.org>
Commit-Queue: Benedikt Meurer <bmeurer@chromium.org>
---

diff --git a/front_end/panels/network/NetworkLogView.test.ts b/front_end/panels/network/NetworkLogView.test.ts
index 614e5b7..b0d653e 100644
--- a/front_end/panels/network/NetworkLogView.test.ts
+++ b/front_end/panels/network/NetworkLogView.test.ts
@@ -181,6 +181,34 @@
     );
   });
 
+  it('generates a valid curl command when header values contain CRLF', async () => {
+    const request = createNetworkRequest(urlString`http://localhost`, {
+      requestHeaders: [{name: 'cookie', value: 'query=evil\r\n & cmd /c calc.exe \n\n'}],
+    });
+    assert.strictEqual(
+        await Network.NetworkLogView.NetworkLogView.generateCurlCommand(request, 'unix'),
+        'curl \'http://localhost\' -b $\'query=evil\\r\\n & cmd /c calc.exe \\n\\n\'',
+    );
+    assert.strictEqual(
+        await Network.NetworkLogView.NetworkLogView.generateCurlCommand(request, 'win'),
+        'curl ^\"http://localhost^\" -b ^\"query=evil^\n\n ^& cmd /c calc.exe ^\n\n^\n\n^\"',
+    );
+  });
+
+  it('generates a valid curl command when header values contain CR only', async () => {
+    const request = createNetworkRequest(urlString`http://localhost`, {
+      requestHeaders: [{name: 'cookie', value: 'query=evil\r & cmd /c calc.exe'}],
+    });
+    assert.strictEqual(
+        await Network.NetworkLogView.NetworkLogView.generateCurlCommand(request, 'unix'),
+        'curl \'http://localhost\' -b $\'query=evil\\r & cmd /c calc.exe\'',
+    );
+    assert.strictEqual(
+        await Network.NetworkLogView.NetworkLogView.generateCurlCommand(request, 'win'),
+        'curl ^\"http://localhost^\" -b ^\"query=evil^\n\n ^& cmd /c calc.exe^\"',
+    );
+  });
+
   const tests = (inScope: boolean) => () => {
     beforeEach(() => {
       networkLogView = createNetworkLogView();
diff --git a/front_end/panels/network/NetworkLogView.ts b/front_end/panels/network/NetworkLogView.ts
index 903695e..7e89058 100644
--- a/front_end/panels/network/NetworkLogView.ts
+++ b/front_end/panels/network/NetworkLogView.ts
@@ -2354,7 +2354,7 @@
               .replace(/"/g, '\\"')
               .replace(/[^a-zA-Z0-9\s_\-:=+~'\/.',?;()*`]/g, '^$&')
               .replace(/%(?=[a-zA-Z0-9_])/g, '%^')
-              .replace(/\r?\n/g, '^\n\n') +
+              .replace(/\r?\n|\r/g, '^\n\n') +
           encapsChars;
     }
 
Loading diff…

Original Bug Report

reported by am...@gmail.com

Copy as Curl (CMD) Leads to code execution on windows

Steps to reproduce the problem

  1. Copy and run the payload in console (we can achieve it via html page too)
  2. Copy the request with copy as curl (windows)
  3. Paste on the windows cmd you can see calc triggers

Problem Description

HI Team, chrome latest on windows is vulnerable for code execution using copy as curl(cmd) which allows attacker to excute arbitrary code on victims (windows) machine

Issue ref: https://issues.chromium.org/issues/352651673 Similar issue was already reported to chrome and fixed (ref above) but now it was bypassed with my attached payload

Payload: fetch(“https://example.com/postit", { “credentials”: “omit”, “headers”: { “Accept-Language”: “en-US”, “Content-Type”: “text/plain”, }, “body”: “query=evil\n\ncmd /c calc1.exe\t\r\t calc2.exe \t calc3.exe \rcalc.exe\rcalc.exe\r calc7.exe \rt\r\t calc2.exe \t calc3.exe \r”, “method”: “POST”, });

I have attached the poc video for reference

Summary

Copy as Curl (CMD) Leads to code execution on windows

Custom Questions

Reporter credit:

Ameen Basha M K

Additional Data

Category: Security
Chrome Channel: Canary
Regression: N/A

View on issue tracker