Azure Front Door AiTM Phishing
Introduction
We see new phishing toolkits and infrastructures all the time. Most of them are following the same patterns and are easy to spot. But, just a couple of weeks ago, during one of the investigations in Lab539 we detected something unusual - phishing infrastructure, set up to perform Adversary in The Middle (AiTM) attacks, hiding behind Microsoft’s Azure Front Door service.
At first glance this looks like a legitimate websites hosted on a Microsoft owned domain, a redirection chain containing all kinds of security focused websites, and trusted infrastructure. However, it turned out that it was a very well crafted AiTM campaign using some new and clever techniques to stay hidden from automated detection systems, non-technical targeted users, and investigators.
After some time digging deeper we uncovered some specific organisations being targeted. Those included The World Food Programme, UNICEF, major Swiss newspapers, and even some state.gov email addresses (U.S. Department of State).
Using Microsoft’s Azure Front Door reverse proxy to host phishing content, use of hashes in redirection chains, various encryption techniques, and strong obfuscation methods makes this one of the most advanced currently active phishing campaigns.
So first of all, what is Azure Front Door?
Simply, Azure Front Door is a cloud-based reverse proxy from Microsoft Azure. The initial purpose of it is load balancing, CDN capabilities, security, routing, and other. This has some similarities to Cloudflare’s workers.dev which has been used by adversaries to run phishing campaigns for quite some time, but azurefd.net doesn’t have serverless execution, it acts as a traffic manager so attackers need a backend.
Currently, we have detected 2 infrastructures that use Azure Front Door. We, imaginatively, call these Infrastructure 1 and Infrastructure 2. They have some differences but our analysis suggests, with a high level of confidence they are derived from the same codebase. The contents of the phishing page are exactly the same, the difference is in redirection methods and level of obfuscation.
Infrastructure 1
Concept
Infrastructure 1 is generally more obfuscated and much harder to block. Usually it uses 6-7 redirections before landing on the phishing page, and because of clever use of hashes in the link, the user won’t be able to access the final URL unless they go through all of the redirections from the start (more detailed explanation of that later).
Usually the redirection chain starts with a previously legit website that is bought by attackers or even compromised (the idea is to have a domain with historically legit-looking certificates). In this specific case the first website is ritba[.]org. This website gives us a hash that is stored in the URL bar and is used for client-side calculations only. User gets redirected through 1-2 telemetry collecting websites like: sophos.com, awstrack.me, secure-web.cisco.com, googleads.g.doubleclick.com, sendgrid.net, also some link shortening services like rb.gy and others. Those websites don’t have access to the hash.
After those 3-4 redirections we land on the first AzureFD page (to make it simple I will just call them page{number of the page}.azurefd) - page1.azurefd decodes the hash passed from the ritba[.]org and redirects us to page2.azurefd with a new hash. page2.azurefd has another, completely different decryption formula that redirects us to the final page3.azurefd phishing webpage. Diagram of infrastructure 1:
Redirections
The adversaries have created a clever system that uses hashes to ensure that the entire redirection chain was followed. First website - ritba[.]org gives us a hash, which is part of the URL fragment. It stays in the URL bar of your browser until it gets any status code other than 3xx(redirections ends) or if JavaScript was used. I have selected it with yellow (Some part of the hash is blurred, because after decoding, it will contain the email address of the targeted organisation).
The Sophos redirection acts as a security gateway, most likely to block bots. Although the hash appears in the URL, it is never sent to Sophos or any other website’s servers. Instead, it remains in the browser’s address bar and is used for client-side operations. Azurefd websites used for redirection can access the hash, but only when JavaScript specifically requests it. Example of JS requesting the hash:
hash = window.location.hash.substring(1)
page1.azurefd.net
After getting redirected through ritba -> Sophos -> rb.gy we finally get to the first AzureFD page. Whole link:
This page responded with 200 status code and contains a JavaScript algorithm that decrypts the hashed link. At this stage, the decryption is not very obfuscated in terms of code readability, but still follows a complicated chain of steps.
First, the code checks if a hash exists. For simplicity let’s say the hash is #1a2b3c
Then it splits the hash in 2 character chunks - [‘1a’, ‘2b’, ‘3c’]
The list is reversed - [‘3c’, ‘2b’, ‘1a’]
Each chunk is converted from base36 to its decimal value: [‘120’, ‘83’, ‘46’]
Then it performs its decryption formula which looks like the following:
chunk = (chunk - i) / 2 - KeyDigit.
Where ‘i’ is the index of the chunk. KeyDigit is a list of numbers - [‘8’, ‘7’, ‘4’, ‘2’] that are used one by one. So the formulas for each chunk would look like:
3b = (120 - 0) / 2 - 8
2b = (83 - 1) / 2 - 7
1a = (46 - 2) /2 -4
After all the numbers from KeyDigit are used it cycles back to the start again: 8 - 7 - 4 - 2 - 8 - 7 - 4 - 2 - 8. Once this process is completed we simply convert those decimal values into letters.
So after decrypting, we get the next url with a new hash:
page2.azurefd.net
This is the last website before arriving at the phishing page. Although its purpose is to redirect you to the next URL (same as the previous page), it is more obfuscated and uses a different decryption algorithm. This is the first page protected against web-scrapping, it blocks anything that may be used by investigators, including DevTools, Console, Page Source viewing, right-click in general, certain User Agents, Burp, and a lot more. Variable names are random, and some parts of the code are stored as hex-encoded strings in the list. Whenever needed, those strings are used from the list directly. For example this piece of code is blocking right-click, F12, Ctrl + Shift + I, Ctrl + Shift + J, Ctrl + U, Ctrl + S, Ctrl + A:
Hash decryption on this page follows the next process:
Base64 decode string
Each character is turned into %xx hex code
decodeURIComponent() function turns it to string
Then it takes each character, converts it to char code and subtract one from its char code number. For example: Letter ‘S’ = 83, 83 - 1 = 82, 82 = ‘R’.
Reverse the string
URI decode it again
Check if the link is valid, and redirect to the final phishing page.
In this instance, after decoding we get the following HTML/JavaScript returned:
<script src="https://s00983254234564356-ftd6bjcbamf6ggeg.z03.azurefd.net/"></script> <script lang="javascript"> const reloadUsingQuery = () => { window.location.search = "example@example539.com"; } window.onload = reloadUsingQuery; </script>
We can see the final phishing URL as well as the target - example@example539.com (obviously just an example email!). This email will be added to the link as a parameter after ‘?’ character. The link we got points us to the actual phishing page.
Phishing Page
Obfuscation
The final phishing page is intentionally obfuscated in order to make it hard to follow the decryption process: This includes: hex numbers, complicated mathematical operations, random variables, useless functions, etc. And the hardest part is that this code is like an onion. You have to go through multiple layers to access the main part.
First layer
At this point we have heavily obfuscated code that has one large array named _0x55eff5. This array contains a lot of hex encoded items and one very long item that will be our HTML:
Initially, the array is randomly rotated, and its items are intentionally placed in the wrong order. To make the code work, the array must be rotated so that items are in their correct positions. While loop that performs mathematical calculations continuously rotates the array moving the first item to the end until the result of those calculations matches a specific value. The original code of that mathematical operations is:
Decoded version:
_0x4cd2e1 - is a function that calls several other functions, and returns an item from an array based on a calculated index. To find the index, it subtracts 773 from the first parameter. For example in the first line:
926 - 773 = 153. So the index 153 is accessed. Interestingly there are only around 35 items in the array. To handle this, array goes through the list 4 times to get the item with index 13 (35 + 35 + 35 + 35 + 13 = 153).
After the calculations are made, the result is compared to the variable _0x32230f - which holds a decimal number - 238,785.
If the result of the calculation matches 238,785 - code moves to the next step. If not, the array is rotated, the first item is moved to the end, and calculation repeats.
Now the array is in the correct order and code can request any item from it. To set a contents of the page it uses function:
After placing array in the correct order and retrieving items with those indexes we get:
document[write](unescape({HTML of the page}));
Second Layer
After we find the real index of HTML and the way it was used we can explore it a bit more. First, we decode hex values. The result after decoding hex values:
Then we URL decode it:
Now we can see the structure, some parts are encoded using CryptoJS library, some parts are just Base64 encoded. The obfuscation techniques are still present including:
1) Web-scrapping protection:
The code is a bit longer than shown, but the idea is that if one of those parameters is met or the debugger is opened for longer than 100ms it will redirect the user to etsy.com. On some other websites, we have seen redirections to alibaba.com, for example.
2) There are some variables that are using the fromCharCode method to decode the words after some mathematical operations. There is no need in that other than to obfuscate the activity.
After decoding we get:
tMPVTJGfyY = globalThis[“eval”]; - function that will allow to run the script in the future
LiFLMNPSsn = globalThis[“asob”]; - accessing something called asob in the code
xnmIFCkAfN = “write”
Those are just parts of some other functions
3) The next layer is encrypted using the CryptoJS library, but at the bottom we can find the key and initialization vector to decrypt it . Decryption algorithm:
After the string was decrypted, new HTML is written with - document[write]({decrypted HTML})
Third Layer
At this layer, we finally see standard HTML elements, CSS, and of course one more web scrapping protection. Page begins by building the skeleton: divs, buttons, images or other elements. They are loaded from trusted sources like aadcdn.msauth.net, Github, Cloudflare, and others that are also commonly used by Microsoft. However, at the bottom of the page there is a large <script> element containing Base64 encoded JavaScript responsible for changing the contents of the page, sending and receiving data from the backend server, and logic in general.
After decoding it, there’s quite a lot going on. Unfortunately, I can’t include all of it here, as it would make the blog post too long, but here are some interesting facts:
First of all, the page is dynamic. It doesn’t reload or redirect you after you enter an email or password. Instead, it simply updates specific elements based on your actions. Each page is prewritten in this script and based on the command from the backend server can be activated and displayed.
Telegram links in passwords are not accepted, likely to prevent users from reporting the phishing.
Script is not just asking for email and password but if needed can work with 2FA, recovery emails, etc.
For some reason the website is blocking Apple and Netflix email addresses.
Communication with a Backend
The script sends POST requests to the backend server, but before doing so, it encrypts the data using the CryptoJS library. The same key and initialisation vector (IV) are used for both encryption of the packets sent to the server, and for decrypting the responses from the backend server.
Request 1: Contain email address, ip ,country and a couple more parameters used in the script:
Response 1: The response from the server checks if email really exists, sends values for some fields that have to be shown like "Forgot my password”, and sets you with a uid and token which will be used in future requests.
Request 2: This request is automatic and is just sending your Base64 encoded User Agent with your personal token
Response 2:
Request 3: In this request we send our token with password:
/977984265a721ada57062bf01d2c8a9943c4ff9693d5993cdeca1f5f6988664c/Password123?/1
Response 3: In our case, the server returns an error because the password is incorrect. It also sends a command indicating what to display on the page. Such as the ‘reset password’ part.
Based on how the server responds, the page will change its contents. For example you can’t access the 2FA verification page or reset password page unless the server tells you to do that.
Infrastructure 2
Infrastructure 2 is a bit less complicated. It still uses hashes, but unlike the infrastructure 1, there are no redirections between Azure webpages. The main difference is that before reaching AzureFD, we first land on a website controlled by the attackers - drugstoretab[.]de in this case. This is a Lightspeed server which contains 4 directories:
cgi-bin: This folder has restricted access and probably that’s where the stolen data is stored.
d-folder: It follows a standard redirection pattern: first, a random string is generated, and JavaScript creates a link using that string as the path.
pss-folder: Same as d-folder, just redirect to different page
sk-folder: Same as d and pss folders, just redirect to different page
pss.zip: It saves index.html - the same page content that pss folder would respond.
Indicators of Attack/Compromise
Login attempts from this infrastructure usually originate from IPv6 addresses. There’s a method to get the corresponding IPv4 address from them, but explaining it deserves a whole blog post, so maybe we will cover it soon. Most of the logins come from the same IP range, under the ASN: Global Connectivity Solutions. A few addresses also belong to Hivelocity Corp, a provider of dedicated server solutions.
IP Addresses:
2a02:748:4000:16:0:1:ed8a[:]2cfc 2a05:541:116:60:[:]1 2a05:541:116:61:[:]1 2a05:541:116:62:[:]1 37.72.168[.]192 212.18.104[.]220 212.18.104[.]221 212.18.104[.]222
Domains and Subdomains:
vwnrellsg[.]es gchjyeijlk[.]es pxktldnvpqkl[.]es drugstoretab[.]de ritba[.]org 57577784839920020028892-eyf5c3afh8bthnf5.z03.azurefd.net 7843667324672387872332-gadyc6gmehd3cqew.z01.azurefd.net 872438734784387349-fsavftf7d7hhezhn.z03.azurefd.net 67489930000338928291133-b0gabfabbbaka4ej.z03.azurefd.net 873474398349834798-czahekhndkb5gdf9.z03.azurefd.net 87437438794398-c7aadzffc4dtdzf7.z03.azurefd.net 7647667436734788748745-bjeceug9fmewcmbg.z03.azurefd.net 71588949038839301-frazdne6f5eff3gg.z03.azurefd.net 835480451364892591-e6dga4btbec8bqgp.z02.azurefd.net 9843878743873487-b5anezhyerarfrbq.z02.azurefd.net 7487834732478938647934348-ejd6hvg9g2edapbv.z02.azurefd.net 783784387348438743-fkhghccdfzc8e8cd.z02.azurefd.net 8934984398439438934-cgarbrfrbcabetcf.z02.azurefd.net 8784387438743-h7cnghf2c5acfvf8.z02.azurefd.net
Summary
This is likely the next stage in the evolution of the phishing campaigns. We start seeing the same techniques and codebase reused across different setups. While the Azure Front Door-based campaigns are very successful in achieving their goals, other infrastructures that use the same codebase are adopting their obfuscation methods and delivery tactics. This isn’t just another reverse proxy toolkit with improved anti-detection, but a combination of novel and highly technical approaches. We can expect to see many more of these emerging soon, so it is very important to understand how they work.