Skip to content

Remove obfuscated RCE payload from jest.config.js and restore .gitignore - #7

Draft
kevpay wants to merge 1 commit into
mainfrom
claude/citizenwallet-malicious-cleanup-bubc6e
Draft

Remove obfuscated RCE payload from jest.config.js and restore .gitignore#7
kevpay wants to merge 1 commit into
mainfrom
claude/citizenwallet-malicious-cleanup-bubc6e

Conversation

@kevpay

@kevpay kevpay commented Aug 28, 2026

Copy link
Copy Markdown

Summary

jest.config.js carried an obfuscated JavaScript payload appended after the legitimate config, hidden behind a long run of spaces on the closing line so it sits off-screen in an editor. .gitignore was rewritten by the same activity to delete the .env entry and add config.bat.

What the payload does

  • Resolves its C2 endpoint from the Ethereum blockchain (EtherHiding), reading the host from recent transactions of a hardcoded attacker-controlled address via public RPC endpoints and a Blockscout txlist API.
  • Fetches a second stage over HTTP(S) and runs it two ways: eval() in-process, and a detached spawn(node, ['-e', ...]) with stdio: 'ignore' and windowsHide, unref()'d so it outlives the parent.

jest.config.js executes on every jest invocation, so any npm test would run it.

Provenance — a same-day reinfection

The payload arrived in 19cba20 ("don't display address twice", 2025-11-14). On 2026-05-27, 2e7b59d ("remove polin rider") successfully removed it — the file drops to a clean 614 bytes. Later the same day, 6aef8c5 ("Update .gitignore") put a newer, blockchain-C2 variant back, taking the file to 30,567 bytes.

A clean removal followed by reinfection hours later, from the same author, points at the committing machine rather than CI. File-level cleanup will not hold until that is dealt with.

Scope note

This PR cleans the current tip only; the payload remains reachable in history. Ten other org repositories carry the same campaign, and any credentials reachable from an affected machine or CI run should be treated as compromised.


Generated by Claude Code

jest.config.js carried an obfuscated JavaScript payload appended after the legitimate
config, hidden behind a long run of spaces on the closing line so it sits
off-screen in an editor. Restored to its pre-tampering content.

What the payload does:
- Resolves its C2 endpoint from the Ethereum blockchain (EtherHiding), reading
  the host from recent transactions of a hardcoded attacker-controlled address
  via public RPC endpoints and a Blockscout txlist API.
- Fetches a second stage over HTTP(S) and runs it two ways: eval() in-process,
  and a detached spawn(node, ['-e', ...]) with stdio 'ignore' and windowsHide,
  unref'd so it outlives the parent.

jest.config.js executes_on_every_jest_invocation, so this ran on developer
machines and in CI.

.gitignore was rewritten by the same commit: line endings converted, the .env
entry deleted, and a config.bat entry added. Removing .env from .gitignore
stages local secrets to become committable. Restored to its pre-tampering
revision.

The payload first arrived in 19cba20_("don't_display_address_twice",_2025-11-14);_2e7b59d_removed_it_on_2026-05-27_and_6aef8c5_reintroduced_it_the_same_day.
@kevpay
kevpay force-pushed the claude/citizenwallet-malicious-cleanup-bubc6e branch from b7185d5 to eebd227 Compare September 1, 2026 09:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants