Medium CVSS 8.8 webkit Logic Error 🔧 Commit mapped

Overview

Medium
Severity
8.8
CVSS
No
Exploited ITW
Fixed
Fix Status
DescriptionCopying a URL from Web Inspector may lead to command injection
ComponentWebInspectorUI
Bug ClassLogic Error
Tracker283718
Fix commitfed47c6e0559 (WebKit/WebKit)
CWECWE-77
CVSS vectorCVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H
CISA KEVNot listed
CreditedJohan Carlsson (joaxcar)
Disclosed2025-01-27

Background

Copy as cURL
A Web Inspector Network-tab feature that serializes a recorded HTTP request into an equivalent curl command line for the user to paste into a terminal.
escapeStringPosix
A WebInspectorUI helper that wraps a string in POSIX single quotes and escapes embedded quotes so shell metacharacters in the value are not interpreted by the shell.
curl --data-binary vs --data-raw
Both send a request body, but --data/--data-binary treat a leading ‘@’ as a filename to read, whereas --data-raw sends the argument literally with no ‘@’ interpretation.
Argument injection
An attack where attacker-controlled data becomes part of a command’s arguments and is interpreted by the invoked program in a way the caller did not intend, even without classic shell metacharacter injection.

Root Cause Analysis

Web Inspector’s Network tab offers a “Copy as cURL” action, implemented in WI.Resource’s command-generation code (Source/WebInspectorUI/UserInterface/Models/Resource.js). When the recorded request carries a body, the generated command appends the request body as a curl data option. For non-form-encoded bodies the old code emitted --data-binary <escaped-body>, where escapeStringPosix() wraps the body in POSIX single quotes to neutralize shell metacharacters. The violated invariant is that the copied command string, when pasted into a shell, must transmit exactly the bytes the page sent and must not cause curl to perform any additional filesystem or side-effecting action. The flaw is that curl’s --data / --data-binary options treat a leading @ in their argument as a request to read the argument’s remainder as a filename (or - as stdin), and shell single-quoting does NOT stop this: the quotes are stripped by the shell, and curl itself then interprets the resulting literal @... string as a file path. So a request body beginning with @ — fully attacker-controlled by the page under inspection (e.g. via fetch/XHR POST) — would make the pasted command read an arbitrary local file (like @/etc/passwd) and upload it, or otherwise act on a filename the victim never intended.

The fix replaces --data-binary with --data-raw; per curl semantics --data-raw is identical to --data except it explicitly does NOT interpret a leading @, so the body is always sent verbatim. The LayoutTest is updated in lockstep to expect --data-raw '{"update":"now"}'. This is not memory corruption; it is an argument-injection / command-construction logic bug where the escaping layer (shell quoting) did not cover the actual sink (curl’s own @-filename parsing).

Key insight
The escaping was applied at the wrong layer: shell single-quoting protects against the shell, but the dangerous parsing happens inside curl, whose --data-binary/--data options interpret a leading ‘@’ as a filename after the shell strips the quotes — switching to --data-raw moves the body to a sink that never performs that interpretation.

Attack Path

  1. Serve a page with a crafted request body The attacker controls or influences a web page and causes it to issue an HTTP request whose body begins with ‘@’, e.g. a POST with body ‘@/etc/passwd’ or ‘@~/.ssh/id_rsa’, via fetch() or XMLHttpRequest.
  2. Victim opens Web Inspector on the request A developer or user inspecting the site opens the Network tab, which records the request and its body.
  3. Victim uses Copy as cURL The victim right-clicks the request and selects Copy as cURL; the pre-patch code builds --data-binary '@/etc/passwd'.
  4. Victim pastes into a shell The shell strips the single quotes, leaving curl with the literal argument @/etc/passwd.
  5. curl reads and exfiltrates a local file curl interprets the leading ‘@’ as a file reference, reads the named local file, and sends its contents to the request’s URL (attacker-controlled), disclosing arbitrary local files; other ‘@’/’-’ tricks can redirect input in unintended ways.

Impact Assessment

The primitive is local file disclosure / unintended file access by curl, gated on significant user interaction: a victim must open Web Inspector on an attacker-influenced request, choose Copy as cURL, and paste the result into a shell. There is no memory corruption and nothing runs in the WebContent/GPU/Network sandbox as a result of the browser code itself; the side effect executes in the victim’s own shell/user context with their filesystem privileges. Severity is medium precisely because it is a data-exfiltration/argument-injection issue rather than code execution in the engine, and it requires the multi-step manual paste. Escalation is limited to reading files curl can access and sending them to the request URL; it is not a stepping stone to browser-process RCE.

Changed Functions

FunctionChangeNotes
WI.Resource.prototype (Copy as cURL command builder)
Source/WebInspectorUI/UserInterface/Models/Resource.js
modified Changed the non-form-encoded body branch to push `--data-raw <escaped>` instead of `--data-binary <escaped>`, so curl no longer treats a leading '@' in the body as a filename.
test (copy-as-curl layout test)
LayoutTests/http/tests/inspector/network/copy-as-curl.html
modified Updated the expectation from `--data-binary '{...}'` to `--data-raw '{...}'` to match the corrected output.

Audit Directions

  • Other curl option builders in Resource.js
    Audit the same Copy-as-cURL generator for any other option whose argument curl parses specially even when quoted; grep the file for ‘command.push(’ and every ‘–’ curl flag (e.g. --data, -d, --form/-F, --header, --url, --config) and check whether an attacker-controlled leading ‘@’, ‘-’, or ‘=’ could trigger file reads or config loading.
  • escapeStringPosix reliance across WebInspectorUI
    Search the WebInspectorUI tree for callers of escapeStringPosix and any command/serialization export (Copy as fetch, Copy as PowerShell, HAR export) that assume shell quoting alone sanitizes values; verify each downstream consumer does not itself interpret sigils like ‘@’.
  • Cross-tool 'copy as command' features
    Look for analogous ‘copy as <tool>’ serializers elsewhere (grep for ‘–data-binary’, ‘as-curl’, ‘copyAs’, ’toCurl’) in WebKit and confirm none reintroduce the ‘@’-filename or ‘-’-stdin interpretation for attacker-controlled bodies or headers.

Original Bug Report

The reporter's bug is still restricted on the tracker.