The Apple Bug That Fell Near The WebKit Tree

March 14, 2019 | Jasiel Spelman

Pwn2Own Vancouver is right around the corner so it seemed fitting to talk about an old Pwn2Own bug. Because we recently gave a presentation at OffensiveCon on how Adobe improperly patched some vulnerabilities, it seemed only fair to highlight how Apple has had similar issues.

In April of 2018, we received a vulnerability submission about an issue within the JIT engine in JavaScriptCore. It was patched in July of 2018 as ZDI-18-606/CVE-2018-4262.

The vulnerability was in the handling of regular expressions within a JIT-optimized function. If that sentence seems confusing, I'd recommend reading through some of our older blogs, perhaps starting off with this one.

Let’s start off by looking at this proof of concept:

The issue here was caused by the lastIndex property of a regular expression, which is generally a numerical value but is allowed to be set to any JavaScript value. As such, it is possible to set it to an Object with a custom valueOf method that can get executed when executing the RegExp.test or RegExp.exec operations.

This was patched in git commit c46d3aa42a7c2940d68f9354e06034a587c2de03. Abstract interpreter and Data Flow Graph (DFG) clobberize were modified such that they always clobber state when executing RegExp.exec or RegExp.test.

The patch definitely broke the submitted proof of concept, but unfortunately, there were still a couple of issues lurking in nearby code. As part of the patch, the RegExpMatchFast operation was still explicitly allowed to execute without any clobbering of state. This operation is supposed to handle the RegExp.match operation on objects known to be RegExpObject instances.

In Pwn2Own Tokyo, the Fluoroacetate duo brought two Safari JIT vulnerabilities, both of which were also related to handling of RegExp operations within JIT-optimized functions. They're identified by ZDI-19-124/CVE-2019-6217 and ZDI-19-131/CVE-2019-6216.

The first one, identified by ZDI-19-124/CVE-2019-6217, was used as part of Fluoroacetate's iOS Wi-Fi chain. This vulnerability was so close to ZDI-18-606 that I initially worried they had collided, which would have made the chain a partial win at best.

Let's look at the proof of concept for this:

So, how did this not get caught by the patch for ZDI-18-606? The proof of concept for ZDI-18-606 has a g at the end of it, which specifies that it is a global regular expression that is expected to find all matches rather than stopping at the first one. This difference is enough that it results in a completely different code path that got covered, while the code path handling regular expressions that are not global did not. This was patched in git commit 7cf9d2911af9f255e0301ea16604c9fa4af340e2 by modifying the JavaScript built-ins that guard access to the fast call functions.

The second one, identified by ZDI-19-131/CVE-2019-6216, was used as part of Fluoroacetate's Safari chain, and is a variant of the two previously mentioned vulnerabilities.

Let’s look at the proof of concept:

The issue was that JSArray::push() was being called every time the regular expression matched a portion of the string. As such, it was possible to set a custom mutator method on Array.prototype in order to break assumptions made about the potential side effects of executing RegExp.match. This behavior actually violates the spec and amusingly enough, there was an explicit test case in the WebKit repository to ensure this behavior existed. This was patched in git commit 7b9d7c94da59d85ab17ce2fe40056bc1cbbe0887 by removing the non-standard behavior.

There have been a number of Pwn2Own entries where at least one of the bugs in the chain ended up being the result of a failed patch. Now that we’ve covered one example, maybe you’ll find something to bring to a future contest. Maybe we’ll even see one next week. Until then, you can find me on Twitter at @WanderingGlitch, and follow the team for the latest in exploit techniques and security patches.