T1204.005 Microsoft Sentinel · KQL

Detect Malicious Library in Microsoft Sentinel

Adversaries may rely on a user installing a malicious library to facilitate execution. Threat actors upload malware to package managers such as NPM and PyPI, or backdoor existing popular libraries through supply chain compromise. Users install these libraries without realizing they are malicious, bypassing initial access controls. Execution occurs via setup.py install-time scripts (Python), postinstall/preinstall lifecycle hooks (NPM/yarn), or malicious code embedded in library modules that executes on import. Common delivery vectors include typosquatting (e.g., 'reqeusts' vs 'requests'), dependency confusion attacks, compromised maintainer accounts, and first-use namespace squatting. Threat actors including Contagious Interview have leveraged malicious NPM and Python packages published to public registries to deliver infostealers, remote access tools, and BeaverTail/InvisibleFerret malware targeting software developers.

MITRE ATT&CK

Tactic
Execution
Technique
T1204 User Execution
Sub-technique
T1204.005 Malicious Library
Canonical reference
https://attack.mitre.org/techniques/T1204/005/

KQL Detection Query

Microsoft Sentinel (KQL)
kusto
let TrustedPackageHosts = dynamic(["pypi.org", "files.pythonhosted.org", "npmjs.com", "registry.npmjs.org", "github.com", "raw.githubusercontent.com", "yarnpkg.com", "registry.yarnpkg.com", "anaconda.com", "conda.anaconda.org", "rubygems.org", "pkg.go.dev"]);
let SuspiciousChildProcs = dynamic(["cmd.exe", "powershell.exe", "pwsh.exe", "mshta.exe", "rundll32.exe", "certutil.exe", "bitsadmin.exe", "wscript.exe", "cscript.exe", "regsvr32.exe", "msiexec.exe", "schtasks.exe", "at.exe", "sc.exe", "reg.exe", "net.exe", "netsh.exe"]);
let PackageRuntimes = dynamic(["python.exe", "python3.exe", "node.exe", "pip.exe", "pip3.exe"]);
// Branch 1: Package manager or Python/Node spawning suspicious child processes (setup.py/postinstall hook abuse)
let Branch1 = DeviceProcessEvents
| where Timestamp > ago(24h)
| where (InitiatingProcessFileName has_any (PackageRuntimes)
        or InitiatingProcessCommandLine has_any ("pip install", "pip3 install", "npm install", "npm ci", "yarn add", "setup.py install", "setup.py build", "python setup.py"))
| where FileName has_any (SuspiciousChildProcs)
| extend DetectionBranch = "PackageInstallSpawnedSuspiciousProcess"
| project Timestamp, DeviceName, AccountName, DetectionBranch,
          SubjectProcess = FileName, SubjectCmdLine = ProcessCommandLine,
          ParentProcess = InitiatingProcessFileName, ParentCmdLine = InitiatingProcessCommandLine;
// Branch 2: Python or Node making external connections on non-HTTP ports (C2 callback from malicious library code)
let Branch2 = DeviceNetworkEvents
| where Timestamp > ago(24h)
| where InitiatingProcessFileName in~ ("python.exe", "python3.exe", "node.exe")
| where RemoteIPType == "Public"
| where not(RemoteUrl has_any (TrustedPackageHosts))
| where RemotePort !in (80, 443, 8080, 8443)
| extend DetectionBranch = "MaliciousLibraryC2Callback"
| project Timestamp, DeviceName,
          AccountName = InitiatingProcessAccountName, DetectionBranch,
          SubjectProcess = strcat(InitiatingProcessFileName, " -> ", tostring(RemoteIP), ":", tostring(RemotePort)),
          SubjectCmdLine = InitiatingProcessCommandLine,
          ParentProcess = InitiatingProcessParentFileName,
          ParentCmdLine = "";
// Branch 3: Package installers dropping executable or script files in persistence-relevant locations
let Branch3 = DeviceFileEvents
| where Timestamp > ago(24h)
| where InitiatingProcessFileName has_any (PackageRuntimes)
        or InitiatingProcessParentFileName has_any (PackageRuntimes)
| where FileName endswith ".exe" or FileName endswith ".dll"
        or FileName endswith ".bat" or FileName endswith ".ps1"
        or FileName endswith ".vbs" or FileName endswith ".scr"
| where FolderPath has_any ("\\AppData\\Roaming\\Microsoft\\Windows\\Start Menu\\Programs\\Startup",
                             "C:\\Windows\\Temp\\", "\\AppData\\Local\\Temp\\",
                             "C:\\Windows\\System32\\", "C:\\Windows\\SysWOW64\\",
                             "\\ProgramData\\")
| extend DetectionBranch = "PackageInstallerDroppedExecutable"
| project Timestamp, DeviceName,
          AccountName = InitiatingProcessAccountName, DetectionBranch,
          SubjectProcess = strcat(FileName, " in ", FolderPath),
          SubjectCmdLine = InitiatingProcessCommandLine,
          ParentProcess = InitiatingProcessFileName,
          ParentCmdLine = InitiatingProcessCommandLine;
Branch1
| union Branch2
| union Branch3
| sort by Timestamp desc
high severity medium confidence

Detects malicious library execution across three branches: (1) package managers or Python/Node processes spawning known LOLBins as child processes via setup.py or NPM postinstall hooks; (2) Python or Node runtimes making outbound network connections to non-package-registry hosts on non-HTTP/HTTPS ports, indicating a C2 callback from malicious library code; (3) package installer processes dropping executable or script files into persistence-relevant system locations. All three branches target different phases of malicious library execution from install-time through runtime.

Data Sources

Process: Process CreationNetwork Traffic: Network Connection CreationFile: File CreationMicrosoft Defender for Endpoint

Required Tables

DeviceProcessEventsDeviceNetworkEventsDeviceFileEvents

False Positives & Tuning

  • Legitimate Python packages with compiled extensions (e.g., numpy, scipy, cryptography) invoke MSVC toolchain processes (cl.exe, link.exe) during build — these are not LOLBins but may cause noise if broad parent-process filters are used
  • NPM packages with native addons use node-gyp, which spawns cmd.exe and Python — filter by known build tools in the postinstall command line
  • Developer environments where Python or Node scripts make legitimate API calls to internal services on non-standard ports — baseline expected outbound destinations and ports for developer workstations
  • CI/CD pipeline agents (Jenkins, GitHub Actions self-hosted runners) routinely run pip/npm installs that produce process trees with build tooling — apply host-based allowlists for known build agent hostnames
  • Package manager updates of setuptools or pip itself may write executables to site-packages — the FolderPath filter excludes site-packages paths but confirm in your environment
Download portable Sigma rule (.yml)

Other platforms for T1204.005


Testing Methodology

Validate this detection against 5 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 1Malicious Python Package via Local pip install (setup.py subprocess spawn)

    Expected signal: Sysmon Event ID 1: Process Create with ParentImage=python.exe (pip runner), Image=cmd.exe, CommandLine containing 'whoami'. DeviceProcessEvents: InitiatingProcessFileName=python.exe with CommandLine containing 'setup.py', FileName=cmd.exe. The process tree root will be pip.exe -> python.exe -> cmd.exe.

  2. Test 2Malicious NPM Package with Postinstall Hook (Linux/macOS)

    Expected signal: Sysmon for Linux or auditd: process creation with parent=node (npm runner), child=sh, CommandLine containing 'id > /tmp/npm_postinstall_test.txt'. If Sysmon for Linux is deployed: EventID=1, ParentImage path contains node, Image=/bin/sh. Linux audit log: execve syscall with parent node process.

  3. Test 3Python Library C2 Callback Simulation (Non-Standard Port)

    Expected signal: Sysmon Event ID 3: Network Connection with Image=python.exe, DestinationIp=127.0.0.1, DestinationPort=4444. DeviceNetworkEvents: InitiatingProcessFileName=python.exe, RemotePort=4444. The connection will fail with ECONNREFUSED but the outbound attempt is still logged.

  4. Test 4Malicious Python Library Dropping Persistence Script to Startup Folder

    Expected signal: Sysmon Event ID 11: File Create with Image=python.exe, TargetFilename=%APPDATA%\Microsoft\Windows\Start Menu\Programs\Startup\df00tech_test_persistence.vbs. DeviceFileEvents: InitiatingProcessFileName=python.exe, FileName=df00tech_test_persistence.vbs, FolderPath contains Startup.

  5. Test 5Simulated Typosquat Package Python Credential Harvester

    Expected signal: Sysmon Event ID 15 (FileCreateStreamHash) or Event ID 11 if file is created. DeviceFileEvents: InitiatingProcessFileName=python.exe accessing FolderPath containing 'Google\Chrome\User Data'. This also fires the hunting query for credential store access.

Unlock Pro Content

Get the full detection package for T1204.005 including response playbook, investigation guide, and atomic red team tests.

Response PlaybookInvestigation GuideHunting QueriesAtomic Red Team TestsTuning Guidance

Related Detections