Malicious Library (MITRE ATT&CK T1204.005) is a sub-technique of user execution. The adversary relies on a user installing a malicious library. MITRE files it under Execution and lists Linux, Windows and macOS.
The word "user" here is usually a developer or a build system. The trigger is a command they run every day.
How attackers use it
MITRE says threat actors may upload malware to package managers such as npm and PyPI, and to public code repositories such as GitHub. A user may install a library without realizing it is malicious. That bypasses techniques that specifically aim at initial access, because the victim brought the code in on their own.
The result can be execution of malicious code that sets up persistence, steals data or mines cryptocurrency.
MITRE describes two ways the library gets in front of people:
- Backdooring a popular library. Threat actors compromise an existing popular library. That is covered by compromise of software dependencies and development tools (T1195.001).
- Creating new packages. Attackers publish entirely new packages and use behaviors such as typosquatting to encourage installs.
The MITRE page cites a campaign in which users were relied upon to install a malicious library from a code repository, which led to more payloads being delivered and sensitive data being stolen.
How to detect it
MITRE's detection strategy is about what happens after the install command.
Linux. A non-root user runs pip install, npm install or another language library install. Shortly afterward, new .py, .sh or .js files appear in hidden directories, or interpreters run during boot or login. Unexpected network connections, credential access or startup file changes follow.
Windows. pip.exe, npm.cmd or MSI installers run in user context. Then a script interpreter starts (python.exe, for example), or PowerShell starts with unusual children, or files are written in %APPDATA%, %TEMP% or %LOCALAPPDATA%. Correlate command-line install tools with Sysmon and Windows event logs to trace what came next.
macOS. Homebrew, pip3, npm or a manually downloaded PKG runs from Terminal or a shell. Then startup agents are created, interpreters spawn, unsigned binaries launch, or python3 or node connect to remote endpoints. Terminal commands link to plist creation and new outbound connections.
A few signals deserve their own alert:
- A package install whose child process is a shell, curl or PowerShell
- New launch agents, scheduled tasks, cron entries or startup items created within minutes of an install
- Outbound connections from an install step to a host that is not your registry
- A package manager process reading credential files, such as tokens in a user's configuration
On developer machines and CI runners, install time is the best moment to catch this, since many malicious packages run code through install lifecycle scripts.
How to reduce the risk
From MITRE's mitigations:
- Limit software installation. Where possible, require developers to pull from internal repositories of verified and approved packages instead of external ones.
- Use network intrusion prevention and systems that scan and remove malicious downloads to block activity.
- Train developers to know that malicious libraries exist and how to avoid installing them.
Practical steps that follow from those: put a proxy or internal mirror in front of public registries, review new dependencies before they are added, and keep CI runners in a network segment that cannot reach arbitrary hosts.
Check your own environment
- Do developers install straight from public registries, or through a mirror you control?
- Does anyone review a new dependency before it lands in a lockfile?
- Can a CI job reach any host on the internet during install?
- Do CI jobs and developer laptops hold long-lived tokens that an install script could read?
- Can you list every package installed across your repositories this month, and who added it?
For a first look at a project, the free in-browser dependency scan at gold.safeguard.sh/scan needs no login, keeps your file on your machine and covers up to 100 dependencies.
A read-only way to see what a Node project would run during install is to look at the lifecycle scripts declared by your dependencies. On macOS or Linux, this lists packages in node_modules that declare an install-time script:
grep -l -E '"(preinstall|install|postinstall)"' node_modules/*/package.json
Read each hit before deciding whether it needs to stay. Scoped packages sit one level deeper, so repeat with node_modules/@//package.json.
Try it yourself
This demo shows how malicious packages and look-alike names are told apart from the real thing.
Click through it at your own pace, or open it full screen.
Quick check
Why does this technique bypass controls aimed at initial access?
The user installs the code themselves, so there is no separate break-in. The malicious library arrives through a normal developer action.
What two ways does MITRE give for getting a malicious library in front of users?
Compromising and backdooring an existing popular library, or publishing entirely new packages and using tricks such as typosquatting to get them installed.
Which events after an install should raise an alert?
New files in hidden directories, interpreter or shell children of the package manager, startup items created soon after, and new network connections to unfamiliar hosts.