Last week, one of my colleagues received an email that was, well… phishy (see what I did there?).
He had the right reflex and forwarded the email to the company’s CISO / CTO / general IT guy (i.e. me) rather than click on the link. It was quite obviously a phishing attempt, but I was curious about how this particular one was put together. So, down the rabbit hole I went!
This investigation began with an email sent by microsoft32[…..]email@example.com, obviously a legit support email address, warning about syncing issues: recent emails couldn’t be delivered because of some passive aggressive server dropping emails. The message is really hard to read in Outlook, which kind of gives the trap away.
The same message viewed with Office 365 online (or whatever the cool kids are calling OWA these days) is, however, much more convincing. If it wasn’t for a few grammatical errors, and the weird email address, you could be tempted to click on the link. I don’t know if the Microsoft logo next to the sender is a default icon or if it’s added automatically because of the exchange.microsoft.com domain, but it certainly adds legitimacy to the whole thing.
Ok, so far we have a pretty decent attempt to get an unsuspecting user to click on a link. With run of the mill phishing attempts, this is where it usually breaks down: they link to some weird looking domain that should send you away running when you see it, something like ms-online-security-support.com.cn. If there’s a sliver of a doubt that the domain may be legit, a WHOIS on the domain name can quickly dispel it.
In this email, however, the link points to stralab.z13.web.core.windows.net. In https, no less. While the sub-domain is definitely strange, the windows.net part is… unexpected. At first I thought it was a homograph attack, but checking the encoding confirms that it’s the real windows.net. Maybe domain squatting then ? Nope, a quick WHOIS shows that this domain name is owned by Microsoft.
Ar this stage, I admit that I initially thought the attackers had managed to get their content on a machine somewhere in MS-land which had been accidentally left open to the Internet. After some googling it turns out, much to my chagrin, that this isn’t some super-stealthy code injection on an MS-managed server. This is what Azure static website hosting URLs look like.
So, come to think of it, it is malicious code injection on an MS-managed server exposed to the Internet… But it’s paid for, so that probably makes it all right.
By this point I was genuinely curious about the rest of the phish. So I jumped to incognito mode, which is the equivalent of a static strap for dodgy websites, and followed the link.
The link redirects to a pretty good imitation of the real Microsoft login page. Some details are off, like how the email address of the victim (which is passed through the link) is displayed, and an encoding issue next to the copyright sign. But we are greeted with a green padlock, so everything must be ok, right? And indeed, the certificate is legitimate.
Of course, all this is obvious if you know this is an Azure Storage static website (which I only found out later, so cut me some slack). My gripe is that the URL doesn’t really make this explicit, contrary to e.g. how S3 static hosting URLs look like (
<bucket_name>.s3-website.<region_name>.amazonaws.com). I feel that putting “Azure” somewhere in the URL would make it more obvious.
Before we look under the hood, a quick aside on the site appearance. Some details of the fake login page looked a little fuzzy on my 4K laptop screen.
As you can see on the close-up above, some text elements, such as the “Password” placeholder, are sharper than others. This doesn’t show up if the page is viewed on a regular HD screen:
Also, while we’re looking up close at this dialog box, notice the “Back” and “Sign in” buttons are misaligned by one or two pixels.
The code source
Enough already with the screenshots and pixels, I hear you say. Show us the code! Well, there you go:
Some fun stuff to note about the content of the page:
- an escaped “Sign in” title and “Password” placeholder —probably to avoid triggering automatic scans or AV/browser detection.
- a password-masking library lifted from a sitepoint tutorial.
- use of the microsoft CDN for the favicon — nicely done 😄
- the entire page, including the form and the buttons, is a collage of png files served from Alibaba Cloud. This explains why it looks fuzzy on 4K screens and why the buttons are misaligned.
eval defines the function
j2daedb, and the second
eval calls this function with an encrypted payload before writing the result in the document. The function itself splits the payload in two strings, an encrypted message and a key. It then deciphers the message by xoring it with the key.
Once more unto the breach, dear friends: let’s see what hides in this encrypted payload. In case you’re feeling both curious and lazy, here’s the key used for decryption: 6212839612778 (it doesn’t appear to mean anything, though).
Well, now that we’ve gotten to the juicy core of this nugget of obfuscated badness, the payload is straightforward to analyze (and a bit disappointing, to be honest).
twoToneButton event listener replaces the “Login” button by a “Loading…” image when clicked, giving the illusion that login is proceeding as usual. There’s some code for
#pass which doesn’t appear to do anything other than identify enter key presses.
The main event is the
sendmails function, which is called directly from the html. This function basically sends the email address and password to a remote server and waits for a response (in case you’re wondering, the remote URL was registered by Alibaba; it was purchased in July). If this response is
"donesend", the user is redirected to the real Microsoft login page. Otherwise, an error message is displayed and the user is invited to try again. I don’t know if the remote server simply stores the sent credentials or tries to login with them before acknowledging the request. But once again, a fair bit of effort was put in making the process inconspicuous.
A closer look at the email
Now that we’ve seen what happens when a victim clicks on the link and tries to login, let’s go back to how the email was built and how it was sent. First, a quick look at the body of the email (edited for brevity):
The main thing to note is that the email content is interspersed by a lot of
<font id="xv">zu0kI6zuzs3kI6</font> elements, which looks like an old-school way to get past spam filters. As per the inline css, this should be displayed with
font-size:0px , i.e. be invisible. Obviously, Outlook doesn’t follow the CSS standard, which explains why the email looks bad in the desktop client— and much more convincing in the web app.
This leaves one question: how did this email end up in a mailbox masquerading as a Microsoft support message? Let’s take a look at the email headers to find out. The relevant code is here (posted as a link rather than inline, as it’s long and frankly ugly to look at). Making sense of email headers is somewhat of a black art, at least for me, but MxToolbox has a really nice tool for that; here’s how the headers look like in human-readable format.
The email originates from an IP in Switzerland; the reverse lookup identifies it as a residential IP address from a Swisscom customer. Interestingly, this IP has already made it into a blacklist, although I wasn’t able to find out if this was before or after the email was sent.
The entry SMTP relay is controlled by bluewin.ch, which is a Swiss FAI bought by Swisscom a few years back. Taken together, this seems to point to a Swisscom customer’s computer having been compromised and being used, along with the customer’s email account, to send phishing emails.
From there, the email goes through the O365 SMTP servers without a hitch and lands in the victim’s mailbox. And the curious body formatting allows it to go through spam filters unscathed, as the headers show:
In the snippet above, “PCL” stands for Phishing Confidence Level. So, definitely not a spam or a phishing attempt.
Luckily, we have SPF and DKIM to protect us from fake sending domains! Right?
No we don’t. SPF configuration for exchange.microsoft.com defaults to a soft fail if the sending address is not recognized, and Outlook doesn’t flag soft fails 😕
So there you have it. I spent way too much time looking at this phishing attempt, and writing this post. It was fun to see how it was put together, and depressing to see how mundane the setup is. I was halfway expecting exotic vulns and the mark of an evil genius, and only found common exploits and plodding mediocrity.
It’s not often that two wrongs make a right, but in this case Outlook failing to display the CSS properly foiled a phishing attempt 😁. Failing that, there’s always typos and grammatical errors to give the game away…
The effort put into using https and a legit domain name shows that education on “checking for the green padlock” is making headway, thanks in large part to browsers’ more aggressive warnings. Let’s keep it up.
This attempt shows how trivial it is to set up a halfway decent phishing website. A static hosting service, some screenshots, a bit of js and you’re done. This is essentially free, and there’s nothing stopping you from just creating a new account if your website gets pulled.
It also underlines just how easy it is to smuggle bad emails even through large, modern email services. It shouldn’t be this easy to pretend you’re from microsoft.com.
It took more than a week for the phishing website to be pulled after I reported it through Microsoft’s abuse reporting tool. And this only happened after (or at the same time that) it was added to Chrome’s blacklist. So… Next time I’ll submit the URL directly to Chrome and see if this is more efficient.
Azure websites inheriting from a windows.net URL and SSL certificate… The potential for abuse seems high. People, at least those susceptible to fall for this type of scam, have come to recognize and trust the Windows / Microsoft name and this attack plays fully on that. It’s no coincidence that the phishing email was sent to an O365 email address. On the other hand, these same people are much less likely to trust an Azure URL.
Microsoft, maybe make the distinction between your online properties more explicit? Or at the very least please recognize that you’re serving potentially malicious code from your own domain name and, you know, maybe look into abuse reports promptly rather than, say, after a week. This is, ultimately, your reputation at stake.
This post may come across as Microsoft bashing. It’s not (well, maybe a little bit. Come on, who wouldn’t ?). But you know the old saw: with great power comes great responsibility. Microsoft is a trusted household name. Its attack surface is huge, between Azure and Office 365 and of course, Windows. On the other hand, they have made massive progress on security issues over the last few years, and I was frankly disappointed to see how long it took them to react to this issue. So here’s to hoping that I’m blown away (but in a good way) next time I report a security issue to them!