Compromise Software Dependencies and Development Tools
Adversaries manipulate software dependencies and development tools prior to receipt by a final consumer to compromise data or systems. This includes injecting malicious code into popular open source packages (npm, PyPI, RubyGems), registering typosquatted or abandoned package names, and poisoning CI/CD pipeline components such as GitHub Actions. Malicious packages commonly use preinstall/postinstall lifecycle hooks to execute arbitrary OS commands at install time, enabling immediate credential theft, reverse shell establishment, or persistent implant deployment. Detection focuses on package manager processes spawning unexpected child processes, outbound network connections from package manager child processes, CI/CD workflow file modifications, and installation from non-standard or suspicious registries.
// T1195.001 — Compromised Software Dependencies: package manager spawning suspicious children + non-standard registry connections
let PackageManagerProcs = dynamic(["npm", "npm.cmd", "npm.exe", "node", "node.exe", "pip", "pip3", "pip.exe", "pip3.exe", "python", "python3", "python.exe", "python3.exe", "pipx", "yarn", "pnpm", "gem", "bundle", "cargo", "go", "nuget", "dotnet"]);
let SuspiciousChildProcs = dynamic(["powershell.exe", "pwsh.exe", "cmd.exe", "wscript.exe", "cscript.exe", "mshta.exe", "rundll32.exe", "regsvr32.exe", "certutil.exe", "bitsadmin.exe", "curl.exe", "wget.exe"]);
let SuspiciousCommandPatterns = dynamic(["/dev/tcp", "base64 -d", "base64 --decode", "bash -i", "sh -i", "nc -e", "ncat -e", "python -c", "perl -e", "ruby -e", "DownloadString", "DownloadFile", "Invoke-Expression", "IEX(", "curl http", "wget http", "--registry http://", "--registry https://", "postinstall", "preinstall"]);
// Arm 1: Package managers spawning suspicious OS-level processes (postinstall/preinstall hook abuse)
let PackageManagerChildProc =
DeviceProcessEvents
| where Timestamp > ago(24h)
| where InitiatingProcessFileName has_any (PackageManagerProcs)
| where FileName has_any (SuspiciousChildProcs)
or ProcessCommandLine has_any (SuspiciousCommandPatterns)
| extend DetectionArm = "PackageManagerSpawnedSuspiciousChild"
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine,
InitiatingProcessFileName, InitiatingProcessCommandLine,
InitiatingProcessFolderPath, FolderPath, DetectionArm;
// Arm 2: Network connections from package manager child processes to non-standard or public IPs
let PackageManagerNetworkAnomalies =
DeviceNetworkEvents
| where Timestamp > ago(24h)
| where InitiatingProcessFileName has_any (PackageManagerProcs)
| where RemoteIPType == "Public"
| where not (RemoteUrl has_any ("registry.npmjs.org", "pypi.org", "files.pythonhosted.org", "rubygems.org", "crates.io", "nuget.org", "pkg.go.dev", "proxy.golang.org", "yarnpkg.com", "registry.yarnpkg.com"))
| extend DetectionArm = "PackageManagerUnexpectedExternalConnection"
| project Timestamp, DeviceName, AccountName = InitiatingProcessAccountName,
FileName = InitiatingProcessFileName, ProcessCommandLine = InitiatingProcessCommandLine,
RemoteUrl, RemoteIP, RemotePort, RemoteIPType, DetectionArm;
// Arm 3: CI/CD workflow file modifications (GitHub Actions poisoning)
let CICDWorkflowModifications =
DeviceFileEvents
| where Timestamp > ago(24h)
| where FolderPath has_any (".github/workflows", ".gitlab-ci", "Jenkinsfile", ".circleci", ".travis", "azure-pipelines")
or FileName has_any ("Makefile", "CMakeLists.txt", "build.gradle", "pom.xml") and ActionType in ("FileModified", "FileCreated")
| where ActionType in ("FileModified", "FileCreated", "FileRenamed")
| extend DetectionArm = "CICDPipelineFileModified"
| project Timestamp, DeviceName, AccountName = InitiatingProcessAccountName,
FileName, FolderPath, ActionType,
InitiatingProcessFileName, InitiatingProcessCommandLine, DetectionArm;
// Union all arms
PackageManagerChildProc
| union PackageManagerNetworkAnomalies
| union CICDWorkflowModifications
| sort by Timestamp desc Data Sources
Required Tables
False Positives
- Legitimate npm packages with postinstall scripts that compile native binaries (node-gyp, esbuild, sharp) — these frequently spawn cmd.exe or bash
- Developer tools that legitimately use non-standard registries (Artifactory, Nexus, Azure Artifacts, private npm mirrors) for corporate package management
- CI/CD automation committing workflow file updates as part of normal GitOps or automated dependency update PRs (Dependabot, Renovate)
- Python packages with C extensions running compiler toolchains (gcc, cl.exe, link.exe) via setup.py during installation
- Security scanning tools (Snyk, FOSSA, Trivy) that inspect packages and may trigger file access patterns
References (12)
- https://attack.mitre.org/techniques/T1195/001/
- https://www.paloaltonetworks.com/blog/cloud-security/github-actions-worm-dependencies/
- https://unit42.paloaltonetworks.com/github-actions-supply-chain-attack
- https://checkmarx.com/zero-post/python-pypi-supply-chain-attack-colorama/
- https://thehackernews.com/2024/09/hackers-hijack-22000-removed-pypi.html
- https://owasp.org/www-project-top-10-ci-cd-security-risks/CICD-SEC-04-Poisoned-Pipeline-Execution
- https://hackread.com/backdoors-python-npm-packages-windows-linux/
- https://www.bitdefender.com/en-gb/blog/hotforsecurity/popular-npm-repositories-compromised-in-man-in-the-middle-attack
- https://www.trendmicro.com/vinfo/dk/security/news/cybercrime-and-digital-threats/hacker-infects-node-js-package-to-steal-from-bitcoin-wallets
- https://checkmarx.com/blog/new-technique-to-trick-developers-detected-in-an-open-source-supply-chain-attack/
- https://cyberpress.org/malicious-npm-and-pypi-packages-disguised-as-dev-tools
- https://github.com/redcanaryco/atomic-red-team/blob/master/atomics/T1195.001/T1195.001.md
Unlock Pro Content
Get the full detection package for T1195.001 including response playbook, investigation guide, and atomic red team tests.