Skip to content
KitploitKITPLOIT
ToolsExploitsBlog
Log in
Submit
ToolsExploitsBlog
Submit

Hacking, PenTest, and Cybersecurity Tools for Your Security Arsenal!

Kitploit is a directory of hacking, cybersecurity, and pentesting tools. Discover the latest project updates to find vulnerabilities, analyze systems, automate testing, and strengthen your security.

FeedsContactPrivacy© 2026 Kitploit

Tool Directory

Categories

View all categories
Loading categories
CVE-2019-8601 — Exploiting a patched vulnerability in JavaScriptCore | Kitploit
Tools/GitHubGitHub/badaccess11/cve-2019-8601
Vulnerability AnalysisExploitationWeb Application ExploitationLearning & EducationPayload DevelopmentBinary Exploitation
GitHubbadaccess11/cve-2019-8601

CVE-2019-8601

Exploiting a patched vulnerability in JavaScriptCore

View Repository
17346 years agoNot yet reviewed

Most Popular

View all →

Discover the most used tools by our community.

Explore all tools

Browse our collection of tools

View all tools →
Share

Exploiting CVE-2019-8601

This is an exploit for a WebKit vulnerability that was originally discovered by Fluoroacetate during the pwn2own competition in Vancouver. While I did not discover this bug, I wrote this exploit to practice my exploit development skills. The original writeup for this exploit is here from Zero Day Initiative . While this write up is very good and it was instrumental in helping me understand the vulnerability, it is from the point of view of someone verifying the vulnerability. I found that some key details are missing when trying to engineer this exploit from scratch and I hope to fill in some of the gaps that the ZDI write up missed and gain practical skills on how to engineer a complicated exploit from scratch.

Steps to Exploitation

These steps serve as an outline to get arbitrary code execution within JavaScriptCore (JSC), the JavaScript engine for WebKit

  • Identify the vulnerability
  • Trigger the vulnerability and crash with ASAN enabled
  • Get leakAddr and fakeObj primitives
  • Corrupt array butterfly in order to achieve read and write primitives
  • Use read and write primitives to achieve arbitrary code execution within JSC

Identifying the Vulnerability

The vulnerability that will be exploited is an integer overflow that occurs in the code produced by the DFG just in time (JIT) compiler for WebKit. This specifically occurs in the compileNewArrayWithSpread function. This function will be called when code using JavaScript spread syntax to create a new array is JITed by DFG.

compileNewArrayWithSpread

Inside the JITed code, first it will compute the size of the array. It does this by adding the length of each argument passed to the array constructor. As it computes the size for each addition it checks for an overflow of the size. After this it will call the compileAllocateNewArray function passing the length that was computed in this function.

compileAllocateNewArrayWithSize

The compileAllocateNewArray will then pass the length that was computed before to emitAllocateButterfly.

emitAllocateButterfly

The emitAllocateButterfly will then left shift the size 3 bits which is equivalent to multiplying it by 8. However, there is no check for an overflow and thus a number such as 0x20000001 can overflow to 0x8

This c program illustrates this vulnerability:

overflow-example2

overflow-example

We can use this vulnerability to trick the JavaScript engine into thinking we've allocated an array with size 0x20000001 but actually only have allocated enough space for 1 JSValue (8 bytes). This will result in an out-of-bounds (OOB) read and write (R/W) primitive that can then be leveraged to achieve arbitrary R/W and eventually remote code execution (RCE).

  • Identify the vulnerability

    Triggering the Vulnerability with ASAN

In order to confirm that we have an OOB read we will try to trigger this vulnerability on an address sanitizer (ASAN) build of JSC.

To do this from the WebKit directory we can run the commands:

Tools/Scripts/set-webkit-configuration --asan
Tools/Scripts/build-jsc --jsc--only --debug

This will build a debug build of JSC with ASAN enabled allow us to verify whether of not we have successfully triggered the vulnerability.

Here is the first iteration of exploit.js

function jitMe(array){
  return [...array]
}

let dummy = [1.1]
for(let i = 0; i < 200; i++){
  jitMe(dummy);
}

let a = []

let len = 0x20000001                                                                     

for(let i = 0; i < len; i++){
  a[i] = 1.1 
}

jitMe(a)

When running this I get the following error:

Program terminated with signal SIGKILL, Killed. The program no longer exists.

My guess was that too much memory was being consumed when trying to allocate such a large array. In order to confirm this, I added a breakpoint to the JITed code by adding a call to m_jit.breakpoint() inside of the compileNewArrayWithSpread which adds an int3 instruction to the JITed code.

After adding the breakpoint I found that it wasn't hit and then I decided to test a length of 0x20001. I then realized that the code wasn't even being compiled so I added more iterations to activate the DFG compiler

function jitMe(array){
  for(let i = 0; i < 0x4000; i++){
    let x = 1 + 1 
  }
  return [...array]
}

let dummy = [1.1]
for(let i = 0; i < 60; i++){
  print(i)
  jitMe(dummy);
}

let a = []

let len = 0x20000001 

for(let i = 0; i < len; i++){
  a[i] = 1.1 
}

jitMe(a)

Testing the program as is still leads to the SIGKILL however, when testing with a smaller length, the breakpoint gets hit. At this point it still seems to me that JSC is running out of memory when trying to process that huge array.

In order to deal with this, I decided to allocate a smaller a array and then use the spread syntax to use it multiple times when creating the corrupted array resulting in the following exploit.js

function jitMe(array){
  for(let i = 0; i < 0x4000; i++){
    let x = 1 + 1
  }
  return [...array, ...array, ...array, ...array, ...array, ...array, ...array, ...array, ...array, ...array, ...array, ...array, ...array, ...array, ...array, ...array]
}

let dummy = [1.1]
for(let i = 0; i < 100; i++){
  print(i)
  jitMe(dummy);
}

let a = []

let len = 0x20000010 / 0x10

for(let i = 0; i < len; i++){
  a[i] = 1.1
}

jitMe(a)

Using this code we were able to hit the breakpoint without a SIGKILL! As is usually the case, fixing one issue brings out another and we got a SIGABORT instead... Using the gdb bt command we can see that operationNewArrayWithSize was called which called create.backtrace1

It seems strange that our JITed code would be calling operationNewArrayWithSize and it must be that the JITed code had to take a slow path to the JavaScript engine for some reason.

slowcases

We can see in the compileAllocateNewArrayWithSize that there is indeed a bailout to operationNewArrayWithSize. We then need to find out why exactly we are bailing out to the slow case.

We can see that in compileNewArrayWithSpread that shouldConvertLargeSizeToArrayStorage is set to false and that slow path won't be in the compiled code.compileNewArrayWithSpread2

Therefore it makes sense that the slow path is being hit somewhere within emitAllocateJSObject

emitAllocateJSObject

emitAllocateJSObject calls emitAllocateJSCell which in turn calls emitAllocate.

Download Tool