01 – Introduction
On November 2, 2023, POC2023 took place as scheduled in South Korea. I was fortunate to attend this conference where YYJB and I presented on the topic of “Modern Chrome Exploit Chain Development.”
Given the title “Modern”, it would indeed be a bit awkward if we didn’t share something relatively new with the attendees. Therefore, after finalizing our topic, I quickly dived into researching V8 sandbox bypasses. Initially, I tried to bypass using binary data fetched, and eventually, I managed to bypass PartitionAlloc, achieving arbitrary read and write of the full address. Thinking I had successfully bypassed it, I quickly finished the presentation and sent it to the organizers, waiting for the conference.
On the early morning of October 30, 2023, at 1 am, a realization hit me that I might have overlooked something. In the middle of the night, an idea popped into my mind, prompting me to test the arbitrary read and write functionality. Recall that during a previous event, the Tianfu Cup, I had exploited a WASM vulnerability using the PartitionAlloc method to achieve arbitrary read and write. So, initially, I didn’t give it much thought this time. I just tested the read and write functionality. However, when I attempted to read and write within WASM, it crashed. I was jolted awake, realizing I hadn’t fully bypassed the V8 sandbox.
Upon further debugging and disassembly analysis, I concluded that: PartitionAlloc had changed from its original version. To be precise, four new mitigation measures had been added:
- We can no longer control the MetadataPage by modifying FreeList. This is because Chrome checks the difference between the current and next address, and if it doesn’t meet the criteria, it directly interrupts (int).
- Once we control the MetadataPage, in the previous x86 version, there’s a logical operation performed on the FreeList address. If the FreeList doesn’t meet the criteria, it directly interrupts (int3).
Partition Alloc free list Mitigation
- Restrictions on Target Address Writing in PartitionAlloc: Neither too small
- nor too large.
I reached a preliminary conclusion: I hadn’t fully bypassed PartitionAlloc Alloc; I had only gained arbitrary read and write capabilities within a certain memory range.
Partition Alloc MetadataPage Mitigation(version 118.0.5993.117)
02 – Searching for New Attacking Surfaces
After confirming that I hadn’t fully bypassed the system, I immediately contacted the event organizers via email to request a later update to my presentation. Then, restless and unable to sleep, I continued my attempts until 3 am when I thought I might have stumbled upon a new bypass method. After a short break, I resumed my efforts at 5 am, and following thorough debugging, I ultimately confirmed a complete bypass.
To bypass the V8 sandbox, we need to achieve full address hijacking and ensure stable shellcode address jumping. In other words, we need reliable RWX/RX address computation. Previously, we disclosed a WASM bypass for the latest version of the V8 sandbox using Function’s native pointer hijacking. However, after our disclosure, Google quickly moved the function parameter allocation in WASM to readable and writable memory, preventing us from placing our desired shellcode within WASM. Here, we first require pointer hijacking, which isn’t too difficult. I believe this is a point Google overlooked, as illustrated below.
Any Chrome researcher, when crafting an exploit, will pinpoint this pointer in memory, then calculate the WASM address, and just like before, inject the shellcode. However, Google hasn’t encapsulated this Native pointer.
After closely examining the JIT-optimized shellcode provided by Manyuemo, I’ve noticed that even in the latest version of Chrome, optimized floating-point numbers are still placed in RWX memory. The challenge lies in reliably calculating this address. I haven’t delved into the source code for address computation post-JIT, as time is of the essence.
I’d like to share a little side note here. Often, after pulling off an exploit, there are points I’m not entirely clear on as to why they worked. With the boss pressing for results and the theoretical foundation not being robust under the given circumstances, all I could do was keep trying to find that method to pop up the calculator. Typically, once an exploit is completed, I’d quickly report to my superiors, and the underlying reasons and principles would be something to delve into later. I believe much of the progress over the past few years has been the result of relentless trial and error.
03 – WASM’s JIT
Drawing from Manyuemo’s JIT function, I suspect that if a V8 sandbox bypass exists, it’s likely to be found in some other peripheral areas. For instance, in WASM, WebAudio, WebSQL, and so on — areas where writing exploits used to be straightforward but where Google has been consistently beefing up security. So, I decided to give WASM function JIT a shot. Sure enough, I discovered that even after optimization, WASM functions would place parameters in RWX memory.
04 – Demonstration
05 – Conclusion
While balancing JIT performance, much like the previous WASM bypasses, we have to consider setting longer, predictable constants like floating-point numbers to R/RW, or concurrently fix their predictable addressing methods. If not, attackers could easily achieve stable shellcode execution.
06 – Reference