Reverse Shell Spawned From Vulnerable React Server Components
Query
let Lookback = 70m;
let CorrelationWindow = 5m;
let ServerDeviceIds =
DeviceInfo
| summarize arg_max(Timestamp, DeviceType) by DeviceId
| where DeviceType =~ "Server"
| project DeviceId;
let RscServerDeviceIds =
DeviceTvmSoftwareInventory
| where DeviceId in (ServerDeviceIds)
| extend PackageName = replace_string(tolower(SoftwareName), "_", "-")
| where PackageName in (
"react-server-dom-webpack",
"react-server-dom-parcel",
"react-server-dom-turbopack"
)
| where SoftwareVersion matches regex @"^19\.[0-3]\.[0-9]+$"
| extend Version = parse_version(SoftwareVersion)
| where
Version between (parse_version("19.0.0") .. parse_version("19.0.8"))
or Version between (parse_version("19.1.0") .. parse_version("19.1.9"))
or Version between (parse_version("19.2.0") .. parse_version("19.2.8"))
or Version == parse_version("19.3.0")
| distinct DeviceId;
let Shells =
DeviceProcessEvents
| where Timestamp >= ago(Lookback)
| where DeviceId in (RscServerDeviceIds)
| where ActionType == "ProcessCreated"
| where InitiatingProcessFileName in~ ("node", "node.exe")
| where FileName in~ (
"sh", "bash", "dash", "ash", "zsh", "ksh",
"cmd.exe", "powershell.exe", "pwsh.exe", "pwsh"
)
| extend Cmd = tolower(ProcessCommandLine)
| extend
DownloadPipeToShell =
Cmd matches regex
@"\b(curl|wget)\b[^|;\r\n]*\|\s*(/[^\s|;]+/)?(bash|sh|dash|ash|zsh|ksh)\b",
BashReverseShell =
Cmd contains "/dev/tcp/"
and Cmd matches regex @"[012]?[<>]&[012]",
PowerShellDownloadExecute =
Cmd matches regex @"\b(iex|invoke-expression)\b"
and (
Cmd contains "downloadstring"
or Cmd contains "downloadfile"
or Cmd contains "invoke-webrequest"
or Cmd contains "invoke-restmethod"
),
PowerShellReverseShell =
Cmd contains "net.sockets.tcpclient"
and Cmd contains "getstream"
| where DownloadPipeToShell
or BashReverseShell
or PowerShellDownloadExecute
or PowerShellReverseShell
| extend DetectionReason = case(
BashReverseShell, "Node -> shell: /dev/tcp reverse-shell pattern",
PowerShellReverseShell, "Node -> PowerShell: TCP reverse-shell pattern",
DownloadPipeToShell, "Node -> shell: download piped into interpreter",
"Node -> PowerShell: download and Invoke-Expression"
)
| project
Timestamp,
DeviceId,
DeviceName,
ReportId,
AccountName,
ShellPid = ProcessId,
ShellStart = ProcessCreationTime,
ShellCommandLine = ProcessCommandLine,
NodeCommandLine = InitiatingProcessCommandLine,
DetectionReason;
let Egress = materialize(
DeviceNetworkEvents
| where Timestamp >= ago(Lookback)
| where DeviceId in (RscServerDeviceIds)
| where ActionType == "ConnectionSuccess"
| where RemoteIPType == "Public"
| project
DeviceId,
NetworkTime = Timestamp,
RemoteIP,
RemotePort,
RemoteUrl,
InitiatingProcessId,
InitiatingProcessCreationTime,
InitiatingProcessParentId,
InitiatingProcessParentCreationTime
);
let EgressByProcess =
union
(
Egress
| project
DeviceId,
NetworkTime,
RemoteIP,
RemotePort,
RemoteUrl,
MatchPid = InitiatingProcessId,
MatchStart = InitiatingProcessCreationTime
),
(
Egress
| project
DeviceId,
NetworkTime,
RemoteIP,
RemotePort,
RemoteUrl,
MatchPid = InitiatingProcessParentId,
MatchStart = InitiatingProcessParentCreationTime
);
Shells
| join kind=inner EgressByProcess on
DeviceId,
$left.ShellPid == $right.MatchPid,
$left.ShellStart == $right.MatchStart
| where NetworkTime >= ShellStart
and NetworkTime <= (ShellStart + CorrelationWindow)
| summarize
FirstNetworkTime = min(NetworkTime),
RemoteIPs = make_set(RemoteIP, 20),
RemotePorts = make_set(RemotePort, 20),
RemoteUrls = make_set(RemoteUrl, 20)
by
Timestamp,
DeviceId,
DeviceName,
ReportId,
AccountName,
ShellPid,
ShellStart,
NodeCommandLine,
ShellCommandLine,
DetectionReasonAbout this query
Explanation
This query is designed to detect potential security threats on Node.js servers running vulnerable versions of React Server Components. Here's a simplified breakdown of what the query does:
-
Identify Target Servers: It first identifies devices classified as servers that have specific vulnerable React Server Component packages installed. These packages are within certain version ranges known to be vulnerable.
-
Monitor for Suspicious Activity: The query then looks for processes on these servers that might indicate a reverse shell attack. It checks for shell or interpreter processes (like bash, PowerShell, etc.) that were started by a Node.js process within the last 70 minutes.
-
Pattern Matching: It examines the command lines of these processes for patterns typical of malicious activity, such as:
- Using
curlorwgetto download a script and pipe it into a shell. - Bash-style reverse shells using TCP.
- PowerShell commands that download and execute scripts.
- PowerShell reverse shells using TCP connections.
- Using
-
Correlate with Network Activity: For each suspicious shell process, the query checks for any outbound network connections to public IP addresses that occurred within five minutes of the shell process starting. This helps confirm whether the suspicious process is part of an actual attack.
-
Output: If a match is found, the query outputs details about the suspicious command lines, along with the remote IPs, ports, and URLs involved. This information helps analysts confirm the exploitation and take action to contain it.
-
Tuning and Adjustments: The query includes notes on how to adjust the time windows for different environments and suggests reviewing version ranges when new vulnerabilities are disclosed. It also recommends allowlisting known internal IPs to reduce false positives.
Overall, this query helps security teams detect and respond to potential reverse shell attacks on vulnerable Node.js servers by correlating process and network activity.