{"id":13,"date":"2019-12-02T07:30:08","date_gmt":"2019-12-02T07:30:08","guid":{"rendered":"https:\/\/blog.mozilla.org\/attack-and-defense\/?p=13"},"modified":"2020-06-16T07:24:20","modified_gmt":"2020-06-16T14:24:20","slug":"help-test-firefoxs-built-in-html-sanitizer-to-protect-against-uxss-bugs","status":"publish","type":"post","link":"https:\/\/blog.mozilla.org\/attack-and-defense\/2019\/12\/02\/help-test-firefoxs-built-in-html-sanitizer-to-protect-against-uxss-bugs\/","title":{"rendered":"Help Test Firefox\u2019s built-in HTML Sanitizer to protect against UXSS bugs"},"content":{"rendered":"<div style=\"display: none; visibility: none;\"><\/div>\n<p><em>This post first appeared on the <a href=\"https:\/\/blog.mozilla.org\/security\/2019\/12\/02\/help-test-firefoxs-built-in-html-sanitizer-to-protect-against-uxss-bugs\/\">Mozilla Security Blog<\/a><\/em><\/p>\n<p>I recently gave a talk at OWASP Global AppSec in Amsterdam and summarized the presentation in a blog post about <a href=\"https:\/\/frederik-braun.com\/firefox-ui-xss-leading-to-rce.html\"><em>how to achieve &#8220;critical&#8221;-rated code execution vulnerabilities in Firefox with user-interface XSS<\/em><\/a>. 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.<\/p>\n<p>Our about: pages are privileged pages that control the browser (e.g., <code>about:preferences<\/code>, which contains Firefox settings). A successful XSS exploit has to bypass <a href=\"https:\/\/blog.mozilla.org\/security\/2019\/10\/14\/hardening-firefox-against-injection-attacks\/\"><em>the Content Security Policy (CSP), which we have recently added <\/em><\/a>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 <a href=\"https:\/\/www.mozilla.org\/en-US\/security\/client-bug-bounty\/\"><em>bounty pag<\/em><\/a><a href=\"https:\/\/www.mozilla.org\/en-US\/security\/client-bug-bounty\/\"><em>es<\/em><\/a> for more information, including how to submit findings.<\/p>\n<h2>How the Sanitizer works<\/h2>\n<p>The Sanitizer runs in the so-called <a href=\"https:\/\/w3c.github.io\/DOM-Parsing\/#dfn-fragment-parsing-algorithm\"><em>&#8220;fragment parsing&#8221;<\/em><\/a> 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 &#8211; because it is indeed the actual parser.<\/p>\n<p>The line of code that triggers the sanitizer is in <a href=\"https:\/\/searchfox.org\/mozilla-central\/rev\/171109434c6f2fe086af3b2322839b346a112a99\/dom\/base\/nsContentUtils.cpp#4683\"><em>nsContentUtils::ParseFragmentHTML<\/em><\/a> and <em>nsContentUtils::ParseFragmentXML<\/em>. 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.<\/p>\n<p>The sanitizer is implemented as an allow-list of elements, attributes and attribute values in <a href=\"https:\/\/searchfox.org\/mozilla-central\/source\/dom\/base\/nsTreeSanitizer.cpp\"><em>nsTreeSanitizer.cpp<\/em><\/a>. Please consult the allow-list before testing.<\/p>\n<p><strong>Finding a Sanitizer bypass is a hunt for <\/strong><a href=\"https:\/\/en.wikipedia.org\/wiki\/Cross-site_scripting#Mutated_XSS_(mXSS)\"><em><strong>Mutated XSS (mXSS)<\/strong><\/em><\/a><strong> bugs in Firefox<\/strong> \u2013 Unless you find an element in our allow-list that has recently become capable of running script.<\/p>\n<h2>How and where to test<\/h2>\n<p>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 <a href=\"https:\/\/nightly.mozilla.org\/\"><em>latest development version<\/em><\/a>. We often times rewrite lots of code that isn&#8217;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 <a href=\"https:\/\/nightly.mozilla.org\/\"><em>Firefox Nightly<\/em><\/a>. Otherwise, the issues you find in Beta or Release might have already been fixed in Nightly.<\/p>\n<h2>Sanitizer runs in all privileged pages<\/h2>\n<p>Some of Firefox\u2019s 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.<\/p>\n<p>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.<\/p>\n<h3>Using Developer Tools to emulate a vulnerability<\/h3>\n<p>From <code>about:config<\/code>, open The developer tools console (Go to <em>Tools<\/em> in the menu bar. Select <em>Web Developers<\/em>, then <em>Web Console<\/em> (Ctrl+Shift+k)).<\/p>\n<p>To emulate an XSS vulnerability, type this into the console:<\/p>\n<pre><code>document.body.innerHTML = '&lt;img src=x onerror=alert(1)&gt;'<\/code><\/pre>\n<p>Observe how Firefox sanitizes the HTML markup by looking at the error in the console:<\/p>\n<pre><code>\u201cRemoved unsafe attribute. Element: img. Attribute: onerror.\u201d<\/code><\/pre>\n<p>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.<\/p>\n<h3>Finding an actual XSS vulnerability<\/h3>\n<p>Right, so for now we have <em>emulated<\/em> the Cross-Site Scripting (XSS) vulnerability by typing in innerHTML ourselves in the Web Console. That&#8217;s pretty much cheating. But as I said above: <em>What we want to find are sanitizer bypasses. This is a call to test our mitigations.<\/em><\/p>\n<p>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 <a href=\"https:\/\/github.com\/mozilla\/eslint-plugin-no-unsanitized\"><em>eslint-plugin-no-unsanitized<\/em><\/a>.<\/p>\n<h2>Summary<\/h2>\n<p>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\u2019re looking forward to getting some feedback. If you find something, please consult the <a href=\"https:\/\/www.mozilla.org\/en-US\/security\/bug-bounty\/\"><em>Bug Bounty pages<\/em><\/a> on how to report it.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>This post 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 &hellip; <a class=\"go\" href=\"https:\/\/blog.mozilla.org\/attack-and-defense\/2019\/12\/02\/help-test-firefoxs-built-in-html-sanitizer-to-protect-against-uxss-bugs\/\">Read more<\/a><\/p>\n","protected":false},"author":405,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[448815,449215],"tags":[],"coauthors":[280726],"_links":{"self":[{"href":"https:\/\/blog.mozilla.org\/attack-and-defense\/wp-json\/wp\/v2\/posts\/13"}],"collection":[{"href":"https:\/\/blog.mozilla.org\/attack-and-defense\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.mozilla.org\/attack-and-defense\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.mozilla.org\/attack-and-defense\/wp-json\/wp\/v2\/users\/405"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.mozilla.org\/attack-and-defense\/wp-json\/wp\/v2\/comments?post=13"}],"version-history":[{"count":0,"href":"https:\/\/blog.mozilla.org\/attack-and-defense\/wp-json\/wp\/v2\/posts\/13\/revisions"}],"wp:attachment":[{"href":"https:\/\/blog.mozilla.org\/attack-and-defense\/wp-json\/wp\/v2\/media?parent=13"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.mozilla.org\/attack-and-defense\/wp-json\/wp\/v2\/categories?post=13"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.mozilla.org\/attack-and-defense\/wp-json\/wp\/v2\/tags?post=13"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/blog.mozilla.org\/attack-and-defense\/wp-json\/wp\/v2\/coauthors?post=13"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}