Why Does an NPM Math Library Need an Encrypted Loader?
A seemingly innocuous npm math library, mathmain, was found to contain a sophisticated, encrypted remote access implant. The malicious payload remained dormant until a specific mathematical input to the library's solver function served as the decryption key. This clever supply chain attack highlights the ongoing challenge of securing software dependencies, as the code was injected during package publication rather than being present in public source repositories.
The Lowdown
Security researchers at SafeDep uncovered a highly sophisticated remote access implant hidden within mathmain@1.0.0, an npm package that mimicked the popular mathjs library. The attack vector was particularly ingenious, leveraging a specific mathematical operation within the library itself to decrypt and activate the malware. This post details the discovery, decryption, and capabilities of this hidden threat.
- The implant was found as obfuscated code, with an extra function call to
removeSolveValidation()inserted into thelusolve()function, which then invokedisGraph(). isGraph()was responsible for decrypting a hidden payload. It used the JSON string representation of the lower triangular matrix (L) resulting from thelusolve()operation as the decryption password.- The encryption scheme involved
scryptfor key derivation and AES-GCM for data decryption. The decrypted file was written to disk and then loaded usingrequire(). - The specific trigger matrix identified was a 3x3 Pascal matrix
[[1, 1, 1], [1, 2, 3], [1, 3, 6]], whose LU decomposition's lower factorLyielded the correct password uponJSON.stringify(). - Similar encrypted loaders were discovered in two other npm packages,
mathsbaseandmath-universe, across multiple versions. - Crucially, the malicious code was not present in the linked public GitHub repositories for these packages, indicating it was injected during the npm package publication process.
- Payload analysis revealed a remote access implant capable of reading host data, generating cryptographic keys, running shell commands, and communicating via various channels.
- Command and control (C2) channels included Slack
chat.postMessage,api.telegram.org, and a smart contract on the Base Sepolia blockchain test network. - The
fraction.jscomponent of the payload acted as a command agent, polling Slack for operator commands and executing them on the compromised host. - Despite the sophisticated trigger, no public code or projects were found that explicitly called the solver with the required input matrix.
This incident serves as a stark reminder of the evolving complexity of software supply chain attacks, where even mathematical utility libraries can harbor deeply embedded and conditionally activated malware, making detection incredibly challenging.