Help Test Firefox's built-in HTML Sanitizer to protect against UXSS bugs

This article first appeared on the Mozilla Security blog

I recently gave a talk at OWASP Global AppSec in Amsterdam and summarized the presentation in a blog post about how to achieve "critical"-rated code execution vulnerabilities in Firefox with user-interface XSS. The end of that blog posts encourages the reader to participate the bug bounty program, but did not come with proper instructions. This blog post will describe the mitigations Firefox has in place to protect against XSS bugs and how to test them. Our about: pages are privileged pages that control the browser (e.g., about:preferences, which contains Firefox settings). A successful XSS exploit has to bypass the Content Security Policy (CSP), which we have recently added but also our built-in XSS sanitizer to gain arbitrary code execution. A bypass of the sanitizer without a CSP bypass is in itself a severe-enough security bug and warrants a bounty, subject to the discretion of the Bounty Committee. See the bounty pages for more information, including how to submit findings.

How the Sanitizer works

The Sanitizer runs in the so-called "fragment parsing" step of innerHTML. In more detail, whenever someone uses innerHTML (or similar functionality that parses a string from JavaScript into HTML) the browser builds a DOM tree data structure. Before the newly parsed structure is appended to the existing DOM element our sanitizer intervenes. This step ensures that our sanitizer can not mismatch the result the actual parser would have created - because it is indeed the actual parser. The line of code that triggers the sanitizer is in nsContentUtils::ParseFragmentHTML and nsContentUtils::ParseFragmentXML. This aforementioned link points to a specific source code revision, to make hotlinking easier. Please click the file name at the top of the page to get to the newest revision of the source code. The sanitizer is implemented as an allow-list of elements, attributes and attribute values in nsTreeSanitizer.cpp. Please consult the allow-list before testing. Finding a Sanitizer bypass is a hunt for Mutated XSS (mXSS) bugs in Firefox -- Unless you find an element in our allow-list that has recently become capable of running script.

How and where to test

A browser is a complicated application which consists of millions of lines of code. If you want to find new security issues, you should test the latest development version. We often times rewrite lots of code that isn't related to the issue you are testing but might still have a side-effect. To make sure your bug is actually going to affect end users, test Firefox Nightly. Otherwise, the issues you find in Beta or Release might have already been fixed in Nightly.

Sanitizer runs in all privileged pages

Some of Firefox's internal pages have more privileges than regular web pages. For example about:config allows the user to modify advanced browser settings and hence relies on those expanded privileges. Just open a new tab and navigate to about:config. Because it has access to privileged APIs it can not use innerHTML (and related functionality like outerHTML and so on) without going through the sanitizer.

Using Developer Tools to emulate a vulnerability

From about:config, open The developer tools console (Go to Tools in the menu bar. Select Web Developers, then Web Console (Ctrl+Shift+k)). To emulate an XSS vulnerability, type this into the console: document.body.innerHTML = '<img src=x onerror=alert(1)>' Observe how Firefox sanitizes the HTML markup by looking at the error in the console: “Removed unsafe attribute. Element: img. Attribute: onerror.” You may now go and try other variants of XSS against this sanitizer. Again, try finding an mXSS bug or by identifying an allowed combination of element and attribute which execute script.

Finding an actual XSS vulnerability

Right, so for now we have emulated the Cross-Site Scripting (XSS) vulnerability by typing in innerHTML ourselves in the Web Console. That's pretty much cheating. But as I said above: What we want to find are sanitizer bypasses. This is a call to test our mitigations. But if you still want to find real XSS bugs in Firefox, I recommend you run some sort of smart static analysis on the Firefox JavaScript code. And by smart, I probably do not mean eslint-plugin-no-unsanitized.

Summary

This blog post described the mitigations Firefox has in place to protect against XSS bugs. These bugs can lead to remote code execution outside of the sandbox. We encourage the wider community to double check our work and look for omissions. This should be particularly interesting for people with a web security background, who want to learn more about browser security. Finding severe security bugs is very rewarding and we're looking forward to getting some feedback. If you find something, please consult the Bug Bounty pages on how to report it.

Please contact me or comment on Mastodon or Twitter. If you find something you wish to change, you may also submit a pull request.

All posts

  1. Origins, Sites and other Terminologies (Sat 14 January 2023)
  2. Finding and Fixing DOM-based XSS with Static Analysis (Mon 02 January 2023)
  3. DOM Clobbering (Mon 12 December 2022)
  4. Neue Methoden für Cross-Origin Isolation: Resource, Opener & Embedding Policies mit COOP, COEP, CORP und CORB (Thu 10 November 2022)
  5. Reference Sheet for Principals in Mozilla Code (Mon 03 August 2020)
  6. Hardening Firefox against Injection Attacks – The Technical Details (Tue 07 July 2020)
  7. Understanding Web Security Checks in Firefox (Part 1) (Wed 10 June 2020)
  8. Help Test Firefox's built-in HTML Sanitizer to protect against UXSS bugs (Fri 06 December 2019)
  9. Remote Code Execution in Firefox beyond memory corruptions (Sun 29 September 2019)
  10. XSS in The Digital #ClimateStrike Widget (Mon 23 September 2019)
  11. Chrome switching the XSSAuditor to filter mode re-enables old attack (Fri 10 May 2019)
  12. Challenge Write-up: Subresource Integrity in Service Workers (Sat 25 March 2017)
  13. Finding the SqueezeBox Radio Default SSH Passwort (Fri 02 September 2016)
  14. New CSP directive to make Subresource Integrity mandatory (`require-sri-for`) (Thu 02 June 2016)
  15. Firefox OS apps and beyond (Tue 12 April 2016)
  16. Teacher's Pinboard Write-up (Wed 02 December 2015)
  17. A CDN that can not XSS you: Using Subresource Integrity (Sun 19 July 2015)
  18. The Twitter Gazebo (Sat 18 July 2015)
  19. German Firefox 1.0 ad (OCR) (Sun 09 November 2014)
  20. My thoughts on Tor appliances (Tue 14 October 2014)
  21. Subresource Integrity (Sun 05 October 2014)
  22. Revoke App Permissions on Firefox OS (Sun 24 August 2014)
  23. (Self) XSS at Mozilla's internal Phonebook (Fri 23 May 2014)
  24. Tales of Python's Encoding (Mon 17 March 2014)
  25. On the X-Frame-Options Security Header (Thu 12 December 2013)
  26. html2dom (Tue 24 September 2013)
  27. Security Review: HTML sanitizer in Thunderbird (Mon 22 July 2013)
  28. Week 29 2013 (Sun 21 July 2013)
  29. The First Post (Tue 16 July 2013)