The Discovery
It all began in December 2018, when I received an email from a friend asking for help with some JavaScript code he was investigating. I wasn’t actively hunting for bugs, but the email formatting on my Android phone looked strange. The screenshot below in figure 1, (with PII scrubbed, of course) is the email I received:
That gray box—it shouldn’t have been there. When I looked closer, it was clear the JavaScript probably contained HTML code for an iframe that would render normally outside of the mobile application. Again, this seemed strange. When I opened it up on a laptop, the code was displayed normally, as you can see below in figure 2:
This struck me as a problem: the ability to embed an iframe into an email is already a vulnerability. Even worse, as the iframe was not affected by the block external images setting that prevents tracking pixels and web beacons. But if an attacker could gain the ability to run JavaScript in an email, there could be a much more dangerous attack vector.
With this in mind I tried inserting a script tag instead of an iframe into an email. It failed, which is good. However, I was able to circumvent this by using a JavaScript URL in my iframe. Things were getting interesting.
Stored XSS Via Email
In a web browser, it’s possible to run JavaScript code by using a URL that starts javascript:. But in a web browser, JavaScript in an iframe on a separate domain shouldn’t have access to the data in the rest of the page. In Outlook on the Android, there is no such restriction. My iframe JavaScript had full access to cookies, tokens and even some emails. Not only that, I could send them back out to a remote attacker.
This kind of vulnerability could be exploited by an attacker sending an email with JavaScript in it. The server escapes that JavaScript and does not see it because it’s within an iframe. When delivered, the mail client automatically undoes the escaping and the JavaScript runs on the client device. Bingo – a stored XSS. This code can do whatever the attacker desires, up to and including stealing information and/or sending data back out. An attacker can send you an email and just by you reading it, they could steal the contents of your inbox. Weaponized, this can turn into a very nasty piece of malware.
Attempt at Disclosure
This was a big deal, so I needed to let Microsoft know. Before disclosing, I created a short Proof of Concept (POC) that demonstrated my vulnerability. It ran an arbitrary external script that stole and exfiltrated private data (although admittedly, with very limited access to email data). I sent this to the Microsoft Security Response Center (MSRC) on December 10, 2018.
At this point I did not know exactly which part of the code caused the bug. How could I? I don’t have access to Outlook source code. And really, I had no experience debugging mobile apps. Besides, I assumed that it would be pretty easy for the app developers to take my POC and find the problem.
Unfortunately, the vulnerability didn’t reproduce for the engineering team. I was stymied and worried. This was real and I needed them to address it. I sent them a video of the bug occurring. I later learned that another researcher reported it too, but neither POC reproduced the bug for the security engineers.
I tested different Outlook settings to see if that was the cause of the discrepancy, but had no success, and so the case went cold.
No Repro, No Bug
Every security engineer and developer will tell you that not being able to reproduce a reported bug is a real headache; and their time is a precious and limited resource to the business. An organization can only expend so much effort to try to reproduce a bug. If they can’t reproduce it, the reasoning goes, then surely an attacker can’t either. So the engineers and devs sit on it, and often put the responsibility back on the researcher to find a way to create a POC that security engineers can easily confirm, triage, and hopefully patch.
Breakthrough
I couldn’t let it go. A few months later, I was still thinking about this vulnerability, and the difficulty in creating a POC for the Microsoft Security Engineers. I remembered this bug and thought about how to extract the rendered HTML from the app. I realized that the key was the bug itself! The bug let me steal data from the app—I could use it to read and extract the HTML.
I constructed a new payload using this information, which can be seen in the screenshot below in Figure 3:
The payload I constructed looked like this:
<iframe src="javascript:alert(window. top. document. body. innerHTML+'')" style="position:absolute;left:-2330px;" src=h
This payload correctly escaped on the server side with HTML entities. But the escaping was undone on the client, and the payload was converted to:
<iframe src="javascript:alert(window. top. document. body. innerHTML+'')" style="position:absolute;left:<a href=” tel:+442330”=””>-2330</a>px;" src=h</iframe>
The bug occurred in the client-side code, which made the phone number clickable. This bug was not reproducible at first because my Android localization settings were set to UK, which caused the number to be judged as a valid phone number. Other localizations didn’t detect a phone number, so there was no bug.
I rewrote the POC, using a full US phone number format, xxx-xxx-xxxx, and tested that this reproduced across localities. I then sent this back to Microsoft. On March 26, 2019, they confirmed the repro and committed to a fix within 90 days of that date.
Timeline of Disclosure
- 10 December 2018 – First disclosed to MSRC
- 16 January 2019 – Video screen capture of vulnerability sent. Microsoft cannot repro. Case went cold.
- 26 March 2019 – Universal POC sent to MSRC
- 26 March 2019 – Microsoft confirms repro
- 20 June 2019 – Fix released
Conclusion
CVE-2019-1105 has a fix is available as of June 20, 2019, available at https://portal.msrc.microsoft.com/en-US/security-guidance/advisory/CVE-2019-1105. It is always important for both individual and enterprise users to keep their applications up to date in order to lower the risk of exploitation of known vulnerabilities. It’s also important to seek and report security vulnerabilities, so we can all have safer products—and, obviously, keep our systems patched to avoid getting burned by these new holes when they are released.