A Tale Of Two Pwnies (Part 2)
When we wrapped up our recent Pwnium event, we praised the creativity of the submissions and resolved to provide write-ups on how the two exploits worked. We already covered Pinkie Pie’s submission in a recent post, and this post will summarize the other winning Pwnium submission: an amazing multi-step exploit from frequent Chromium Security Reward winner Sergey Glazunov.
From the start, one thing that impressed us about this exploit was that it involved no memory corruption at all. It was based on a so-called “Universal Cross-Site Scripting” (or UXSS) bug. The UXSS bug in question (117226) was complicated and actually involved two distinct bugs: a state corruption and an inappropriate firing of events. Individually there was a possible use-after-free condition, but the exploit -- perhaps because of various memory corruption mitigations present in Chromium -- took the route of combining the two bugs to form a “High” severity UXSS bug. However, a Pwnium prize requires demonstrating something “Critical”: a persistent attack against the local user’s account. A UXSS bug alone cannot achieve that.
The exploit still had a long way to go, though, as there are plenty of additional barriers:
- chrome://chromewebdata does not have any sensitive APIs associated with it.
- chrome://a is not same-origin with chrome://b.
- chrome://* origins only have privileges when the backing process is tagged as privileged by the browser process, and this tagging only happens as a result of a top-level navigation to a chrome:// URL.
- The sensitive chrome://* pages generally have CSPs applied that prevent the UXSS bug in question.
The exploit became extremely creative at this point. To get around the defenses, the compromised chrome://chromewebdata origin opened a window to chrome://net-internals, which had an iframe in its structure. Another WebKit bug -- the ability to replace a cross-origin iframe (117583) -- was used to run script that navigated the popped-up window, simply “back” to chrome://net-internals (117417). This caused the browser to reassess the chrome://net-internals URL as a top-level navigation -- granting limited WebUI permissions to the backing process as a side-effect (117418).
As you can imagine, it took us some time to dissect all of this. Distilling the details into a blog post was a further challenge; we’ve glossed over the use of the UXSS bug to bypass pop-up window restrictions. The UXSS bug was actually used three separate times in the exploit. We also omitted details of various other lockdowns we applied in response to the exploit chain.
What’s clear is that Sergey certainly earned his $60k Pwnium reward. He chained together a whopping 14[*] bugs, quirks and missed hardening opportunities. Looking beyond the monetary prize, Sergey has helped make Chromium significantly safer. Besides fixing the array of bugs, we’ve landed hardening measures that will make it much tougher to abuse chrome:// WebUI pages in the future.