Defending against the Attack of the Clone[d website]s!

Front matter

In a previous post, Casey talked about our Cloned Website Canarytoken and how it fares against modern phishing attacks.

Today, we are releasing two new versions of the token which alert you when an attacker is using an Adversary-in-the-Middle (AitM) attack against one of your sites. An added bonus is that the new tokens can be deployed on properties you only have limited administrative access to (like your Azure tenant login portal or hosted blog).

In this post we briefly recap AitM phishing, the limitations of the Javascript-based Cloned Website Token, and how we built our new CSS-based one. Finally we wrap up with showing you how our CSS can be installed into an Azure Entra ID login portal with just a couple of clicks – giving you high-quality alerts if your users are being targeted.

The folks at Zolder.io (a Thinkst Partner) independently built a very snazzy and similar token, check out their blog post for more details.

AitM phishing

Early phishing sites were static clones of the HTML/CSS and Javascript of the victim’s site, that would store entered credentials, and in some cases redirect the victim user to the legitimate site after stealing their credentials. These static sites struggled to handle dynamic login flows and MFA prompts. 

Now, with tools like EvilGinx and Modlishka, the adversary creates a reverse proxy server that sits in between the user and the victim site. This allows for dynamic modification of Javascript to reflect the phishing domain (which can be configured to prevent a non-obfuscated Cloned Website Token from firing), as well as seamlessly passing the user to the victim site after stealing the session. Since the adversary is acting as a proxy, the victim site is presented to the users dynamically, and the adversary can bypass many types of MFA, since the user is logging in and will accept the push, or enter the SMS/TOTP code.

This figure from Microsoft shows the steps of a classic AitM phishing attack:

Javascript Cloned Website Token

As detailed in Casey’s blog, our classic Cloned Website CanaryToken has been useful to companies around the world. We simply generate a small snippet of (optionally obfuscated) Javascript code to be added to your site. When the client loads the page, the javascript checks if the site was served from the expected parent domain, otherwise it triggers an alert.

Side-Note:

It’s worth noting that when the site is loaded from the expected parent domain, there is no external request made to the Canarytokens servers. We only receive requests in the infrequent situation that there is a cloned site detected.

This token works really well, especially if obfuscated against simple domain name replacements by the AitM server. However, it requires the ability to insert Javascript into a page, a requirement that many 3rd party service providers do not allow.

CSS Cloned Website Token

We spent some time trying to figure out if there was a clever CSS trick to similarly only alert the Canarytokens server when the domain was unexpected, but in order to reduce the risk of 3rd party CSS imports, the CSS interpreter in the browser is quite restricted. Our solution is to create a reference via a url() for an invisible background image that points to our server, where we check the Referer [sic] HTTP header to ensure it’s being loaded from the correct domain. This means a Token can be inserted into your webpage by simply adding a CSS rule.

Version 0.1

Some services allow us light customization (mainly for look and feel) but won’t allow us to upload custom JavaScript (preventing us from using our trusty cloned site token) so we went off in search of a CSS alternative. CSS is relatively restricted, but we can get an early start by creating a reference via a url() for an invisible background image that points to a server we control.

On that server, we can check the Referer [sic] HTTP header to ensure the CSS is being loaded from the correct domain.

This will work for many AitM phishing attacks (since the adversary doesn’t control the privacy policy on the browser for the Referer header) but some browsers omit or otherwise spoof that header. 

Another downside is that our Canarytokens server is now “in-the-loop” for every page load, benign or not, adding to latency for the sites valid users, and potentially adding significant load to our servers, especially if deployed to, for example, a popular Azure tenant’s login portal.

Lambdas, functions and edges

In order to manage this, and to obscure the purpose of the CSS reference (as there are numerous Canary Token detectors that essentially grep for a canarytokens.com string), we wanted to use a popular cloud-based provider to filter out the legitimate requests, and to then only pass suspect requests on to our servers.

The first idea was an AWS Lambda, or the Azure Function equivalent, but the startup times could degrade the user experience, and the costs would be unnecessarily high. AWS provides a spectrum of other serverless functions that balance cost, latency, and features: in light of this, we explored Lambda@Edge functions, which execute automatically at all regions (reducing latency), but have some limitations on the runtime environment.

Cloudfront Functions

Even further in the scalability/functionality tradeoff are CloudFront Functions

These run extremely quickly (~1ms maximum execution time), but have (deliberately) very limited functionality. The goal was then to figure out how to make use of the limited functions to provide a cost-effective and performant experience. 

Our solution

We deployed a single function to all of the CloudFront points-of-presence that parse an incoming request-string for two pieces of encoded data: an expected Referer, and a Canarytoken ID associated with it. If the Referer header matches the expected one, the function terminates by returning a 1×1 transparent GIF, otherwise it issues a HTTP Redirect to the Canarytoken server with the parsed ID. The below figure shows the flow as a user logs in to an Azure tenant:

And this diagram shows what occurs when an AitM phishing server is deployed in-between the user the the login page:

This design wins. It offers very low latency, global deployment, a lightweight function, and since CloudFront is a CDN, linking to CDN assets is not suspicious to an adversary.

Now we just have to make it easy to deploy!

Using the CSS token around the web

In order to deploy one of these tokens to a site with limited scope for edits (e.g. one where you can only add HTML but not Javascript), I’ll show you how I added an invisible <span> to my personal web site that alerts when it’s not loaded via the correct domain.

First I navigate to the Canarytokens site and select the CSS cloned website token from the dropdown. I then put in my email address, and the domain I expect (my webpage is jacobtorrey.com). It’s also important to put a useful memo to remind myself in months when this fires where it was placed to help me respond. Click create and the token is generated:

This CSS is designed for copy and paste (or automatic installation) into an Azure Entra ID tenant, but the url() can be used elsewhere. I create an empty <span> on my webpage that has the style="display: hidden; background: url('https://dakg4cmpuclai.cloudfront.net/h6jf84z88jbpfuc1at5n5/amFjb2J0b3JyZXku/img.gif'); and save it. When I view this page as expected, it looks no different, and I get no alert:

But if I use EvilGinx to phish this site and navigate to it, it looks the same:

Immediately I get an email alert:

This has the client IP address and the referring domain, as well as the reminder I set myself. Sweet! I can see where this site was served from, if this was a real phishing site, I’d know its URL and could proceed with filing an abuse report, or blocking that domain from my network. Clicking more information provides the following information (censoring the IP and hostname):

This technique will work on most blogging platforms (e.g., WordPress) and other hosted platforms that allow some HTML editing, but not Javascript.

Sweating the small stuff

Now that we have the new CSS-based Cloned Website Token, it was time to make the user experience for deployment even simpler. We expect that Azure portals will be a popular use case, and in our testing found that the steps needed when uploading a custom CSS were cumbersome. It’s important to note that the custom branding feature in the Entra UI requires an Entra ID P1 or P2 subscription. In order to make this as easy to deploy as possible, we explored how to build Azure applications, which can install custom branding CSS on behalf of a tenant once authorized.

We built an Azure application that can request access to install the CSS file automatically (but only if there is not already one present that would conflict) when the user is logged in to their tenant with sufficient admin privileges. The Azure application will then revoke its own permissions once the CSS is installed, reducing risk. We have noted that sometimes it takes minutes to hours for the custom CSS to be propagated to Azure’s CDN, but it is installed. The following screenshots show this simplified process for protecting an Azure tenant:

Starting the same way, we create a token, this time selecting the Azure Entra ID login. Again we want to give ourselves a helpful reminder about the token:

After we click ‘Create’, we see the token has been generated:

This time we’re presented with an option to either follow the automatic installation flow, or manually download and manually install the CSS into the Azure tenant. I click on the automatic option and a new tab opens prompting me to login, or choose an Azure account to log in with. This account must be an Administrator account on the tenant to grant the permissions 1needed:

I choose the account for the tenant I want to protect with this token, and am sent to an authorization page asking if I want to authorize the application and the permissions needed.

Accepting this prompt will redirect you back to the tokens page with a status:

That’s it! In a few moments your Azure login page will now include this CSS, and if an attacker tries to use an AitM phishing proxy to target your users, you’ll be alerted the moment it happens, and the domain the attacker is using.

Conclusions

Phishing remains a highly effective method of compromising accounts, and the tooling has matured to make all but FIDO2/Passkeys MFA techniques vulnerable to AitM phishing and session stealing. While our extant Javascript token remains popular and provides valuable alerts on phishing site creation, we wanted to cover more of our users’ online services. Our new CSS and seamless Azure login portal cloned site token offers another way to quickly instrument 3rd party service providers you rely on to protect your organization. These tokens rely on client behavior to alert, so they are part of a holistic security strategy (which should include token-based MFA!) and not a single silver bullet. We’d love it if you took it for a spin and let us know how it goes!

  1. The installer requires both the Organization.ReadWrite.All, Application.ReadWrite.All permissions to create/update the Company Branding with the CSS, and remove itself from the tenant, respectively. The User.Read is a default permission needed to authenticate the application. All permissions are revoked at the end of the installation attempt. ↩︎

Leave a Reply

Site Footer

Discover more from Thinkst Thoughts

Subscribe now to keep reading and get access to the full archive.

Continue reading

Authored with 💚 by Thinkst