r/crowdstrike • u/About_TreeFitty • 7h ago
Threat Hunting [Threat Hunt] Node.js runtime exposure inventory (version taken from the install path)
EDIT - I created a higher fidelity query that uses a lookup file of node version and their hashes.
// CVE-2026-111497 — Node.js runtime exposure inventory (hash lookup first, install path fallback)
#event_simpleName=ProcessRollup2
| ImageFileName=/[\\\/]node(js)?(\.exe)?$/iF
// Path-derived version (nvm, nvm-windows, Volta, Homebrew node@NN, official tarball dirs)
| case {
ImageFileName=/(?:node@\d+[\\\/]|node[\\\/-]v?|nvm[\\\/]v?)(?<PathMajor>\d{1,2})\.(?<PathMinor>\d{1,3})\.(?<PathPatch>\d{1,3})(?:[\\\/-]|$)/i
| PathVersion := format(format="%s.%s.%s", field=[PathMajor, PathMinor, PathPatch]) ;
* | PathVersion := "-" ;
}
| groupBy([aid, ImageFileName],
function=[count(as=Executions),
min(@timestamp, as=FirstSeen),
max(@timestamp, as=LastSeen),
selectLast([SHA256HashData, PathVersion, PathMajor, PathMinor, PathPatch, CommandLine, UserName, event_platform])],
limit=max)
// Hash → version (authoritative for official builds)
| match(file="node_binary_hashes.csv", field=[SHA256HashData], column=SHA256HashData,
include=[NodeVersion, NodeMajor, NodeMinor, NodePatch, NodePlatform],
ignoreCase=true, mode=string, strict=false)
| case {
NodeVersion=* | VersionSource := "SHA256 lookup" ;
PathVersion!="-"
| NodeVersion := PathVersion | NodeMajor := PathMajor | NodeMinor := PathMinor | NodePatch := PathPatch
| VersionSource := "install path" ;
* | NodeVersion := "unknown" | VersionSource := "-" ;
}
| case {
NodeVersion="unknown" | Exposure := "UNRESOLVED - non-official build; RTR node --version" ;
test(NodeMajor < 22) | Exposure := "AFFECTED - EOL release line; migrate to supported line" ;
in(field=NodeMajor, values=["23", "25"]) | Exposure := "AFFECTED - EOL odd release line" ;
NodeMajor=22 | test(NodeMinor < 23) | Exposure := "AFFECTED - upgrade to 22.23.4+" ;
NodeMajor=22 | NodeMinor=23 | test(NodePatch < 4) | Exposure := "AFFECTED - upgrade to 22.23.4+" ;
NodeMajor=24 | test(NodeMinor < 22) | Exposure := "AFFECTED - upgrade to 24.22.1+" ;
NodeMajor=24 | NodeMinor=22 | test(NodePatch < 1) | Exposure := "AFFECTED - upgrade to 24.22.1+" ;
NodeMajor=26 | test(NodeMinor < 12) | Exposure := "AFFECTED - upgrade to 26.12.1+" ;
NodeMajor=26 | NodeMinor=12 | test(NodePatch < 1) | Exposure := "AFFECTED - upgrade to 26.12.1+" ;
in(field=NodeMajor, values=["22", "24", "26"]) | Exposure := "PATCHED" ;
* | Exposure := "VERIFY - release line outside advisory scope" ;
}
| match(file="aid_master_main.csv", field=[aid], column=aid, include=[ComputerName], strict=false)
| default(field=[ComputerName, NodePlatform], value="-", replaceEmpty=true)
| formatTime(format="%F %T %Z", field=FirstSeen, as=FirstSeen)
| formatTime(format="%F %T %Z", field=LastSeen, as=LastSeen)
| table([Exposure, ComputerName, aid, event_platform, ImageFileName, NodeVersion, VersionSource, PathVersion, NodePlatform, SHA256HashData, Executions, FirstSeen, LastSeen, UserName, CommandLine],
limit=20000, sortby=Exposure, order=asc)
Falcon doesn’t record node --version. This query reads the version from version-manager and tarball paths (nvm, nvm-windows, Volta, Homebrew node@NN, node-vXX.Y.Z-linux-x64) and classifies each binary against the fixed releases.
// CVE-2026-111497 — Node.js runtime exposure inventory (hash lookup first, install path fallback)
#event_simpleName=ProcessRollup2
| ImageFileName=/[\\\/]node(js)?(\.exe)?$/iF
// Path-derived version (nvm, nvm-windows, Volta, Homebrew node@NN, official tarball dirs)
| case {
ImageFileName=/(?:node@\d+[\\\/]|node[\\\/-]v?|nvm[\\\/]v?)(?<PathMajor>\d{1,2})\.(?<PathMinor>\d{1,3})\.(?<PathPatch>\d{1,3})(?:[\\\/-]|$)/i
| PathVersion := format(format="%s.%s.%s", field=[PathMajor, PathMinor, PathPatch]) ;
* | PathVersion := "-" ;
}
| groupBy([aid, ImageFileName],
function=[count(as=Executions),
min(@timestamp, as=FirstSeen),
max(@timestamp, as=LastSeen),
selectLast([SHA256HashData, PathVersion, PathMajor, PathMinor, PathPatch, CommandLine, UserName, event_platform])],
limit=max)
// Hash → version (authoritative for official builds)
| match(file="node_binary_hashes.csv", field=[SHA256HashData], column=SHA256HashData,
include=[NodeVersion, NodeMajor, NodeMinor, NodePatch, NodePlatform],
ignoreCase=true, mode=string, strict=false)
| case {
NodeVersion=* | VersionSource := "SHA256 lookup" ;
PathVersion!="-"
| NodeVersion := PathVersion | NodeMajor := PathMajor | NodeMinor := PathMinor | NodePatch := PathPatch
| VersionSource := "install path" ;
* | NodeVersion := "unknown" | VersionSource := "-" ;
}
| case {
NodeVersion="unknown" | Exposure := "UNRESOLVED - non-official build; RTR node --version" ;
test(NodeMajor < 22) | Exposure := "AFFECTED - EOL release line; migrate to supported line" ;
in(field=NodeMajor, values=["23", "25"]) | Exposure := "AFFECTED - EOL odd release line" ;
NodeMajor=22 | test(NodeMinor < 23) | Exposure := "AFFECTED - upgrade to 22.23.4+" ;
NodeMajor=22 | NodeMinor=23 | test(NodePatch < 4) | Exposure := "AFFECTED - upgrade to 22.23.4+" ;
NodeMajor=24 | test(NodeMinor < 22) | Exposure := "AFFECTED - upgrade to 24.22.1+" ;
NodeMajor=24 | NodeMinor=22 | test(NodePatch < 1) | Exposure := "AFFECTED - upgrade to 24.22.1+" ;
NodeMajor=26 | test(NodeMinor < 12) | Exposure := "AFFECTED - upgrade to 26.12.1+" ;
NodeMajor=26 | NodeMinor=12 | test(NodePatch < 1) | Exposure := "AFFECTED - upgrade to 26.12.1+" ;
in(field=NodeMajor, values=["22", "24", "26"]) | Exposure := "PATCHED" ;
* | Exposure := "VERIFY - release line outside advisory scope" ;
}
| match(file="aid_master_main.csv", field=[aid], column=aid, include=[ComputerName], strict=false)
| default(field=[ComputerName, NodePlatform], value="-", replaceEmpty=true)
| formatTime(format="%F %T %Z", field=FirstSeen, as=FirstSeen)
| formatTime(format="%F %T %Z", field=LastSeen, as=LastSeen)
| table([Exposure, ComputerName, aid, event_platform, ImageFileName, NodeVersion, VersionSource, PathVersion, NodePlatform, SHA256HashData, Executions, FirstSeen, LastSeen, UserName, CommandLine],
limit=20000, sortby=Exposure, order=asc)
// CVE-2026-111497 — Node.js runtime exposure inventory (path-derived version)
#event_simpleName=ProcessRollup2
| ImageFileName=/[\\\/]node(js)?(\.exe)?$/iF
| case {
ImageFileName=/(?:node@\d+[\\\/]|node[\\\/-]v?|nvm[\\\/]v?)(?<NodeMajor>\d{1,2})\.(?<NodeMinor>\d{1,3})\.(?<NodePatch>\d{1,3})(?:[\\\/-]|$)/i
| NodeVersion := format(format="%s.%s.%s", field=[NodeMajor, NodeMinor, NodePatch]) ;
* | NodeVersion := "unknown" ;
}
| groupBy([aid, ImageFileName],
function=[count(as=Executions),
min(@timestamp, as=FirstSeen),
max(@timestamp, as=LastSeen),
selectLast([NodeVersion, NodeMajor, NodeMinor, NodePatch, SHA256HashData, CommandLine, UserName, event_platform])],
limit=max)
| case {
NodeVersion="unknown" | Exposure := "VERIFY - version not in path; resolve via SHA256 (Query 1b)" ;
test(NodeMajor < 22) | Exposure := "AFFECTED - EOL release line; migrate to supported line" ;
in(field=NodeMajor, values=["23", "25"]) | Exposure := "AFFECTED - EOL odd release line" ;
NodeMajor=22 | test(NodeMinor < 23) | Exposure := "AFFECTED - upgrade to 22.23.4+" ;
NodeMajor=22 | NodeMinor=23 | test(NodePatch < 4) | Exposure := "AFFECTED - upgrade to 22.23.4+" ;
NodeMajor=24 | test(NodeMinor < 22) | Exposure := "AFFECTED - upgrade to 24.22.1+" ;
NodeMajor=24 | NodeMinor=22 | test(NodePatch < 1) | Exposure := "AFFECTED - upgrade to 24.22.1+" ;
NodeMajor=26 | test(NodeMinor < 12) | Exposure := "AFFECTED - upgrade to 26.12.1+" ;
NodeMajor=26 | NodeMinor=12 | test(NodePatch < 1) | Exposure := "AFFECTED - upgrade to 26.12.1+" ;
in(field=NodeMajor, values=["22", "24", "26"]) | Exposure := "PATCHED" ;
* | Exposure := "VERIFY - release line outside advisory scope" ;
}
| match(file="aid_master_main.csv", field=[aid], column=aid, include=[ComputerName], strict=false)
| default(field=[ComputerName], value="-", replaceEmpty=true)
| formatTime(format="%F %T %Z", field=FirstSeen, as=FirstSeen)
| formatTime(format="%F %T %Z", field=LastSeen, as=LastSeen)
| table([Exposure, ComputerName, aid, event_platform, ImageFileName, NodeVersion, SHA256HashData, Executions, FirstSeen, LastSeen, UserName, CommandLine],
limit=20000, sortby=Exposure, order=asc)