Hiding in the Ether - Wallet Draining Phish
Good morning, readers! While poking around in inboxes and spam folders, I managed to dig up an interesting crypto wallet drainer that makes some interesting use of infrastructure. Grab a coffee, sit back, and let's take a look at this phish and how it leverages basic redirection tactics, IPFS, and even Ethereum Smart Contracts to deliver it's final landing page.
Email Pretext
The phishing email itself is spoofing Bittrex. In this case, the email is stating that an admistration service provider has been assigned to us so that we may claim our funds.

If you aren't aware, the reason that this pretext is even remotely believable is that Bittrex has notified users that they are shutting down services. Here's the notification available on their site:

Interesting Redirect Infrastructure
Before just jumping to the end and showing that this phish is designed to steal our seed phrases, let's take a look to see what's actually going on and how our session is handled. If you're more interested in the IoCs instead, you can find them at the bottom.
When we click the link, we are initially sent to an X/Twitter shortened URL:
- hxxps://t[.]co/Khmb9qWFUZ
This is the underlying html:
<head>
<noscript>
<meta
http-equiv="refresh"
content="0;URL=hxxps://ipfs[.]io/ipfs/QmXm7F2nGe72y5vqZiHWpJyDix6GETqzoRUePax1PiKc5K"
/>
</noscript>
<title>
hxxps://ipfs[.]io/ipfs/QmXm7F2nGe72y5vqZiHWpJyDix6GETqzoRUePax1PiKc5K
</title>
</head>
<script>
window[.]opener = null; location[.]replace("https:\/\/ipfs[.]io\/ipfs\/QmXm7F2nGe72y5vqZiHWpJyDix6GETqzoRUePax1PiKc5K")
</script>
The redirect is taking us to an IPFS url:
- hxxps://ipfs[.]io/ipfs/QmXm7F2nGe72y5vqZiHWpJyDix6GETqzoRUePax1PiKc5K
While the use of IPFS for phishing isn't overly novel, I really liked how the actors handled the next section, or how we get the final redirect to the phishing host. When loading the ipfs hosted content, we are presented with the following html:
<!DOCTYPE html>
<html lang="en-US">
<head>
<script>
fetch("hxxps://1rpc[.]io/eth", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON[.]stringify({ id: 0, jsonrpc: "2.0", method: "eth_call", params: [{ to: "0x0558aaa5290385322bb5af964bff7e90477d7636", data: "0x8a054ac2" }, "latest"] }) }).then(t => { t[.]json().then(t => { let e = "", a = t[.]result[.]slice(130); for (; a[.]length > 0;) { let o = String[.]fromCharCode(parseInt(a[.]substring(0, 2), 16)); if ("\0" == o) break; e = o, a = a[.]slice(2) } window[.]location = `hxxps://${e}` }) });
</script>
</head>
</html>
We can see that a fetch is happening to 1rpc[.]io/eth
. 1RPC is a blockchain relay, from their site, they describe themselves as:
1RPC is a relay that protects user privacy and keeps AI accountable with attested TEEs. A Trusted Execution Environment, or TEE, provides hardware-based isolation to ensure the confidentiality and integrity of computation that runs on it.
What does this mean for our phish? The code that's being run is fetching the content of an Ethereum smart contract. Specifically, 0x0558aaa5290385322bb5AF964bFF7e90477d7636
. We'll dive just a bit further into the contract in a bit. Once the contract is fetched, the code takes the result, decodes a portion of it, then puts the intended URL into a window.location for redirect.
I'll break this down:
1RPC response with the following:
{
jsonrpc: '2.0',
result: '0x00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000027636c69656e742e3739333231352d626974747265782e636f6d2f686f6d652f3f763d363332313400000000000000000000000000000000000000000000000000',
id: 0
}
Our code then takes the result field content and removes the first 130 characters. This is to remove some of the extra content within the Ethereum ABI.
// First 32 bytes are pointer, next 32 bytes are length, +2, equals a 130 character offset
let a = t.result.slice(130);
// results in 636c69656e742e3739333231352d626974747265782e636f6d2f686f6d652f3f763d363332313400000000000000000000000000000000000000000000000000
Now the code's loop can iterate and hex decode the following (it skips the nulls so I removed them):
636c69656e742e3739333231352d626974747265782e636f6d2f686f6d652f3f763d3633323134
This then results in the following decoded URL (oh hey, it's spoofing Bittrex):
client[.]793215-bittrex[.]com/home/?v=63214
Here's a full commented verion of the JavaScript for those interested:
// Makes a network request to an Ethereum RPC endpoint
fetch("https://1rpc.io/eth", {
// Specifies this is a POST request
method: "POST",
// Sets the request headers to indicate JSON content
headers: {
"Content-Type": "application/json"
},
// Constructs and stringifies the JSON-RPC payload
body: JSON.stringify({
// Request ID (standard in JSON-RPC)
id: 0,
// JSON-RPC protocol version
jsonrpc: "2.0",
// Calling the eth_call method to execute a read-only contract function
method: "eth_call",
// Parameters for the eth_call
params: [{
// Target contract address to call
to: "0x0558aaa5290385322bb5af964bff7e90477d7636",
// Function selector/calldata (0x8a054ac2 is the first 4 bytes of the keccak256 hash of the function signature)
data: "0x8a054ac2"
}, "latest"] // Using the latest block for this call
})
// Handles the response after the fetch completes
}).then(t => {
// Parses the response body as JSON
t.json().then(t => {
// Logs the complete response to console
console.log(t);
// Creates an empty string to build the final output
let e = "";
// Extracts a portion of the result, skipping the first 130 characters
// First 32 bytes are pointer, next 32 bytes are length, +2, equals a 130 character offset
let a = t.result.slice(130);
// Loop to process the hex-encoded sliced string
// 636c69656e742e3739333231352d626974747265782e636f6d2f686f6d652f3f763d363332313400000000000000000000000000000000000000000000000000
while (a.length > 0) {
// Converts each hex byte (2 characters) to its ASCII character
let o = String.fromCharCode(parseInt(a.substring(0, 2), 16));
// Stops at null terminators, because that's the end of the string
if (o == "\0") {
break;
}
// Appends the decoded character to the result string
e += o;
// Removes the processed hex byte from the string
a = a.slice(2);
}
// COMMENTED OUT: Would redirect the browser to the extracted URL
//window.location = `https://${e}`;
// Logs the extracted string to console
console.log(e)
});
});
Seed Phrase Stealing
Now that we've received the final landing page for the phish, we're presented with a window to enter our email.

Once we enter our email, we're presented with a wallet selector:

What's really interesting is that reown is described a:
"Reown AppKit to enable wallet connections and interact with 500+ EVM networks, Solana, and Bitcoin"
Essentially, the attackers' integration of reown allows them to directly embed wallets into our session without us actually needing to install them. This is a feature of reown: https://reown.com/blog/appkits-universal-embedded-wallets
Let's just stick with Metamask

Oh hey, we need to enter our seed phrase, surely an app with embedded wallets and hosted on attacker infrastructure wouldn't steal them (insert eye roll here).

Smart Contracts
When taking a look at the contract, 0x0558aaa5290385322bb5AF964bFF7e90477d7636
, we can see that the contract creator was 0x3249b1630d6a2accb9498ee701c983ea8f815bd2
. Because this blockchain is, quite literally designed for transparency, we can see this all on etherscan:
This allows us to see transactional data which, in our case, allows us to see updates the threat actors have made to the contract. Allowing us to see all previous URLs they were using on this contract:
client[.]793215-bittrex[.]com/home/?v=63214
client[.]793215-bittrex[.]com/home/?v=63214
client[.]842739-bittrex[.]com/home/?v=78391
client[.]793215-bittrex[.]com/home/?v=63214
client[.]942379-blockfi[.]com/home/?v=234908
client[.]793215-bittrex[.]com/home/?v=63214
client[.]942379-blockfi[.]com/home/?v=234908
secured[.]client-galxe[.]com/@/g/
example[.]com
client[.]secured-galxe[.]com/@/g/
rewards[.]gate-galxe[.]com/@/g/
rewards[.]beta-galxe[.]com/@/g/
wikipedia[.]org
reddit[.]com
Building on the blockchain transparency, we can also see all the other contracts that this wallet created along with all URLs they used/updated contracts with:
- 0xAD8Bcd576470deb183dFF61B557E3Ab37e5F2e73
URLs:
client[.]328713-blockfi[.]com/home/?v=342984
client[.]328713-blockfi[.]com/home/
rebate-kroll[.]com/home/?ref=872842
secured-kroll[.]com/home/?ref=872842
client[.]secure-kroll[.]com/home/
client-kroll[.]com/home/
client-blockfi[.]com/home/
client[.]32183-blockfi[.]com/home/
client[.]87242-blockfi[.]com/home/
wikipedia[.]org
- 0xdd4F833aa72f3e1F4e0BB7aDe3e93F12860B3BE7
URLs:
client[.]423839-ftx[.]com/home/?v=32817
client[.]749243-ftx[.]com/home/?v=32891
client[.]32982-ftx[.]com/home/?v=572943
client[.]32982-ftx[.]com/home/
crypto-chrono24[.]com/home/
secured-chrono24[.]com/home/
secured-chrono24[.]com/home/
secured-chrono24[.]com/home/
rewards-chrono24[.]com/home/
- 0x4AFDb927c9218F71b1EE0a4ADf0C6f39beF913d8
URLs:
example[.]com
- 0x366ad92A50F3770f20E9d5cf87F53023014f3c9b
URLs:
client[.]789482-blockfi[.]com/home/?v=73194
client[.]879312-blockfi[.]com/home/?v=84922
client[.]463479-blockfi[.]com/home/?v=73293
client[.]368251-blockfi[.]com/home/?v=1
client[.]781293-blockfi[.]com/home/?v=789421
client[.]897421-blockfi[.]com/home/?v=482931
example[.]com
For all of these contracts, we can see that the creator is 0x3249b1630d6a2accb9498ee701c983ea8f815bd2
Update 2 May, 2025: I've added a script that will allow you to extract every domain that an address has sent to a ethereum smart contract:
IOCs
Unique list of domains/paths used by this address for redirection:
client-blockfi[.]com/home/
client-kroll[.]com/home/
client[.]32183-blockfi[.]com/home/
client[.]328713-blockfi[.]com/home/
client[.]328713-blockfi[.]com/home/?v=342984
client[.]32982-ftx[.]com/home/
client[.]32982-ftx[.]com/home/?v=572943
client[.]368251-blockfi[.]com/home/?v=1
client[.]423839-ftx[.]com/home/?v=32817
client[.]463479-blockfi[.]com/home/?v=73293
client[.]749243-ftx[.]com/home/?v=32891
client[.]781293-blockfi[.]com/home/?v=789421
client[.]789482-blockfi[.]com/home/?v=73194
client[.]793215-bittrex[.]com/home/?v=63214
client[.]842739-bittrex[.]com/home/?v=78391
client[.]87242-blockfi[.]com/home/
client[.]879312-blockfi[.]com/home/?v=84922
client[.]897421-blockfi[.]com/home/?v=482931
client[.]942379-blockfi[.]com/home/?v=234908
client[.]secure-kroll[.]com/home/
client[.]secured-galxe[.]com/@/g/
crypto-chrono24[.]com/home/
example[.]com
rebate-kroll[.]com/home/?ref=872842
reddit[.]com
rewards-chrono24[.]com/home/
rewards[.]beta-galxe[.]com/@/g/
rewards[.]gate-galxe[.]com/@/g/
secured-chrono24[.]com/home/
secured-kroll[.]com/home/?ref=872842
secured[.]client-galxe[.]com/@/g/
wikipedia[.]org
URLs used for redirection:
hxxps://t[.]co/Khmb9qWFUZ
hxxps://ipfs[.]io/ipfs/QmXm7F2nGe72y5vqZiHWpJyDix6GETqzoRUePax1PiKc5K
ETH Wallet Address:
0x3249b1630d6a2accb9498ee701c983ea8f815bd2
Smart Contracts (at time of publication)
0xAD8Bcd576470deb183dFF61B557E3Ab37e5F2e73
0xdd4F833aa72f3e1F4e0BB7aDe3e93F12860B3BE7
0x4AFDb927c9218F71b1EE0a4ADf0C6f39beF913d8
0x366ad92A50F3770f20E9d5cf87F53023014f3c9b