Skip to content

Instantly share code, notes, and snippets.

@hazcod
Last active November 24, 2025 16:06
Show Gist options
  • Select an option

  • Save hazcod/19ab1ba169b4def000673c70beb0d84c to your computer and use it in GitHub Desktop.

Select an option

Save hazcod/19ab1ba169b4def000673c70beb0d84c to your computer and use it in GitHub Desktop.
Shai Hulud YARA rule
rule shai_hulud_malicious_npm_package_install {
meta:
author = "hazcod"
date = "2025-11-24"
description = "Detects the installation of malicious npm packages associated with the Shai-Hulud 2.0 supply chain attack campaign."
reference = "https://www.wiz.io/blog/shai-hulud-2-0-ongoing-supply-chain-attack"
tags = "SUPPLY_CHAIN, NPM, SHAI_HULUD, INITIAL_ACCESS"
tactic = "TA0001"
technique = "T1195" // T1195 (Supply Chain Compromise) is more specific than the provided T1190.
false_positives = "Legitimate installations of these packages if the versions are different from the compromised ones, though the rule is designed to be specific. If a package is cleaned and republished under the same version, this could trigger."
events:
// Event filter for process launch events
$proc.metadata.event_type = "PROCESS_LAUNCH"
// Identifies the npm process as the target
re.regex($proc.target.process.file.full_path, `/(npx|npm|npm\.cmd|npm\.exe)$`)
// Looks for the 'install' or 'i' command in the command line
re.regex($proc.target.process.command_line, `\s(install|i)\s`)
// Matches known malicious package names from the Shai-Hulud 2.0 campaign in the command line.
// For better performance and maintainability, consider moving this list to a Chronicle reference list.
$matched_package = re.capture($proc.target.process.command_line, `(?:\s|^)(@accordproject/concerto-analysis|@accordproject/concerto-metamodel|@accordproject/concerto-types|@accordproject/markdown-it-cicero|@asyncapi/studio|@ensdomains/address-encoder|@ensdomains/content-hash|@ensdomains/dnsprovejs|@ensdomains/ens-validation|@ensdomains/ensjs|@ensdomains/eth-ens-namehash|@posthog/agent|@posthog/ai|@posthog/cli|@posthog/clickhouse|@posthog/core|@posthog/hedgehog-mode|@posthog/icons|@posthog/lemon-ui|@posthog/nextjs-config|@posthog/nuxt|@posthog/piscina|@posthog/plugin-contrib|@posthog/react-rrweb-player|@posthog/rrdom|@posthog/rrweb|@posthog/rrweb-player|@posthog/rrweb-record|@posthog/rrweb-replay|@posthog/rrweb-snapshot|@posthog/rrweb-utils|@posthog/siphash|@posthog/wizard|@postman/aether-icons|@postman/csv-parse|@postman/node-keytar|@postman/tunnel-agent|@voiceflow/common|@zapier/ai-actions|@zapier/babel-preset-zapier|@zapier/browserslist-config-zapier|@zapier/secret-scrubber|blob-to-base64|cpu-instructions|crypto-addr-codec|enforce-branch-name|ethereum-ens|formik-error-focus|fuzzy-finder|gatsby-plugin-cname|get-them-args|kill-port|posthog-docusaurus|posthog-js|posthog-node|posthog-react-native|posthog-react-native-session-replay|react-hook-form-persist|react-native-email|react-native-google-maps-directions|react-native-phone-call|react-native-websocket|shell-exec|sort-by-distance|template-lib|tenacious-fetch|url-encode-decode|zapier-platform-cli|zapier-platform-core|zapier-platform-schema)(?:@|\s|$)`)
outcome:
// Risk score for the detection
$risk_score = 65
// The specific malicious package that was matched
$package_name = array_distinct($matched_package)
// Contextual information for investigation
$principal_hostname = array_distinct($proc.principal.hostname)
$principal_user_userid = array_distinct($proc.principal.user.userid)
$principal_process_command_line = array_distinct($proc.principal.process.command_line)
$target_process_command_line = array_distinct($proc.target.process.command_line)
$target_process_pid = array_distinct($proc.target.process.pid)
$target_process_file_full_path = array_distinct($proc.target.process.file.full_path)
condition:
$proc
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment