19.11.2016 Views

Technical Analysis of the Pegasus Exploits on iOS

eNQc3Ry

eNQc3Ry

SHOW MORE
SHOW LESS

Create successful ePaper yourself

Turn your PDF publications into a flip-book with our unique Google optimized e-Paper software.

<str<strong>on</strong>g>Technical</str<strong>on</strong>g> <str<strong>on</strong>g>Analysis</str<strong>on</strong>g> <str<strong>on</strong>g>of</str<strong>on</strong>g> <str<strong>on</strong>g>the</str<strong>on</strong>g><br />

<str<strong>on</strong>g>Pegasus</str<strong>on</strong>g> <str<strong>on</strong>g>Exploits</str<strong>on</strong>g> <strong>on</strong> <strong>iOS</strong>


Secti<strong>on</strong> 1: <str<strong>on</strong>g>Pegasus</str<strong>on</strong>g> Exploitati<strong>on</strong> <str<strong>on</strong>g>of</str<strong>on</strong>g> Safari (CVE-2016-4657) 2<br />

Background 3<br />

Exploitati<strong>on</strong> 7<br />

Setting up and triggering <str<strong>on</strong>g>the</str<strong>on</strong>g> vulnerability 7<br />

Acquiring an arbitrary read/write primitive 9<br />

Leaking an object address 9<br />

Native code executi<strong>on</strong> 9<br />

Evading detecti<strong>on</strong> 10<br />

Secti<strong>on</strong> 2: Exploitati<strong>on</strong> <str<strong>on</strong>g>of</str<strong>on</strong>g> KASLR by <str<strong>on</strong>g>Pegasus</str<strong>on</strong>g> 11<br />

Differences Between 32 and 64-Bit Binaries 12<br />

API Loading 12<br />

Envir<strong>on</strong>ment Setup and Platform Determinati<strong>on</strong> 13<br />

Defeating KASLR 16<br />

Establishing Read/Write/Execute Primitives <strong>on</strong> Previously Rooted Devices (32-Bit) 19<br />

Thread Manipulati<strong>on</strong> 21<br />

Establishing Communicati<strong>on</strong> Channel (32-Bit) 21<br />

Payload C<strong>on</strong>structi<strong>on</strong> and Kernel Inserti<strong>on</strong> (32-Bit) 22<br />

Payload C<strong>on</strong>structi<strong>on</strong> and Kernel Inserti<strong>on</strong> (64-Bit) 25<br />

Establishing Kernel Read/Write Primitives (32-Bit) 25<br />

Establishing Kernel Read/Write Primitives (64-Bit) 30<br />

Establishing a Kernel Execute Primitive (32-Bit) 31<br />

Patching <str<strong>on</strong>g>the</str<strong>on</strong>g> Kernel to Allow Kernel Port Access 31<br />

Secti<strong>on</strong> 3: Privilege Escalati<strong>on</strong> and Activating <str<strong>on</strong>g>the</str<strong>on</strong>g> Jailbreak Binary 33<br />

System Modificati<strong>on</strong> for Privilege Escalati<strong>on</strong> 34<br />

Disabling Code Signing 34<br />

Remounting <str<strong>on</strong>g>the</str<strong>on</strong>g> Drive 35<br />

Cleanup 36<br />

Next Stage Installati<strong>on</strong> 36<br />

Existing Jailbreak Detecti<strong>on</strong> 37<br />

Secti<strong>on</strong> 4: <str<strong>on</strong>g>Pegasus</str<strong>on</strong>g> Persistence Mechanism 39<br />

<str<strong>on</strong>g>Pegasus</str<strong>on</strong>g> Persistence Mechanism 40<br />

JavaScriptCore Memory Corrupti<strong>on</strong> Issue 40<br />

Exploitati<strong>on</strong> 41<br />

Acquiring an arbitrary read/write primitive 41<br />

Leaking an object address 42<br />

Unsigned native code executi<strong>on</strong> 42<br />

Page 1


Secti<strong>on</strong> 1: <str<strong>on</strong>g>Pegasus</str<strong>on</strong>g> Exploitati<strong>on</strong> <str<strong>on</strong>g>of</str<strong>on</strong>g> Safari (CVE-<br />

2016-4657)<br />

The First Stage <str<strong>on</strong>g>of</str<strong>on</strong>g> Infecti<strong>on</strong><br />

This secti<strong>on</strong> reports <strong>on</strong> first stage <str<strong>on</strong>g>of</str<strong>on</strong>g> <str<strong>on</strong>g>the</str<strong>on</strong>g> <str<strong>on</strong>g>Pegasus</str<strong>on</strong>g> exploit <str<strong>on</strong>g>of</str<strong>on</strong>g> <str<strong>on</strong>g>the</str<strong>on</strong>g> “Trident” zero-day<br />

vulnerabilities <strong>on</strong> <strong>iOS</strong>, discovered by researchers at Lookout and Citizen Lab. The<br />

first stage <str<strong>on</strong>g>of</str<strong>on</strong>g> <str<strong>on</strong>g>the</str<strong>on</strong>g> attack is triggered when <str<strong>on</strong>g>the</str<strong>on</strong>g> user clicks a spear-phishing link that<br />

opens <str<strong>on</strong>g>the</str<strong>on</strong>g> Safari browser. This enables <str<strong>on</strong>g>the</str<strong>on</strong>g> exploit <str<strong>on</strong>g>of</str<strong>on</strong>g> a vulnerability in WebKit’s<br />

JavaScriptCore library (CVE-2016-4657).<br />

Page 2


<str<strong>on</strong>g>Analysis</str<strong>on</strong>g> <str<strong>on</strong>g>of</str<strong>on</strong>g> <str<strong>on</strong>g>the</str<strong>on</strong>g> <str<strong>on</strong>g>Pegasus</str<strong>on</strong>g> Safari Exploit<br />

The first stage <str<strong>on</strong>g>of</str<strong>on</strong>g> <str<strong>on</strong>g>Pegasus</str<strong>on</strong>g> exploits a vulnerability in WebKit’s JavaScriptCore library (CVE-<br />

2016-4657). The exploit uses <str<strong>on</strong>g>the</str<strong>on</strong>g> Safari web browser to run a JavaScript payload that exploits<br />

<str<strong>on</strong>g>the</str<strong>on</strong>g> initial vulnerability to gain arbitrary code executi<strong>on</strong> in <str<strong>on</strong>g>the</str<strong>on</strong>g> c<strong>on</strong>text <str<strong>on</strong>g>of</str<strong>on</strong>g> <str<strong>on</strong>g>the</str<strong>on</strong>g> Safari WebC<strong>on</strong>tent<br />

process.<br />

Background<br />

The vulnerability exists within <str<strong>on</strong>g>the</str<strong>on</strong>g> slowAppend() method <str<strong>on</strong>g>of</str<strong>on</strong>g> MarkedArgumentBuffer and can be<br />

exploited via <str<strong>on</strong>g>the</str<strong>on</strong>g> usage <str<strong>on</strong>g>of</str<strong>on</strong>g> a MarkedArgumentBuffer in <str<strong>on</strong>g>the</str<strong>on</strong>g> static defineProperties() method. The<br />

defineProperties() method accepts as input an object whose own enumerable properties<br />

c<strong>on</strong>stitute descriptors for <str<strong>on</strong>g>the</str<strong>on</strong>g> properties to be defined or modified <strong>on</strong> ano<str<strong>on</strong>g>the</str<strong>on</strong>g>r target object. The<br />

algorithm used to associate each <str<strong>on</strong>g>of</str<strong>on</strong>g> <str<strong>on</strong>g>the</str<strong>on</strong>g>se properties with <str<strong>on</strong>g>the</str<strong>on</strong>g> target object does two iterati<strong>on</strong>s <str<strong>on</strong>g>of</str<strong>on</strong>g><br />

<str<strong>on</strong>g>the</str<strong>on</strong>g> provided list <str<strong>on</strong>g>of</str<strong>on</strong>g> properties. In <str<strong>on</strong>g>the</str<strong>on</strong>g> first pass, each <str<strong>on</strong>g>of</str<strong>on</strong>g> <str<strong>on</strong>g>the</str<strong>on</strong>g> property descriptors is checked for<br />

proper formatting and a PropertyDescriptor object is created that references <str<strong>on</strong>g>the</str<strong>on</strong>g> underlying<br />

value.<br />

size_t numProperties = propertyNames.size();<br />

Vector descriptors;<br />

MarkedArgumentBuffer markBuffer;<br />

for (size_t i = 0; i < numProperties; i++) {<br />

JSValue prop = properties->get(exec, propertyNames[i]);<br />

if (exec->hadExcepti<strong>on</strong>())<br />

return jsNull();<br />

PropertyDescriptor descriptor;<br />

if (!toPropertyDescriptor(exec, prop, descriptor))<br />

return jsNull();<br />

descriptors.append(descriptor);<br />

The sec<strong>on</strong>d pass is performed after each property has been validated. This pass associates<br />

each <str<strong>on</strong>g>of</str<strong>on</strong>g> <str<strong>on</strong>g>the</str<strong>on</strong>g> user-supplied properties with <str<strong>on</strong>g>the</str<strong>on</strong>g> target object, using <str<strong>on</strong>g>the</str<strong>on</strong>g> type specific<br />

defineOwnProperty() method.<br />

for (size_t i = 0; i < numProperties; i++) {<br />

Identifier propertyName = propertyNames[i];<br />

if (exec->propertyNames().isPrivateName(propertyName))<br />

c<strong>on</strong>tinue;<br />

object->methodTable(exec->vm())->defineOwnProperty(object, exec, propertyName,<br />

descriptors[i], true);<br />

This method may result in user-defined JavaScript methods (that are associated with <str<strong>on</strong>g>the</str<strong>on</strong>g><br />

property being defined) being called. Within any <str<strong>on</strong>g>of</str<strong>on</strong>g> <str<strong>on</strong>g>the</str<strong>on</strong>g>se user-defined methods, it is possible<br />

that a garbage collecti<strong>on</strong> cycle may be triggered, resulting in any unmarked heap backed<br />

objects being free()ed. Therefore, it is important that each <str<strong>on</strong>g>of</str<strong>on</strong>g> <str<strong>on</strong>g>the</str<strong>on</strong>g> temporary references to <str<strong>on</strong>g>the</str<strong>on</strong>g>se<br />

Page 3


objects, stored within <str<strong>on</strong>g>the</str<strong>on</strong>g> individual PropertyDescriptors in <str<strong>on</strong>g>the</str<strong>on</strong>g> descriptors vector, be individually<br />

marked to ensure that <str<strong>on</strong>g>the</str<strong>on</strong>g>se references to not become stale. To achieve this, a<br />

MarkedArgumentBuffer is used. This class is intended to temporarily prevent <str<strong>on</strong>g>the</str<strong>on</strong>g> values<br />

appended to it from being garbage collected during <str<strong>on</strong>g>the</str<strong>on</strong>g> period for which it is in scope.<br />

To understand how MarkedArgumentBuffers work we must first understand some basics <str<strong>on</strong>g>of</str<strong>on</strong>g><br />

JavaScriptCore garbage collecti<strong>on</strong>. The garbage collector is resp<strong>on</strong>sible for deallocating objects<br />

that are no l<strong>on</strong>ger referenced and runs at random intervals that increase in frequency as more<br />

memory is used by <str<strong>on</strong>g>the</str<strong>on</strong>g> WebC<strong>on</strong>tent process. To determine whe<str<strong>on</strong>g>the</str<strong>on</strong>g>r an object is referenced, <str<strong>on</strong>g>the</str<strong>on</strong>g><br />

garbage collector walks <str<strong>on</strong>g>the</str<strong>on</strong>g> stack and looks for references to <str<strong>on</strong>g>the</str<strong>on</strong>g> object. References to an<br />

object may also exist <strong>on</strong> <str<strong>on</strong>g>the</str<strong>on</strong>g> applicati<strong>on</strong> heap, however, so an alternate mechanism (which will<br />

be explained in detail below) must be used for <str<strong>on</strong>g>the</str<strong>on</strong>g>se cases.<br />

A MarkedArgumentBuffer initially attempts to maintain an inline stack buffer c<strong>on</strong>taining each<br />

value. When <str<strong>on</strong>g>the</str<strong>on</strong>g> stack is walked within garbage collecti<strong>on</strong>, each value will be noted and <str<strong>on</strong>g>the</str<strong>on</strong>g><br />

underlying objects will avoid deallocati<strong>on</strong>.<br />

class MarkedArgumentBuffer {<br />

...<br />

private:<br />

static c<strong>on</strong>st size_t inlineCapacity = 8;<br />

…<br />

public:<br />

...<br />

MarkedArgumentBuffer()<br />

: m_size(0)<br />

, m_capacity(inlineCapacity)<br />

, m_buffer(m_inlineBuffer)<br />

, m_markSet(0)<br />

{<br />

}<br />

...<br />

void append(JSValue v)<br />

{<br />

if (m_size >= m_capacity)<br />

return slowAppend(v);<br />

slotFor(m_size) = JSValue::encode(v);<br />

++m_size;<br />

}<br />

...<br />

private:<br />

...<br />

int m_size;<br />

int m_capacity;<br />

EncodedJSValue m_inlineBuffer[inlineCapacity];<br />

EncodedJSValue* m_buffer;<br />

ListSet* m_markSet;<br />

The size <str<strong>on</strong>g>of</str<strong>on</strong>g> this inline stack buffer is limited to eight values. When <str<strong>on</strong>g>the</str<strong>on</strong>g> ninth value is added to a<br />

MarkedArgumentBuffer, <str<strong>on</strong>g>the</str<strong>on</strong>g> underlying buffer is moved to <str<strong>on</strong>g>the</str<strong>on</strong>g> heap and <str<strong>on</strong>g>the</str<strong>on</strong>g> capacity is<br />

expanded.<br />

Page 4


void MarkedArgumentBuffer::slowAppend(JSValue v)<br />

{<br />

int newCapacity = m_capacity * 4;<br />

EncodedJSValue* newBuffer = new EncodedJSValue[newCapacity];<br />

for (int i = 0; i < m_capacity; ++i)<br />

newBuffer[i] = m_buffer[i];<br />

if (EncodedJSValue* base = mallocBase())<br />

delete [] base;<br />

m_buffer = newBuffer;<br />

m_capacity = newCapacity;<br />

Once <str<strong>on</strong>g>the</str<strong>on</strong>g> underlying buffer has moved to <str<strong>on</strong>g>the</str<strong>on</strong>g> heap, values are not automatically protected from<br />

garbage collecti<strong>on</strong>. To ensure that <str<strong>on</strong>g>the</str<strong>on</strong>g>se objects are not deallocated, <str<strong>on</strong>g>the</str<strong>on</strong>g> garbage collector<br />

performs a MarkingArgumentBuffers phase in which each value c<strong>on</strong>tained within a<br />

MarkedArgumentBuffer that has been added to <str<strong>on</strong>g>the</str<strong>on</strong>g> Heap’s m_markListSet is marked (marking a<br />

cell ensures that it will not be deallocated in a particular garbage collecti<strong>on</strong> cycle). For this<br />

method <str<strong>on</strong>g>of</str<strong>on</strong>g> marking to work, <str<strong>on</strong>g>the</str<strong>on</strong>g> MarkedArgumentBuffer must be added to <str<strong>on</strong>g>the</str<strong>on</strong>g> markListSet at <str<strong>on</strong>g>the</str<strong>on</strong>g><br />

same time that <str<strong>on</strong>g>the</str<strong>on</strong>g> MarkedArgumentBuffer’s underlying values are moved to <str<strong>on</strong>g>the</str<strong>on</strong>g> heap.<br />

// As l<strong>on</strong>g as our size stays within our Vector's inline<br />

// capacity, all our values are allocated <strong>on</strong> <str<strong>on</strong>g>the</str<strong>on</strong>g> stack, and<br />

// <str<strong>on</strong>g>the</str<strong>on</strong>g>refore d<strong>on</strong>'t need explicit marking. Once our size exceeds<br />

// our Vector's inline capacity, though, our values move to <str<strong>on</strong>g>the</str<strong>on</strong>g><br />

// heap, where <str<strong>on</strong>g>the</str<strong>on</strong>g>y do need explicit marking.<br />

for (int i = 0; i < m_size; ++i) {<br />

Heap* heap = Heap::heap(JSValue::decode(slotFor(i)));<br />

if (!heap)<br />

c<strong>on</strong>tinue;<br />

}<br />

m_markSet = &heap->markListSet();<br />

m_markSet->add(this);<br />

break;<br />

The above code attempts to acquire <str<strong>on</strong>g>the</str<strong>on</strong>g> heap c<strong>on</strong>text for a value and add <str<strong>on</strong>g>the</str<strong>on</strong>g><br />

MarkedArgumentBuffer to <str<strong>on</strong>g>the</str<strong>on</strong>g> Heap’s markListSet. However, this is <strong>on</strong>ly attempted <strong>on</strong>ce for <str<strong>on</strong>g>the</str<strong>on</strong>g><br />

ninth value added to <str<strong>on</strong>g>the</str<strong>on</strong>g> MarkedArgumentBuffer.<br />

inline Heap* Heap::heap(c<strong>on</strong>st JSValue v)<br />

{<br />

if (!v.isCell())<br />

return 0;<br />

return heap(v.asCell());<br />

}<br />

A JSValue c<strong>on</strong>tains a tag which describes <str<strong>on</strong>g>the</str<strong>on</strong>g> type <str<strong>on</strong>g>of</str<strong>on</strong>g> <str<strong>on</strong>g>the</str<strong>on</strong>g> value that it encodes. In <str<strong>on</strong>g>the</str<strong>on</strong>g> case <str<strong>on</strong>g>of</str<strong>on</strong>g><br />

complex objects this tag will be CellTag and <str<strong>on</strong>g>the</str<strong>on</strong>g> JSValue will encode a pointer to an underlying<br />

item <strong>on</strong> <str<strong>on</strong>g>the</str<strong>on</strong>g> Heap. Alternatively, for simple types where <str<strong>on</strong>g>the</str<strong>on</strong>g> entire underlying value <str<strong>on</strong>g>of</str<strong>on</strong>g> <str<strong>on</strong>g>the</str<strong>on</strong>g><br />

variable can be encoded directly into a JSValue (ex. Integers, Booleans, null, and undefined),<br />

storing <str<strong>on</strong>g>the</str<strong>on</strong>g> value <strong>on</strong> <str<strong>on</strong>g>the</str<strong>on</strong>g> heap would be redundant and a different identifying tag will be used.<br />

The functi<strong>on</strong> JSValue::isCell() is used to determine whe<str<strong>on</strong>g>the</str<strong>on</strong>g>r a JSValue encodes a pointer to a<br />

Page 5


cell <strong>on</strong> <str<strong>on</strong>g>the</str<strong>on</strong>g> Heap. Because simple types do not point to <str<strong>on</strong>g>the</str<strong>on</strong>g> heap, attempting to acquire <str<strong>on</strong>g>the</str<strong>on</strong>g> Heap<br />

(via a call to Heap::heap()) for <str<strong>on</strong>g>the</str<strong>on</strong>g>se types has no meaning and will <str<strong>on</strong>g>the</str<strong>on</strong>g>refore return NULL.<br />

inline bool JSValue::isCell() c<strong>on</strong>st<br />

{<br />

return !(u.asInt64 & TagMask);<br />

}<br />

As a result, if <str<strong>on</strong>g>the</str<strong>on</strong>g> ninth value added to a MarkedArgumentBuffer is not a heap backed value,<br />

attempting to acquire <str<strong>on</strong>g>the</str<strong>on</strong>g> Heap c<strong>on</strong>text will return NULL and <str<strong>on</strong>g>the</str<strong>on</strong>g> MarkedArgumentBuffer will<br />

never be added to <str<strong>on</strong>g>the</str<strong>on</strong>g> Heap’s markListSet. This means that <str<strong>on</strong>g>the</str<strong>on</strong>g> MarkedArgumentBuffer will no<br />

l<strong>on</strong>ger serve its purpose (to protect <str<strong>on</strong>g>the</str<strong>on</strong>g> items that it c<strong>on</strong>tains from deallocati<strong>on</strong>) for any item after<br />

<str<strong>on</strong>g>the</str<strong>on</strong>g> ninth. Any reference to a heap backed property (after <str<strong>on</strong>g>the</str<strong>on</strong>g> ninth) c<strong>on</strong>tained within <str<strong>on</strong>g>the</str<strong>on</strong>g><br />

descriptors vector has <str<strong>on</strong>g>the</str<strong>on</strong>g> potential to go stale. In reality, at least <strong>on</strong>e o<str<strong>on</strong>g>the</str<strong>on</strong>g>r reference to <str<strong>on</strong>g>the</str<strong>on</strong>g>se<br />

values still exists (<str<strong>on</strong>g>the</str<strong>on</strong>g> JavaScript variable that was passed to defineProperties()). In order for <str<strong>on</strong>g>the</str<strong>on</strong>g><br />

references within <str<strong>on</strong>g>the</str<strong>on</strong>g> descriptors vector to go stale, <str<strong>on</strong>g>the</str<strong>on</strong>g>se remaining references to <str<strong>on</strong>g>the</str<strong>on</strong>g> JSValue<br />

must also be removed before garbage collecti<strong>on</strong> occurs.<br />

The call to defineOwnProperty() (within <str<strong>on</strong>g>the</str<strong>on</strong>g> sec<strong>on</strong>d loop <str<strong>on</strong>g>of</str<strong>on</strong>g> defineProperties()) may result in<br />

calling user-c<strong>on</strong>trolled methods defined <strong>on</strong> property values. As a result, <str<strong>on</strong>g>the</str<strong>on</strong>g> last marked<br />

references to a property value could be removed within this user-defined JavaScript code. If<br />

garbage collecti<strong>on</strong> can be triggered between <str<strong>on</strong>g>the</str<strong>on</strong>g> removal <str<strong>on</strong>g>of</str<strong>on</strong>g> all remaining references to a<br />

property value and <str<strong>on</strong>g>the</str<strong>on</strong>g> (now stale) value from <str<strong>on</strong>g>the</str<strong>on</strong>g> descriptors vector being defined <strong>on</strong> <str<strong>on</strong>g>the</str<strong>on</strong>g> target<br />

object, a reference to free()ed memory will be defined as a property <strong>on</strong> <str<strong>on</strong>g>the</str<strong>on</strong>g> target object.<br />

Page 6


Exploitati<strong>on</strong><br />

The <str<strong>on</strong>g>Pegasus</str<strong>on</strong>g> exploit triggers this vulnerability by passing a specifically crafted sequence <str<strong>on</strong>g>of</str<strong>on</strong>g><br />

properties to <str<strong>on</strong>g>the</str<strong>on</strong>g> defineProperties() method. When <str<strong>on</strong>g>the</str<strong>on</strong>g>se individual properties are subsequently<br />

inserted into a MarkedArgumentBuffer <str<strong>on</strong>g>the</str<strong>on</strong>g> vulnerability is triggered such that a JSArray object<br />

will be improperly deallocated if garbage collecti<strong>on</strong> can be triggered at a critical point in time.<br />

Because garbage collecti<strong>on</strong> can not be triggered deterministically, <str<strong>on</strong>g>the</str<strong>on</strong>g> exploit makes repeated<br />

attempts to trigger <str<strong>on</strong>g>the</str<strong>on</strong>g> improper deallocati<strong>on</strong> and subsequent reallocati<strong>on</strong> (for a total <str<strong>on</strong>g>of</str<strong>on</strong>g> ten<br />

attempts), testing each time whe<str<strong>on</strong>g>the</str<strong>on</strong>g>r a stale reference has been successfully acquired.<br />

Assuming garbage collecti<strong>on</strong> has been triggered at <str<strong>on</strong>g>the</str<strong>on</strong>g> correct time, ano<str<strong>on</strong>g>the</str<strong>on</strong>g>r object is allocated<br />

over <str<strong>on</strong>g>the</str<strong>on</strong>g> top <str<strong>on</strong>g>of</str<strong>on</strong>g> <str<strong>on</strong>g>the</str<strong>on</strong>g> now stale JSArray. The exploit <str<strong>on</strong>g>the</str<strong>on</strong>g>n sets up <str<strong>on</strong>g>the</str<strong>on</strong>g> tools needed to gain arbitrary<br />

native code executi<strong>on</strong>, namely a read/write primitive and <str<strong>on</strong>g>the</str<strong>on</strong>g> ability to leak <str<strong>on</strong>g>the</str<strong>on</strong>g> address <str<strong>on</strong>g>of</str<strong>on</strong>g> an<br />

arbitrary JavaScript object. Once this is complete <str<strong>on</strong>g>the</str<strong>on</strong>g> exploit can create an executable mapping<br />

c<strong>on</strong>taining <str<strong>on</strong>g>the</str<strong>on</strong>g> native code payload. The following secti<strong>on</strong>s detail <str<strong>on</strong>g>the</str<strong>on</strong>g> various stages <str<strong>on</strong>g>of</str<strong>on</strong>g> this<br />

process.<br />

Setting up and triggering <str<strong>on</strong>g>the</str<strong>on</strong>g> vulnerability<br />

In order to achieve arbitrary code executi<strong>on</strong>, <str<strong>on</strong>g>the</str<strong>on</strong>g> exploit triggers <str<strong>on</strong>g>the</str<strong>on</strong>g> vulnerable code path using<br />

a JSArray object. The following pseudo code is used to trigger <str<strong>on</strong>g>the</str<strong>on</strong>g> vulnerability.<br />

var arr = new Array(2047);<br />

var not_number = {};<br />

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

arr = null;<br />

props["stale"]["value"] = null;<br />

… // Trigger garbage collecti<strong>on</strong> and reallocati<strong>on</strong> over stale object<br />

return 10;<br />

};<br />

var props = {<br />

p0 : { value : 0 },<br />

p1 : { value : 1 },<br />

p2 : { value : 2 },<br />

p3 : { value : 3 },<br />

p4 : { value : 4 },<br />

p5 : { value : 5 },<br />

p6 : { value : 6 },<br />

p7 : { value : 7 },<br />

p8 : { value : 8 },<br />

length : { value : not_number },<br />

stale : { value : arr },<br />

after : { value : 666 }<br />

};<br />

var target = [];<br />

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

The specified props object has been specifically crafted to trigger <str<strong>on</strong>g>the</str<strong>on</strong>g> vulnerability in<br />

slowAppend(). When <str<strong>on</strong>g>the</str<strong>on</strong>g> ninth property is added to <str<strong>on</strong>g>the</str<strong>on</strong>g> MarkedArgumentBuffer (p8),<br />

slowAppend() will fail to acquire a heap c<strong>on</strong>text (because <str<strong>on</strong>g>the</str<strong>on</strong>g> value is simple type, an integer,<br />

and not backed by an item <strong>on</strong> <str<strong>on</strong>g>the</str<strong>on</strong>g> heap). Subsequent Heap-backed values (not_number and<br />

Page 7


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


variables <str<strong>on</strong>g>of</str<strong>on</strong>g> <str<strong>on</strong>g>the</str<strong>on</strong>g> JSGenericTypedArrayView can be read or corrupted. Specifically, <str<strong>on</strong>g>the</str<strong>on</strong>g> exploit<br />

writes to an <str<strong>on</strong>g>of</str<strong>on</strong>g>fset into <str<strong>on</strong>g>the</str<strong>on</strong>g> stale JSArray that overlaps with <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><br />

JSGenericTypedArrayView, effectively setting <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> Uint32Array to 0xFFFFFFFF.<br />

Corrupting this value will allow <str<strong>on</strong>g>the</str<strong>on</strong>g> array to be treated as a view <str<strong>on</strong>g>of</str<strong>on</strong>g> <str<strong>on</strong>g>the</str<strong>on</strong>g> entire virtual address<br />

space <str<strong>on</strong>g>of</str<strong>on</strong>g> <str<strong>on</strong>g>the</str<strong>on</strong>g> WebC<strong>on</strong>tent process (an arbitrary read/write primitive).<br />

The exploit still must determine which <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 that were allocated aligns<br />

with <str<strong>on</strong>g>the</str<strong>on</strong>g> stale object. This can be determined by iterating through each <str<strong>on</strong>g>of</str<strong>on</strong>g> <str<strong>on</strong>g>the</str<strong>on</strong>g> arrays and<br />

checking whe<str<strong>on</strong>g>the</str<strong>on</strong>g>r <str<strong>on</strong>g>the</str<strong>on</strong>g> length has changed to 0xFFFFFFFF. All o<str<strong>on</strong>g>the</str<strong>on</strong>g>r arrays will still have <str<strong>on</strong>g>the</str<strong>on</strong>g><br />

original backing ArrayBuffer (or a length <str<strong>on</strong>g>of</str<strong>on</strong>g> 80 / 4).<br />

for (x = attempts.length - 1; x >= 1; x--) {<br />

if (attempts[x].length != 80 / 4) {<br />

if (attempts[x].length == 0xFFFFFFFF) {<br />

memory_view = attempts[x];<br />

...<br />

break;<br />

Leaking an object address<br />

The final comp<strong>on</strong>ent needed to complete <str<strong>on</strong>g>the</str<strong>on</strong>g> exploit is <str<strong>on</strong>g>the</str<strong>on</strong>g> ability to leak <str<strong>on</strong>g>the</str<strong>on</strong>g> address <str<strong>on</strong>g>of</str<strong>on</strong>g> an<br />

arbitrary JavaScript object. The <str<strong>on</strong>g>Pegasus</str<strong>on</strong>g> exploit accomplishes this using <str<strong>on</strong>g>the</str<strong>on</strong>g> same mechanism<br />

that was used to corrupt <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> Uint32Array used for <str<strong>on</strong>g>the</str<strong>on</strong>g> read/write primitive. By<br />

writing to an <str<strong>on</strong>g>of</str<strong>on</strong>g>fset into <str<strong>on</strong>g>the</str<strong>on</strong>g> stale object, <str<strong>on</strong>g>the</str<strong>on</strong>g> buffer <str<strong>on</strong>g>of</str<strong>on</strong>g> a Uint32Array is corrupted to point to a<br />

user-c<strong>on</strong>trolled JSArray. By setting <str<strong>on</strong>g>the</str<strong>on</strong>g> first element <str<strong>on</strong>g>of</str<strong>on</strong>g> that JSArray to <str<strong>on</strong>g>the</str<strong>on</strong>g> JavaScript object to<br />

be leaked (by corrupting <str<strong>on</strong>g>the</str<strong>on</strong>g> pointer to <str<strong>on</strong>g>the</str<strong>on</strong>g> underlying storage <str<strong>on</strong>g>of</str<strong>on</strong>g> <str<strong>on</strong>g>the</str<strong>on</strong>g> Uint32Array), <str<strong>on</strong>g>the</str<strong>on</strong>g> object’s<br />

address can be read back out <str<strong>on</strong>g>of</str<strong>on</strong>g> <str<strong>on</strong>g>the</str<strong>on</strong>g> Uint32Array.<br />

Native code executi<strong>on</strong><br />

All that is left to do for <str<strong>on</strong>g>the</str<strong>on</strong>g> first stage <str<strong>on</strong>g>of</str<strong>on</strong>g> <str<strong>on</strong>g>the</str<strong>on</strong>g> <str<strong>on</strong>g>Pegasus</str<strong>on</strong>g> exploit is to create an executable mapping<br />

that will c<strong>on</strong>tain <str<strong>on</strong>g>the</str<strong>on</strong>g> shellcode to be executed. To accomplish this purpose, a JSFuncti<strong>on</strong> object<br />

is created (c<strong>on</strong>taining hundreds <str<strong>on</strong>g>of</str<strong>on</strong>g> empty try/catch blocks that will later be overwritten). To help<br />

ensure that <str<strong>on</strong>g>the</str<strong>on</strong>g> JavaScript will be compiled into native code by <str<strong>on</strong>g>the</str<strong>on</strong>g> JIT, <str<strong>on</strong>g>the</str<strong>on</strong>g> functi<strong>on</strong> is<br />

subsequently called repeatedly. This behavior ensures that <str<strong>on</strong>g>the</str<strong>on</strong>g> JIT compiled (JITed) versi<strong>on</strong> <str<strong>on</strong>g>of</str<strong>on</strong>g><br />

<str<strong>on</strong>g>the</str<strong>on</strong>g> functi<strong>on</strong> (which will later be overwritten) will be marked as high priority code that is likely to<br />

be called regularly and should <str<strong>on</strong>g>the</str<strong>on</strong>g>refore not be released. Because <str<strong>on</strong>g>of</str<strong>on</strong>g> <str<strong>on</strong>g>the</str<strong>on</strong>g> way that <str<strong>on</strong>g>the</str<strong>on</strong>g><br />

JavaScriptCore engine handles JITed code, this will reside in an area <str<strong>on</strong>g>of</str<strong>on</strong>g> memory that is mapped<br />

as read/write/execute.<br />

var body = ''<br />

for (var k = 0; k < 0x600; k++) {<br />

body += 'try {} catch(e) {};';<br />

}<br />

var to_overwrite = new Functi<strong>on</strong>('a', body);<br />

for (var i = 0; i < 0x10000; i++) {<br />

Page 9


}<br />

to_overwrite();<br />

The address <str<strong>on</strong>g>of</str<strong>on</strong>g> this JSFuncti<strong>on</strong> object can <str<strong>on</strong>g>the</str<strong>on</strong>g>n be leaked and <str<strong>on</strong>g>the</str<strong>on</strong>g> various members can be<br />

read to acquire <str<strong>on</strong>g>the</str<strong>on</strong>g> address <str<strong>on</strong>g>of</str<strong>on</strong>g> <str<strong>on</strong>g>the</str<strong>on</strong>g> RWX mapping. The JITed versi<strong>on</strong> <str<strong>on</strong>g>of</str<strong>on</strong>g> <str<strong>on</strong>g>the</str<strong>on</strong>g> try/catch blocks are<br />

<str<strong>on</strong>g>the</str<strong>on</strong>g>n overwritten with shellcode, and <str<strong>on</strong>g>the</str<strong>on</strong>g> to_overwrite() functi<strong>on</strong> can simply be called to achieve<br />

arbitrary code executi<strong>on</strong>.<br />

Evading detecti<strong>on</strong><br />

When exploitati<strong>on</strong> fails, <str<strong>on</strong>g>the</str<strong>on</strong>g> <str<strong>on</strong>g>Pegasus</str<strong>on</strong>g> exploit c<strong>on</strong>tains a bailout code path, presumably to ensure<br />

that crash dumps do not expose <str<strong>on</strong>g>the</str<strong>on</strong>g> exploitable vulnerability. This bailout code triggers a crash<br />

<strong>on</strong> a clean NULL dereference. Most likely, an analyst analyzing such a crash dump would<br />

quickly identify <str<strong>on</strong>g>the</str<strong>on</strong>g> bug as a n<strong>on</strong>-exploitable NULL pointer dereference and not suspect anything<br />

more sinister. The following code is used to trigger this “clean” crash.<br />

window.__proto__.__proto__ = null;<br />

x = new String("a");<br />

x.__proto__.__proto__.__proto__ = window;<br />

x.Audio;<br />

Page 10


Secti<strong>on</strong> 2: Exploitati<strong>on</strong> <str<strong>on</strong>g>of</str<strong>on</strong>g> KASLR by <str<strong>on</strong>g>Pegasus</str<strong>on</strong>g><br />

Stage Two <str<strong>on</strong>g>of</str<strong>on</strong>g> Infecti<strong>on</strong>: Kernel Locati<strong>on</strong> Disclosure<br />

Once <str<strong>on</strong>g>the</str<strong>on</strong>g> attack is launched in <str<strong>on</strong>g>the</str<strong>on</strong>g> first stage, <str<strong>on</strong>g>the</str<strong>on</strong>g> sec<strong>on</strong>d stage exploits a<br />

kernel informati<strong>on</strong> leak (CVE-2016-4655). This prepares <str<strong>on</strong>g>the</str<strong>on</strong>g> device for <str<strong>on</strong>g>the</str<strong>on</strong>g><br />

kernel memory corrupti<strong>on</strong> (CVE-2016-4656) that ultimately leads to jailbreak.<br />

Page 11


<str<strong>on</strong>g>Analysis</str<strong>on</strong>g> <str<strong>on</strong>g>of</str<strong>on</strong>g> <str<strong>on</strong>g>Pegasus</str<strong>on</strong>g> KASLR Exploit<br />

The sec<strong>on</strong>d stage, Stage 2, is resp<strong>on</strong>sible for escalating privileges <strong>on</strong> <str<strong>on</strong>g>the</str<strong>on</strong>g> victim’s iPh<strong>on</strong>e and<br />

establishing an envir<strong>on</strong>ment where jailbreaking <str<strong>on</strong>g>the</str<strong>on</strong>g> victim’s device is possible. The Stage 2<br />

binary is used in two distinct c<strong>on</strong>texts within <str<strong>on</strong>g>Pegasus</str<strong>on</strong>g>. By default, Stage 2 c<strong>on</strong>stitutes a<br />

complete <strong>iOS</strong> kernel exploit. Alternatively, <str<strong>on</strong>g>the</str<strong>on</strong>g> Stage 2 binary attempts to detect <strong>iOS</strong> devices<br />

that have already been jailbroken and, in cases where an existing jailbreak is detected (and has<br />

installed a known backdoor), uses <str<strong>on</strong>g>the</str<strong>on</strong>g> pre-existing backdoor mechanisms to install <str<strong>on</strong>g>Pegasus</str<strong>on</strong>g><br />

specific kernel patches.<br />

In order to perform <str<strong>on</strong>g>the</str<strong>on</strong>g>se tasks, Stage 2 must first determine <str<strong>on</strong>g>the</str<strong>on</strong>g> locati<strong>on</strong> <str<strong>on</strong>g>of</str<strong>on</strong>g> <str<strong>on</strong>g>the</str<strong>on</strong>g> kernel in<br />

memory, escalate its own privileges, disable safeguards, and <str<strong>on</strong>g>the</str<strong>on</strong>g>n install <str<strong>on</strong>g>the</str<strong>on</strong>g> necessary tools for<br />

jailbreaking a device. In order to accommodate multiple iPh<strong>on</strong>e versi<strong>on</strong>s, Stage 2 comes in two<br />

flavors, 32-bit and 64-bit. Toge<str<strong>on</strong>g>the</str<strong>on</strong>g>r, <str<strong>on</strong>g>the</str<strong>on</strong>g> two versi<strong>on</strong>s <str<strong>on</strong>g>of</str<strong>on</strong>g> <str<strong>on</strong>g>the</str<strong>on</strong>g> Stage 2 binary target a total <str<strong>on</strong>g>of</str<strong>on</strong>g> 199<br />

iPh<strong>on</strong>e combinati<strong>on</strong>s.<br />

The Stage 2 variants share a lot <str<strong>on</strong>g>of</str<strong>on</strong>g> design similarities, but deviate enough in <str<strong>on</strong>g>the</str<strong>on</strong>g>ir approach that<br />

it is best to look at each variant in relative isolati<strong>on</strong>. The subsecti<strong>on</strong>s that follow will walk through<br />

<str<strong>on</strong>g>the</str<strong>on</strong>g> steps involved in each <str<strong>on</strong>g>of</str<strong>on</strong>g> <str<strong>on</strong>g>the</str<strong>on</strong>g> Stage 2 variants while pointing out areas <str<strong>on</strong>g>of</str<strong>on</strong>g> similarity between<br />

<str<strong>on</strong>g>the</str<strong>on</strong>g> variants when <str<strong>on</strong>g>the</str<strong>on</strong>g>y arise.<br />

Differences Between 32 and 64-Bit Binaries<br />

The 32-bit Stage 2 binary (or simply “32Stage2”) operates <strong>on</strong> <str<strong>on</strong>g>the</str<strong>on</strong>g> older iPh<strong>on</strong>e models (iPh<strong>on</strong>e<br />

4S through iPh<strong>on</strong>e 5c) and targets <strong>iOS</strong> 9.0 through <strong>iOS</strong> 9.3.3. The 64-bit Stage 2 binary (or<br />

simply “64Stage2”) operates <strong>on</strong> <str<strong>on</strong>g>the</str<strong>on</strong>g> newer iPh<strong>on</strong>e models (iPh<strong>on</strong>e 5S and later) and targets <strong>iOS</strong><br />

9.0 through <strong>iOS</strong> 9.3.3. Both binaries perform <str<strong>on</strong>g>the</str<strong>on</strong>g> same general steps and exploit <str<strong>on</strong>g>the</str<strong>on</strong>g> same<br />

underlying vulnerabilities. However, <str<strong>on</strong>g>the</str<strong>on</strong>g> exploitati<strong>on</strong> <str<strong>on</strong>g>of</str<strong>on</strong>g> <str<strong>on</strong>g>the</str<strong>on</strong>g>se vulnerabilities varies between<br />

versi<strong>on</strong>s. In areas where <str<strong>on</strong>g>the</str<strong>on</strong>g> mechanisms differ substantially <str<strong>on</strong>g>the</str<strong>on</strong>g> differences will be specifically<br />

noted or discussed separately.<br />

API Loading<br />

Stage 2 requires a number <str<strong>on</strong>g>of</str<strong>on</strong>g> API functi<strong>on</strong>s to be present in order to succeed. In order to ensure<br />

<str<strong>on</strong>g>the</str<strong>on</strong>g> functi<strong>on</strong>s are available, Stage 2 dynamically loads <str<strong>on</strong>g>the</str<strong>on</strong>g> necessary API functi<strong>on</strong> addresses via<br />

dlsym calls. While dynamically resolving API functi<strong>on</strong> addresses is by no means a novel<br />

technique for malware, what is interesting about Stage 2’s API loading is <str<strong>on</strong>g>the</str<strong>on</strong>g> fact that <str<strong>on</strong>g>the</str<strong>on</strong>g><br />

authors <str<strong>on</strong>g>of</str<strong>on</strong>g> <str<strong>on</strong>g>the</str<strong>on</strong>g> binary reload many <str<strong>on</strong>g>of</str<strong>on</strong>g> <str<strong>on</strong>g>the</str<strong>on</strong>g> API functi<strong>on</strong>s multiple times. In <str<strong>on</strong>g>the</str<strong>on</strong>g> main functi<strong>on</strong><br />

al<strong>on</strong>e, a large number <str<strong>on</strong>g>of</str<strong>on</strong>g> API functi<strong>on</strong> addresses are loaded with <strong>on</strong>ly a small subset <str<strong>on</strong>g>of</str<strong>on</strong>g> those<br />

functi<strong>on</strong>s ever finding <str<strong>on</strong>g>the</str<strong>on</strong>g>mselves used during <str<strong>on</strong>g>the</str<strong>on</strong>g> course <str<strong>on</strong>g>of</str<strong>on</strong>g> Stage 2’s executi<strong>on</strong> (for example,<br />

<str<strong>on</strong>g>the</str<strong>on</strong>g> address <str<strong>on</strong>g>of</str<strong>on</strong>g> socket is loaded into memory but is never called). After loading <str<strong>on</strong>g>the</str<strong>on</strong>g> initial set <str<strong>on</strong>g>of</str<strong>on</strong>g><br />

Page 12


API functi<strong>on</strong>s, 32stage2 calls a subroutine (identified in this report as initialize) that in turn<br />

calls several o<str<strong>on</strong>g>the</str<strong>on</strong>g>r subroutines, each <str<strong>on</strong>g>of</str<strong>on</strong>g> which is resp<strong>on</strong>sible for loading additi<strong>on</strong>al API functi<strong>on</strong>s<br />

in additi<strong>on</strong> to performing various startup tasks.<br />

The grouping <str<strong>on</strong>g>of</str<strong>on</strong>g> <str<strong>on</strong>g>the</str<strong>on</strong>g> API functi<strong>on</strong>s being loaded (in terms <str<strong>on</strong>g>of</str<strong>on</strong>g> which API functi<strong>on</strong>s are loading by<br />

which Stage 2 functi<strong>on</strong>s) and <str<strong>on</strong>g>the</str<strong>on</strong>g> inclusi<strong>on</strong> <str<strong>on</strong>g>of</str<strong>on</strong>g> multiple API functi<strong>on</strong>s being loaded multiple times<br />

suggests that <str<strong>on</strong>g>the</str<strong>on</strong>g> API loading is specific to individual comp<strong>on</strong>ents or operati<strong>on</strong>s <str<strong>on</strong>g>of</str<strong>on</strong>g> <str<strong>on</strong>g>the</str<strong>on</strong>g> Stage 2<br />

binary. For instance, as discussed later, a pair <str<strong>on</strong>g>of</str<strong>on</strong>g> functi<strong>on</strong>s are resp<strong>on</strong>sible for decompressing<br />

<str<strong>on</strong>g>the</str<strong>on</strong>g> jailbreak files, changing <str<strong>on</strong>g>the</str<strong>on</strong>g>ir permissi<strong>on</strong>s via chmod, and positi<strong>on</strong>ing <str<strong>on</strong>g>the</str<strong>on</strong>g> files in <str<strong>on</strong>g>the</str<strong>on</strong>g> correct<br />

locati<strong>on</strong> <strong>on</strong> <str<strong>on</strong>g>the</str<strong>on</strong>g> victim’s iPh<strong>on</strong>e. The API functi<strong>on</strong>s resp<strong>on</strong>sible for <str<strong>on</strong>g>the</str<strong>on</strong>g>se operati<strong>on</strong>s are all<br />

loaded by a self-c<strong>on</strong>tained functi<strong>on</strong>. The loading functi<strong>on</strong> <strong>on</strong>ly loads those API functi<strong>on</strong>s that are<br />

necessary for <str<strong>on</strong>g>the</str<strong>on</strong>g> described operati<strong>on</strong>s, and <str<strong>on</strong>g>the</str<strong>on</strong>g> APIs are not shared with any o<str<strong>on</strong>g>the</str<strong>on</strong>g>r part <str<strong>on</strong>g>of</str<strong>on</strong>g> <str<strong>on</strong>g>the</str<strong>on</strong>g><br />

Stage 2 system.<br />

The analysis <str<strong>on</strong>g>of</str<strong>on</strong>g> Stage 2 was also made somewhat easier given <str<strong>on</strong>g>the</str<strong>on</strong>g> heavy use <str<strong>on</strong>g>of</str<strong>on</strong>g> debug logging<br />

throughout <str<strong>on</strong>g>the</str<strong>on</strong>g> binary. Calls to <str<strong>on</strong>g>the</str<strong>on</strong>g> logging sub-system generally reference <str<strong>on</strong>g>the</str<strong>on</strong>g> original file<br />

names used by <str<strong>on</strong>g>the</str<strong>on</strong>g> exploit developers. The presence <str<strong>on</strong>g>of</str<strong>on</strong>g> this debugging code discloses <str<strong>on</strong>g>the</str<strong>on</strong>g><br />

presence <str<strong>on</strong>g>of</str<strong>on</strong>g> at least <str<strong>on</strong>g>the</str<strong>on</strong>g> following individual modules (or subsystems):<br />

1. fs.c - Loads API functi<strong>on</strong>s related to file and file system management such as ftw,<br />

open, read, rename, and mount<br />

2. kaslr.c - Loads API functi<strong>on</strong>s such as IORegistryEntryGetChildIterator,<br />

IORegistryEntryGetProperty, and IOServiceGetMatchingService that<br />

relate to finding <str<strong>on</strong>g>the</str<strong>on</strong>g> address <str<strong>on</strong>g>of</str<strong>on</strong>g> <str<strong>on</strong>g>the</str<strong>on</strong>g> kernel using a vulnerability in <str<strong>on</strong>g>the</str<strong>on</strong>g><br />

io_service_open_extended functi<strong>on</strong><br />

3. bh.c - Loads API functi<strong>on</strong>s that relate to <str<strong>on</strong>g>the</str<strong>on</strong>g> decompressi<strong>on</strong> <str<strong>on</strong>g>of</str<strong>on</strong>g> next stage payloads and<br />

<str<strong>on</strong>g>the</str<strong>on</strong>g>ir proper placement <strong>on</strong> <str<strong>on</strong>g>the</str<strong>on</strong>g> victim’s iPh<strong>on</strong>e by using functi<strong>on</strong>s such as<br />

BZ2_bzDecompress, chmod, and malloc<br />

4. safari.c - Loads API functi<strong>on</strong>s such as sync, exit, and strcpy that are used for<br />

clearing Safari cache files and terminating <str<strong>on</strong>g>the</str<strong>on</strong>g> Safari process. This cleanup is required<br />

for <str<strong>on</strong>g>the</str<strong>on</strong>g> case where we succeed and exit cleanly, as <str<strong>on</strong>g>the</str<strong>on</strong>g> Safari crash cleanup (described<br />

in <str<strong>on</strong>g>the</str<strong>on</strong>g> Stage 1 writeup) will never occur.<br />

These artifacts suggest that <str<strong>on</strong>g>the</str<strong>on</strong>g> Stage 2 binary is based <strong>on</strong> a modular design philosophy or, at<br />

<str<strong>on</strong>g>the</str<strong>on</strong>g> very least, is made up <str<strong>on</strong>g>of</str<strong>on</strong>g> various library source code files that are ultimately tied toge<str<strong>on</strong>g>the</str<strong>on</strong>g>r to<br />

form <str<strong>on</strong>g>the</str<strong>on</strong>g> Stage 2 binary. The various comp<strong>on</strong>ents that make up <str<strong>on</strong>g>the</str<strong>on</strong>g> Stage 2 exploit were likely<br />

designed to be reused across multiple <strong>iOS</strong> exploit chains.<br />

Envir<strong>on</strong>ment Setup and Platform Determinati<strong>on</strong><br />

After initialize completes, Stage 2 calls a functi<strong>on</strong> that specifies a global callback functi<strong>on</strong><br />

that is used whenever Stage 2 terminates due to an error. Based <strong>on</strong> <str<strong>on</strong>g>the</str<strong>on</strong>g> filename supplied in <str<strong>on</strong>g>the</str<strong>on</strong>g><br />

writeLog, most likely <str<strong>on</strong>g>the</str<strong>on</strong>g> functi<strong>on</strong> is an assert-style callback.<br />

Page 13


In order to determine <str<strong>on</strong>g>the</str<strong>on</strong>g> platform (hardware) <str<strong>on</strong>g>of</str<strong>on</strong>g> <str<strong>on</strong>g>the</str<strong>on</strong>g> victim’s device, a call is made to<br />

sysctlbyname for <str<strong>on</strong>g>the</str<strong>on</strong>g> hw.machine object. Ano<str<strong>on</strong>g>the</str<strong>on</strong>g>r call to sysctlbyname is made for <str<strong>on</strong>g>the</str<strong>on</strong>g><br />

kern.osversi<strong>on</strong> informati<strong>on</strong>. From <str<strong>on</strong>g>the</str<strong>on</strong>g>se two calls, Stage 2 is able to accurately determine<br />

<str<strong>on</strong>g>the</str<strong>on</strong>g> platform and <strong>iOS</strong> kernel versi<strong>on</strong>s. This informati<strong>on</strong> is <str<strong>on</strong>g>the</str<strong>on</strong>g>n used to find a data structure that<br />

defines <str<strong>on</strong>g>the</str<strong>on</strong>g> various memory <str<strong>on</strong>g>of</str<strong>on</strong>g>fsets that Stage 2 will use for its exploitati<strong>on</strong> operati<strong>on</strong>s. If Stage<br />

2 is unable to find <str<strong>on</strong>g>the</str<strong>on</strong>g> appropriate data structure for <str<strong>on</strong>g>the</str<strong>on</strong>g> platform/<strong>iOS</strong> combinati<strong>on</strong>, <str<strong>on</strong>g>the</str<strong>on</strong>g> process<br />

executes <str<strong>on</strong>g>the</str<strong>on</strong>g> assert callback and exits.<br />

Stage 2 uses a lock file during its executi<strong>on</strong>. As part <str<strong>on</strong>g>of</str<strong>on</strong>g> <str<strong>on</strong>g>the</str<strong>on</strong>g> setup <str<strong>on</strong>g>of</str<strong>on</strong>g> <str<strong>on</strong>g>the</str<strong>on</strong>g> working envir<strong>on</strong>ment,<br />

Stage 2 establishes <str<strong>on</strong>g>the</str<strong>on</strong>g> filename and path global variables for <str<strong>on</strong>g>the</str<strong>on</strong>g> lock file as<br />

$HOME/tmp/lock (Note: $HOME is an applicati<strong>on</strong> specific variable).<br />

The 32 bit versi<strong>on</strong> <str<strong>on</strong>g>of</str<strong>on</strong>g> <str<strong>on</strong>g>the</str<strong>on</strong>g> Stage 2 binary has 100 different combinati<strong>on</strong>s <str<strong>on</strong>g>of</str<strong>on</strong>g> platform and <strong>iOS</strong> that<br />

it supports, as identified in <str<strong>on</strong>g>the</str<strong>on</strong>g> table below.<br />

<strong>iOS</strong> Versi<strong>on</strong><br />

iPh<strong>on</strong>e 4S<br />

(“iPh<strong>on</strong>e4,1”)<br />

iPh<strong>on</strong>e 5<br />

(“iPh<strong>on</strong>e5,1”)<br />

iPh<strong>on</strong>e 5<br />

(“iPh<strong>on</strong>e5,2”)<br />

iPh<strong>on</strong>e 5c<br />

(“iPh<strong>on</strong>e5,3”)<br />

iPh<strong>on</strong>e 5c<br />

(“iPh<strong>on</strong>e5,4”)<br />

9.0 ✅ ✅ ✅ ✅ ✅<br />

9.0.1 ✅ ✅ ✅ ✅ ✅<br />

9.0.2 ✅ ✅ ✅ ✅ ✅<br />

9.1 ✅ ✅ ✅ ✅ ✅<br />

9.2 ✅ ✅ ✅ ✅ ✅<br />

9.2.1 ✅ ✅ ✅ ✅ ✅<br />

9.3 (13E233) ✅ ✅ ✅ ✅ ✅<br />

9.3 (13E237) ✅ ✅ ✅ ✅ ✅<br />

9.3 Beta ✅ ✅<br />

9.3 Beta 3 ✅<br />

9.3 Beta 6 ✅<br />

9.3 Beta 7 ✅<br />

9.3.1 ✅ ✅ ✅ ✅ ✅<br />

9.3.2 Beta ✅ ✅ ✅ ✅ ✅<br />

Page 14


9.3.2 Beta 2 ✅ ✅ ✅ ✅ ✅<br />

9.3.2 Beta 3 ✅ ✅ ✅ ✅ ✅<br />

9.3.2 Beta 4 ✅ ✅ ✅ ✅ ✅<br />

9.3.2 ✅ ✅ ✅ ✅ ✅<br />

9.3.3 Beta ✅ ✅ ✅ ✅ ✅<br />

9.3.3 Beta 2 ✅ ✅ ✅ ✅ ✅<br />

9.3.3 Beta 3 ✅ ✅ ✅ ✅ ✅<br />

9.3.3 Beta 4 ✅ ✅ ✅ ✅ ✅<br />

9.3.3 ✅ ✅ ✅ ✅ ✅<br />

Similarly, <str<strong>on</strong>g>the</str<strong>on</strong>g> 64-bit versi<strong>on</strong> <str<strong>on</strong>g>of</str<strong>on</strong>g> <str<strong>on</strong>g>the</str<strong>on</strong>g> Stage 2 binary supports 99 different <strong>iOS</strong> and iPh<strong>on</strong>e<br />

combinati<strong>on</strong>s. The supported iPh<strong>on</strong>e and <strong>iOS</strong> versi<strong>on</strong>s <str<strong>on</strong>g>of</str<strong>on</strong>g> 64Stage2 are identified in <str<strong>on</strong>g>the</str<strong>on</strong>g> table<br />

below.<br />

<strong>iOS</strong><br />

Versi<strong>on</strong><br />

iPh<strong>on</strong>e<br />

5s<br />

(iPh<strong>on</strong>e6,<br />

1)<br />

iPh<strong>on</strong>e<br />

5s<br />

(iPh<strong>on</strong>e6,<br />

2)<br />

iPh<strong>on</strong>e 6<br />

Plus<br />

(iPh<strong>on</strong>e7,<br />

1)<br />

iPh<strong>on</strong>e 6<br />

(iPh<strong>on</strong>e7,<br />

2)<br />

iPh<strong>on</strong>e<br />

6s<br />

(iPh<strong>on</strong>e8,<br />

1)<br />

iPh<strong>on</strong>e<br />

6s Plus<br />

(iPh<strong>on</strong>e8,<br />

2)<br />

iPh<strong>on</strong>e<br />

SE<br />

(iPh<strong>on</strong>e<br />

8,4)<br />

9.2.1<br />

(13D15)<br />

<strong>iOS</strong> 9.2.1<br />

(13D20)<br />

9.3<br />

(13E233)<br />

✅ ✅ ✅ ✅ ✅ ✅<br />

✅ ✅ ✅ ✅<br />

✅ ✅ ✅ ✅ ✅<br />

9.3<br />

(13E234)<br />

✅<br />

✅<br />

9.3<br />

(13E237)<br />

✅<br />

✅<br />

9.3 Beta<br />

4<br />

9.3 Beta<br />

6<br />

✅<br />

✅<br />

Page 15


9.3 Beta<br />

7<br />

✅<br />

9.3.1 ✅ ✅ ✅ ✅ ✅ ✅ ✅<br />

9.3.2<br />

Beta<br />

9.3.2<br />

Beta 2<br />

9.3.2<br />

Beta 3<br />

9.3.2<br />

Beta 4<br />

✅ ✅ ✅ ✅ ✅ ✅ ✅<br />

✅ ✅ ✅ ✅ ✅ ✅ ✅<br />

✅ ✅ ✅ ✅ ✅ ✅ ✅<br />

✅ ✅ ✅ ✅ ✅ ✅ ✅<br />

9.3.2 ✅ ✅ ✅ ✅ ✅ ✅ ✅<br />

9.3.3<br />

Beta<br />

9.3.3<br />

Beta 2<br />

9.3.3<br />

Beta 3<br />

9.3.3<br />

Beta 4<br />

✅ ✅ ✅ ✅ ✅ ✅ ✅<br />

✅ ✅ ✅ ✅ ✅ ✅ ✅<br />

✅ ✅ ✅ ✅ ✅ ✅ ✅<br />

✅ ✅ ✅ ✅ ✅ ✅ ✅<br />

9.3.3 ✅ ✅ ✅ ✅ ✅ ✅ ✅<br />

Defeating KASLR<br />

The majority <str<strong>on</strong>g>of</str<strong>on</strong>g> Stage 2’s functi<strong>on</strong>ality deals with manipulating <str<strong>on</strong>g>the</str<strong>on</strong>g> kernel in order to disable<br />

security features <strong>on</strong> <str<strong>on</strong>g>the</str<strong>on</strong>g> victim’s device. In order to manipulate <str<strong>on</strong>g>the</str<strong>on</strong>g> kernel, Stage 2 must first<br />

locate <str<strong>on</strong>g>the</str<strong>on</strong>g> kernel. Under normal circumstances <str<strong>on</strong>g>the</str<strong>on</strong>g> kernel will be mapped into a randomized<br />

locati<strong>on</strong> due to <str<strong>on</strong>g>the</str<strong>on</strong>g> kernel address space layout randomizati<strong>on</strong> (KASLR) mechanism that <strong>iOS</strong><br />

employs. KASLR is designed to prevent processes from locating <str<strong>on</strong>g>the</str<strong>on</strong>g> kernel in memory by<br />

mapping <str<strong>on</strong>g>the</str<strong>on</strong>g> kernel to a pseudorandom locati<strong>on</strong> in memory each time <str<strong>on</strong>g>the</str<strong>on</strong>g> device is powered <strong>on</strong><br />

by <str<strong>on</strong>g>the</str<strong>on</strong>g> user. In order to locate <str<strong>on</strong>g>the</str<strong>on</strong>g> kernel, Stage 2 must find a way to expose a memory address<br />

within kernel memory space to a process in user space. Stage 2 uses <str<strong>on</strong>g>the</str<strong>on</strong>g> vulnerability CVE-<br />

2016-4655 1 in order expose a memory address in kernel space.<br />

1 http://www.securityfocus.com/bid/92651<br />

Page 16


In order to find <str<strong>on</strong>g>the</str<strong>on</strong>g> kernel, Stage 2 begins by opening a port to <str<strong>on</strong>g>the</str<strong>on</strong>g> IOKit subsystem. Failing this,<br />

Stage 2 calls <str<strong>on</strong>g>the</str<strong>on</strong>g> assert callback and exits. A call to IOServiceMatching for <str<strong>on</strong>g>the</str<strong>on</strong>g> service<br />

named AppleKeyStore is made by Stage 2, and <str<strong>on</strong>g>the</str<strong>on</strong>g> results <str<strong>on</strong>g>of</str<strong>on</strong>g> <str<strong>on</strong>g>the</str<strong>on</strong>g> call are given to<br />

IOServiceGetMatchingService in order to obtain a io_service_t object c<strong>on</strong>taining <str<strong>on</strong>g>the</str<strong>on</strong>g><br />

desired registered IOKit IOService (in this case, AppleKeyStore). With <str<strong>on</strong>g>the</str<strong>on</strong>g> IOService handle,<br />

Stage 2 calls io_service_open_extended and passes a specially crafted properties field to<br />

<str<strong>on</strong>g>the</str<strong>on</strong>g> service. The properties field is a (serialized) binary representati<strong>on</strong> <str<strong>on</strong>g>of</str<strong>on</strong>g> XML data that<br />

io_service_open_extended ultimately passes to <str<strong>on</strong>g>the</str<strong>on</strong>g> OSUnserializeBinary functi<strong>on</strong><br />

located in <str<strong>on</strong>g>the</str<strong>on</strong>g> kernel 2 . Within <str<strong>on</strong>g>the</str<strong>on</strong>g> OSUnserializeBinary functi<strong>on</strong> is a switch statement that<br />

handles <str<strong>on</strong>g>the</str<strong>on</strong>g> various types <str<strong>on</strong>g>of</str<strong>on</strong>g> data structures found within a binary XML data structure. The data<br />

type for kOSSerializeNumber blindly accepts <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> data without performing any<br />

type <str<strong>on</strong>g>of</str<strong>on</strong>g> reas<strong>on</strong>able bound checking, which ultimately gives <str<strong>on</strong>g>the</str<strong>on</strong>g> caller <str<strong>on</strong>g>the</str<strong>on</strong>g> ability to request more<br />

memory than should be allowed. This c<strong>on</strong>diti<strong>on</strong> occurs due to <str<strong>on</strong>g>the</str<strong>on</strong>g> following code fragments:<br />

len = (key & kOSSerializeDataMask);<br />

...<br />

case kOSSerializeNumber:<br />

bufferPos += size<str<strong>on</strong>g>of</str<strong>on</strong>g>(l<strong>on</strong>g l<strong>on</strong>g);<br />

if (bufferPos > bufferSize) break;<br />

value = next[1];<br />

value


unsigned char properties[] = {<br />

// kOSSerializeBinarySignature<br />

0xD3, 0x00, 0x00, 0x00,<br />

// kOSSerializeEndCollect<strong>on</strong> | kOSSerializeDicti<strong>on</strong>ary | 2<br />

0x02, 0x00, 0x00, 0x81,<br />

// KEY 1 specified as 30 bytes l<strong>on</strong>g (0x1E)<br />

// kOSSerializeSymbol | 0x1E<br />

0x1E, 0x00, 0x00, 0x08,<br />

"HIDKeyboardModifierMappingSrc", 0x00,<br />

// (30 bytes)<br />

// padding (30 + 3 / 4 = 8 DWORDS)<br />

0x00, 0x00,<br />

// VALUE 1<br />

// kOSSerializeNumber specified as 0x800 bits (256 bytes)<br />

0x00, 0x08, 0x00, 0x04,<br />

// value <str<strong>on</strong>g>of</str<strong>on</strong>g> OSNumber (4)<br />

0x04, 0x00, 0x00, 0x00,<br />

0x00, 0x00, 0x00, 0x00,<br />

// KEY 2 specified as 30 bytes l<strong>on</strong>g (0x1E)<br />

// kOSSerializeSymbol | 0x1E<br />

0x1E, 0x00, 0x00, 0x08,<br />

"HIDKeyboardModifierMappingDst", 0x00, // (30 chars)<br />

// padding (30 + 3 / 4 = 8 DWORDS)<br />

0x00, 0x00,<br />

// VALUE 2<br />

// kOSSerializeEndCollect<strong>on</strong> | kOSSerializeNumber | 32<br />

0x20, 0x00, 0x00, 0x84,<br />

// value <str<strong>on</strong>g>of</str<strong>on</strong>g> OSNumber (0x193)<br />

0x93, 0x01, 0x00, 0x00,<br />

0x00, 0x00, 0x00, 0x00<br />

};<br />

Stage 2 calls IORegistryEntryGetProperty in order to find <str<strong>on</strong>g>the</str<strong>on</strong>g> entry for<br />

HIDKeyboardModifierMappingSrc, which results in <str<strong>on</strong>g>the</str<strong>on</strong>g> properties array creating an<br />

OSNumber larger than <str<strong>on</strong>g>the</str<strong>on</strong>g> maximum 64-bits (8 bytes). Stage 2 uses <str<strong>on</strong>g>the</str<strong>on</strong>g> following code fragment<br />

to call is_io_registry_entry_get_property_bytes, which will read past <str<strong>on</strong>g>the</str<strong>on</strong>g> end <str<strong>on</strong>g>of</str<strong>on</strong>g> a<br />

kernel stack buffer and copy <str<strong>on</strong>g>the</str<strong>on</strong>g> data to a kernel heap buffer. The<br />

IORegistryEntryGetProperty functi<strong>on</strong> <str<strong>on</strong>g>the</str<strong>on</strong>g>n returns this heap buffer to user space.<br />

Pointers from this stack overread will <str<strong>on</strong>g>the</str<strong>on</strong>g>refore be leaked to user mode and can be used to<br />

calculate <str<strong>on</strong>g>the</str<strong>on</strong>g> base address for <str<strong>on</strong>g>the</str<strong>on</strong>g> <strong>iOS</strong> kernel:<br />

do<br />

{<br />

...<br />

} while ( IORegistryEntryGetProperty_0(v13, "HIDKeyboardModifierMappingSrc", dataBuffer, &size)<br />

);<br />

writeLog(7, "%.2s%5.5d\n", "kaslr.c", 127);<br />

if ( size > 8 )<br />

{<br />

writeLog(7, "%.2s%5.5d\n", "kaslr.c", 138);<br />

return dataBuffer[index] & 0xFFF00000;<br />

}<br />

Two aspects <str<strong>on</strong>g>of</str<strong>on</strong>g> this code should be explicitly noted. First, <str<strong>on</strong>g>the</str<strong>on</strong>g> properties array specifies that <str<strong>on</strong>g>the</str<strong>on</strong>g><br />

OSNumber value is 256 bytes in size, which is what ultimately leads to data leakage. Sec<strong>on</strong>d,<br />

<str<strong>on</strong>g>the</str<strong>on</strong>g> index value used to find <str<strong>on</strong>g>the</str<strong>on</strong>g> memory locati<strong>on</strong> within <str<strong>on</strong>g>the</str<strong>on</strong>g> returned dataBuffer array varies<br />

Page 18


with <str<strong>on</strong>g>the</str<strong>on</strong>g> platform/<strong>iOS</strong> combinati<strong>on</strong>. The developers <str<strong>on</strong>g>of</str<strong>on</strong>g> Stage 2 have mapped out each<br />

combinati<strong>on</strong> <str<strong>on</strong>g>of</str<strong>on</strong>g> platform/<strong>iOS</strong> to determine what positi<strong>on</strong> within <str<strong>on</strong>g>the</str<strong>on</strong>g> dataBuffer array a valid<br />

kernel locati<strong>on</strong> is present.<br />

If Stage 2 is unable to find <str<strong>on</strong>g>the</str<strong>on</strong>g> base address for <str<strong>on</strong>g>the</str<strong>on</strong>g> kernel using <str<strong>on</strong>g>the</str<strong>on</strong>g> above described method or<br />

if Stage 2 finds that it is operating under a versi<strong>on</strong> <str<strong>on</strong>g>of</str<strong>on</strong>g> <strong>iOS</strong> o<str<strong>on</strong>g>the</str<strong>on</strong>g>r than 9, <str<strong>on</strong>g>the</str<strong>on</strong>g> assert callback is<br />

called and <str<strong>on</strong>g>the</str<strong>on</strong>g> applicati<strong>on</strong> terminates.<br />

Establishing Read/Write/Execute Primitives <strong>on</strong> Previously Rooted<br />

Devices (32-Bit)<br />

After finding <str<strong>on</strong>g>the</str<strong>on</strong>g> kernel’s base address, 32Stage2 generates an IPC pipe set via <str<strong>on</strong>g>the</str<strong>on</strong>g> pipe<br />

functi<strong>on</strong>. If <str<strong>on</strong>g>the</str<strong>on</strong>g> pipe command fails, 32Stage2 calls <str<strong>on</strong>g>the</str<strong>on</strong>g> assert callback functi<strong>on</strong> and exits.<br />

Following <str<strong>on</strong>g>the</str<strong>on</strong>g> generati<strong>on</strong> <str<strong>on</strong>g>of</str<strong>on</strong>g> <str<strong>on</strong>g>the</str<strong>on</strong>g> pipe set, 32Stage2 uses a kernel port to obtain <str<strong>on</strong>g>the</str<strong>on</strong>g> clock<br />

services for <str<strong>on</strong>g>the</str<strong>on</strong>g> battery clock (also known as <str<strong>on</strong>g>the</str<strong>on</strong>g> calendar clock) and real-time clock via two<br />

calls to host_get_clock_service. If ei<str<strong>on</strong>g>the</str<strong>on</strong>g>r <str<strong>on</strong>g>of</str<strong>on</strong>g> <str<strong>on</strong>g>the</str<strong>on</strong>g> clocks are inaccessible, <str<strong>on</strong>g>the</str<strong>on</strong>g> asserti<strong>on</strong><br />

callback is called and 32Stage2 exits. The pipe set and <str<strong>on</strong>g>the</str<strong>on</strong>g> clock ports are critical to<br />

establishing a beachhead for gaining access to <str<strong>on</strong>g>the</str<strong>on</strong>g> kernel memory space as <str<strong>on</strong>g>the</str<strong>on</strong>g> combinati<strong>on</strong> <str<strong>on</strong>g>of</str<strong>on</strong>g><br />

<str<strong>on</strong>g>the</str<strong>on</strong>g> three objects (<str<strong>on</strong>g>the</str<strong>on</strong>g> pipe set and <str<strong>on</strong>g>the</str<strong>on</strong>g> two clocks) are later used for kernel memory read and<br />

write access as well as kernel process space executi<strong>on</strong> access.<br />

Immediately following <str<strong>on</strong>g>the</str<strong>on</strong>g> pipe and host_get_clock_service calls, 32Stage2 checks <str<strong>on</strong>g>the</str<strong>on</strong>g><br />

value <str<strong>on</strong>g>of</str<strong>on</strong>g> <str<strong>on</strong>g>the</str<strong>on</strong>g> port to <str<strong>on</strong>g>the</str<strong>on</strong>g> kernel that was generated previously by calling task_from_pid. If<br />

task_from_pid returned a valid (n<strong>on</strong>-NULL) port, 32Stage2 modifies <str<strong>on</strong>g>the</str<strong>on</strong>g> kernel’s memory by<br />

writing a 20-byte data structure using vm_write. The 20-byte data structure overwrites parts <str<strong>on</strong>g>of</str<strong>on</strong>g><br />

<str<strong>on</strong>g>the</str<strong>on</strong>g> clock_ops structures for calend_ops and rtclock_ops 3 .<br />

The 20-byte data structures c<strong>on</strong>tain pointers to handler functi<strong>on</strong>s for <str<strong>on</strong>g>the</str<strong>on</strong>g> battery clock and realtime<br />

clock that <str<strong>on</strong>g>the</str<strong>on</strong>g> kernel will call when functi<strong>on</strong>s such as clock_get_attributes are called<br />

(callback functi<strong>on</strong>s). The 20-byte data structure replaces <str<strong>on</strong>g>the</str<strong>on</strong>g> getattr handler for both <str<strong>on</strong>g>of</str<strong>on</strong>g> <str<strong>on</strong>g>the</str<strong>on</strong>g><br />

clock types with existing kernel functi<strong>on</strong>s. Specifically, <str<strong>on</strong>g>the</str<strong>on</strong>g> real-time clock’s getattr is modified<br />

to point to OSSerializer::serialize, and <str<strong>on</strong>g>the</str<strong>on</strong>g> battery clock’s getattr is modified to point<br />

to _bufattr_cpx.<br />

The choice <str<strong>on</strong>g>of</str<strong>on</strong>g> <str<strong>on</strong>g>the</str<strong>on</strong>g> replacement functi<strong>on</strong>s changes <str<strong>on</strong>g>the</str<strong>on</strong>g> nature <str<strong>on</strong>g>of</str<strong>on</strong>g> <str<strong>on</strong>g>the</str<strong>on</strong>g> clock_get_attributes<br />

call made to <str<strong>on</strong>g>the</str<strong>on</strong>g> two clock types. For calls to clock_get_attributes for <str<strong>on</strong>g>the</str<strong>on</strong>g> battery clock,<br />

<str<strong>on</strong>g>the</str<strong>on</strong>g> functi<strong>on</strong> now operates as a kernel memory read interface. The _bufattr_cpx functi<strong>on</strong><br />

c<strong>on</strong>tains <strong>on</strong>ly two instructi<strong>on</strong>s:<br />

_bufattr_cpx:<br />

LDR<br />

BX<br />

R0, [R0]<br />

LR<br />

3 http://opensource.apple.com/source/xnu/xnu-3248.20.55/osfmk/kern/clock_oldops.c<br />

Page 19


The first parameter to <str<strong>on</strong>g>the</str<strong>on</strong>g> functi<strong>on</strong> (in R0) is used as a memory address that <str<strong>on</strong>g>the</str<strong>on</strong>g> functi<strong>on</strong> reads<br />

and returns in R0 before returning to <str<strong>on</strong>g>the</str<strong>on</strong>g> calling functi<strong>on</strong>. While <str<strong>on</strong>g>the</str<strong>on</strong>g> getattr functi<strong>on</strong>s use<br />

three parameters, given that <str<strong>on</strong>g>the</str<strong>on</strong>g> iPh<strong>on</strong>e’s ARM-based functi<strong>on</strong> calls use registers for <str<strong>on</strong>g>the</str<strong>on</strong>g> first<br />

four functi<strong>on</strong> arguments, <str<strong>on</strong>g>the</str<strong>on</strong>g> lack <str<strong>on</strong>g>of</str<strong>on</strong>g> a fully compliant functi<strong>on</strong> prototype is irrelevant.<br />

The replacement functi<strong>on</strong> for <str<strong>on</strong>g>the</str<strong>on</strong>g> real-time clock’s getattr functi<strong>on</strong> is a bit more complex. The<br />

OSSerializer::serialize functi<strong>on</strong> expects a OSSerializer object as a this<br />

pointer(i.e., an object that includes a virtual functi<strong>on</strong> table (vtable)). The address stored at <str<strong>on</strong>g>of</str<strong>on</strong>g>fset<br />

0x10 within <str<strong>on</strong>g>the</str<strong>on</strong>g> OSSerializer object is used as <str<strong>on</strong>g>the</str<strong>on</strong>g> functi<strong>on</strong> to pass c<strong>on</strong>trol to via <str<strong>on</strong>g>the</str<strong>on</strong>g> BX<br />

instructi<strong>on</strong> and uses <str<strong>on</strong>g>the</str<strong>on</strong>g> DWORDs at <str<strong>on</strong>g>of</str<strong>on</strong>g>fset 8 and 12 as parameters to <str<strong>on</strong>g>the</str<strong>on</strong>g> next functi<strong>on</strong>.<br />

_DWORD OSSerializer::serialize(OSSerialize *):<br />

LDR<br />

R3, [R0,#8]<br />

MOV<br />

R2, R1<br />

LDR<br />

R1, [R0,#0xC]<br />

LDR.W<br />

R12, [R0,#0x10]<br />

MOV<br />

R0, R3<br />

BX<br />

R12<br />

The result <str<strong>on</strong>g>of</str<strong>on</strong>g> replacing <str<strong>on</strong>g>the</str<strong>on</strong>g> getattr handler for <str<strong>on</strong>g>the</str<strong>on</strong>g> real-time clock is that now <str<strong>on</strong>g>the</str<strong>on</strong>g> caller to<br />

clock_get_attributes can execute arbitrary functi<strong>on</strong>s within <str<strong>on</strong>g>the</str<strong>on</strong>g> kernel by supplying a<br />

specially designed data structure, a structure that will be explained in greater detail later in this<br />

report. What is important to remember at this point is that <str<strong>on</strong>g>the</str<strong>on</strong>g> clock modificati<strong>on</strong>s <strong>on</strong>ly occur at<br />

this phase if <str<strong>on</strong>g>the</str<strong>on</strong>g> victim’s kernel is already exposed in some manner. That is, <str<strong>on</strong>g>the</str<strong>on</strong>g>se clock<br />

modificati<strong>on</strong>s would not be possible <strong>on</strong> a n<strong>on</strong>-jailbroken ph<strong>on</strong>e.<br />

If 32Stage2 already has access to <str<strong>on</strong>g>the</str<strong>on</strong>g> kernel port and has performed <str<strong>on</strong>g>the</str<strong>on</strong>g> above-menti<strong>on</strong>ed<br />

modificati<strong>on</strong>s to <str<strong>on</strong>g>the</str<strong>on</strong>g> various clocks, 32Stage2 will skip <str<strong>on</strong>g>the</str<strong>on</strong>g> next several steps that it would<br />

normally perform in order to gain such access, and pick up at <str<strong>on</strong>g>the</str<strong>on</strong>g> privilege escalati<strong>on</strong> phase. If<br />

<str<strong>on</strong>g>the</str<strong>on</strong>g> kernel modificati<strong>on</strong> was not made because <str<strong>on</strong>g>the</str<strong>on</strong>g> kernel’s task port was currently inaccessible,<br />

32Stage2 creates and locks <str<strong>on</strong>g>the</str<strong>on</strong>g> lock file specified during <str<strong>on</strong>g>the</str<strong>on</strong>g> earlier initializati<strong>on</strong> phase. This file<br />

becomes important later as a piece <str<strong>on</strong>g>of</str<strong>on</strong>g> <str<strong>on</strong>g>the</str<strong>on</strong>g> process that ultimately gains 32Stage2 <str<strong>on</strong>g>the</str<strong>on</strong>g> ability to<br />

modify <str<strong>on</strong>g>the</str<strong>on</strong>g> kernel’s memory.<br />

The 64-bit versi<strong>on</strong> <str<strong>on</strong>g>of</str<strong>on</strong>g> <str<strong>on</strong>g>the</str<strong>on</strong>g> Stage 2 binary does not attempt to take advantage <str<strong>on</strong>g>of</str<strong>on</strong>g> pre-existing<br />

backdoors in previously jailbroken devices.<br />

Thread Manipulati<strong>on</strong><br />

Stage 2 will eventually leverage a use-after-free (UAF) vulnerability in order to execute arbitrary<br />

code within <str<strong>on</strong>g>the</str<strong>on</strong>g> kernel space. When using a UAF vulnerability, it is possible that a race c<strong>on</strong>diti<strong>on</strong><br />

may occur where <str<strong>on</strong>g>the</str<strong>on</strong>g> memory that was dereferenced (and which <str<strong>on</strong>g>the</str<strong>on</strong>g> exploit wishes to c<strong>on</strong>trol) is<br />

reallocated for ano<str<strong>on</strong>g>the</str<strong>on</strong>g>r thread before <str<strong>on</strong>g>the</str<strong>on</strong>g> exploit can execute. In order to reduce <str<strong>on</strong>g>the</str<strong>on</strong>g> probability<br />

<str<strong>on</strong>g>of</str<strong>on</strong>g> ano<str<strong>on</strong>g>the</str<strong>on</strong>g>r thread accidentally allocating into a critical deallocated chunk, Stage 2 will generate a<br />

Page 20


list <str<strong>on</strong>g>of</str<strong>on</strong>g> all <str<strong>on</strong>g>of</str<strong>on</strong>g> its running threads and immediately place each thread (outside <str<strong>on</strong>g>of</str<strong>on</strong>g> its main thread) in<br />

a suspended state. Next, Stage 2 modifies <str<strong>on</strong>g>the</str<strong>on</strong>g> scheduling policies for <str<strong>on</strong>g>the</str<strong>on</strong>g> main thread to fur<str<strong>on</strong>g>the</str<strong>on</strong>g>r<br />

increase <str<strong>on</strong>g>the</str<strong>on</strong>g> probability that <str<strong>on</strong>g>the</str<strong>on</strong>g> UAF exploit will not face competiti<strong>on</strong> for <str<strong>on</strong>g>the</str<strong>on</strong>g> memory in<br />

questi<strong>on</strong>.<br />

An additi<strong>on</strong>al step is performed in <str<strong>on</strong>g>the</str<strong>on</strong>g> 64-bit versi<strong>on</strong> <str<strong>on</strong>g>of</str<strong>on</strong>g> Stage 2. With <str<strong>on</strong>g>the</str<strong>on</strong>g> thread scheduler<br />

modificati<strong>on</strong>s complete, 64Stage2 generates up to 1000 threads. Each thread c<strong>on</strong>sists <str<strong>on</strong>g>of</str<strong>on</strong>g> a<br />

single tight loop that merely wait for a global variable to drop below a predefined value (in this<br />

case, <str<strong>on</strong>g>the</str<strong>on</strong>g> value is less than 0). This behavior is intended to ensure (or, at least, significantly<br />

increase <str<strong>on</strong>g>the</str<strong>on</strong>g> chances) that no additi<strong>on</strong>al threads may spawn that can compete for <str<strong>on</strong>g>the</str<strong>on</strong>g> UAF’s<br />

targeted memory.<br />

Establishing Communicati<strong>on</strong> Channel (32-Bit)<br />

32Stage2 generates ano<str<strong>on</strong>g>the</str<strong>on</strong>g>r pipe set using <str<strong>on</strong>g>the</str<strong>on</strong>g> pipe command, reusing <str<strong>on</strong>g>the</str<strong>on</strong>g> same variable that<br />

held <str<strong>on</strong>g>the</str<strong>on</strong>g> original pipe set 32Stage2 generated. This acti<strong>on</strong> is immediately followed by calls to<br />

host_get_clock_service in order to get access to <str<strong>on</strong>g>the</str<strong>on</strong>g> real-time and battery clocks. As with<br />

<str<strong>on</strong>g>the</str<strong>on</strong>g> pipe set, <str<strong>on</strong>g>the</str<strong>on</strong>g> calls to host_get_clock_service reuse <str<strong>on</strong>g>the</str<strong>on</strong>g> same variables from <str<strong>on</strong>g>the</str<strong>on</strong>g><br />

previous calls to host_get_clock_service that gain a port to <str<strong>on</strong>g>the</str<strong>on</strong>g> various clocks.<br />

The previous generati<strong>on</strong> <str<strong>on</strong>g>of</str<strong>on</strong>g> <str<strong>on</strong>g>the</str<strong>on</strong>g> pipe set and <str<strong>on</strong>g>the</str<strong>on</strong>g> clock ports were necessary because <str<strong>on</strong>g>the</str<strong>on</strong>g>se<br />

items are used later for kernel manipulati<strong>on</strong> and if <str<strong>on</strong>g>the</str<strong>on</strong>g> kernel task port was available already,<br />

32Stage2 would simply skip <str<strong>on</strong>g>the</str<strong>on</strong>g> exploitati<strong>on</strong> process necessary to modify <str<strong>on</strong>g>the</str<strong>on</strong>g> kernel and instead<br />

modify <str<strong>on</strong>g>the</str<strong>on</strong>g> kernel directly through vm_write calls. However, if 32Stage2 does not have access<br />

to <str<strong>on</strong>g>the</str<strong>on</strong>g> kernel task port (<str<strong>on</strong>g>the</str<strong>on</strong>g> default case <strong>on</strong> a n<strong>on</strong>-jailbroken device), exploitati<strong>on</strong> is necessary in<br />

order to acquire such access. As part <str<strong>on</strong>g>of</str<strong>on</strong>g> this exploitati<strong>on</strong> process, 32Stage2 needs to have <str<strong>on</strong>g>the</str<strong>on</strong>g><br />

pipe set and clocks available prior to <str<strong>on</strong>g>the</str<strong>on</strong>g> exploit’s activati<strong>on</strong>, and thus <str<strong>on</strong>g>the</str<strong>on</strong>g> binary ensures that<br />

<str<strong>on</strong>g>the</str<strong>on</strong>g>y are available. While this is unnecessarily repetitive, it does serve to ensure that <str<strong>on</strong>g>the</str<strong>on</strong>g> critical<br />

objects are readily available.<br />

The 64-bit versi<strong>on</strong> <str<strong>on</strong>g>of</str<strong>on</strong>g> <str<strong>on</strong>g>the</str<strong>on</strong>g> Stage 2 binary does not need to perform this step, given that <str<strong>on</strong>g>the</str<strong>on</strong>g><br />

triggering mechanism used to ultimately call <str<strong>on</strong>g>the</str<strong>on</strong>g> functi<strong>on</strong> is little more than a redirecti<strong>on</strong> <str<strong>on</strong>g>of</str<strong>on</strong>g> an<br />

existing functi<strong>on</strong> pointer to a sysctl handler.<br />

Payload C<strong>on</strong>structi<strong>on</strong> and Kernel Inserti<strong>on</strong> (32-Bit)<br />

Without a means to modify kernel memory through <str<strong>on</strong>g>the</str<strong>on</strong>g> kernel port, 32Stage2 must leverage a<br />

vulnerability within <strong>iOS</strong> to gain access to <str<strong>on</strong>g>the</str<strong>on</strong>g> kernel. In order to perform this task, 32Stage2<br />

c<strong>on</strong>structs two data buffers: a 20-byte buffer c<strong>on</strong>taining <str<strong>on</strong>g>the</str<strong>on</strong>g> necessary overwrite data to modify<br />

<str<strong>on</strong>g>the</str<strong>on</strong>g> real-time and battery clocks and a 38-byte buffer c<strong>on</strong>taining a payload that runs a series <str<strong>on</strong>g>of</str<strong>on</strong>g><br />

ROP gadgets to install <str<strong>on</strong>g>the</str<strong>on</strong>g> clock handler overwrites. The two data buffers have <str<strong>on</strong>g>the</str<strong>on</strong>g> following<br />

layout after <str<strong>on</strong>g>the</str<strong>on</strong>g>ir c<strong>on</strong>structi<strong>on</strong>:<br />

Page 21


clock_ops_overwrite Buffer:<br />

[00] (rtclock.getattr): address <str<strong>on</strong>g>of</str<strong>on</strong>g> OSSerializer::serialize<br />

[04] (calend_c<strong>on</strong>fig): NULL<br />

[08] (calend_init): NULL<br />

[0C] (calend_gettime): address <str<strong>on</strong>g>of</str<strong>on</strong>g> calen_getattr<br />

[10] (calend_getattr): address <str<strong>on</strong>g>of</str<strong>on</strong>g> _bufattr_cpx<br />

uaf_payload_buffer Exploit Buffer:<br />

[00] ptr to clock_ops_overwrite buffer<br />

[04] address <str<strong>on</strong>g>of</str<strong>on</strong>g> clock_ops array in kern memory<br />

[08] address <str<strong>on</strong>g>of</str<strong>on</strong>g> _copyin<br />

[0C] NULL<br />

[10] address <str<strong>on</strong>g>of</str<strong>on</strong>g> OSSerializer::serialize<br />

[14] address <str<strong>on</strong>g>of</str<strong>on</strong>g> "BX LR" code fragment<br />

[18] NULL<br />

[1C] address <str<strong>on</strong>g>of</str<strong>on</strong>g> OSSymbol::getMetaClass<br />

[20] address <str<strong>on</strong>g>of</str<strong>on</strong>g> "BX LR" code fragment<br />

[24] address <str<strong>on</strong>g>of</str<strong>on</strong>g> "BX LR" code fragment<br />

32Stage2 generates a new thread to handle <str<strong>on</strong>g>the</str<strong>on</strong>g> necessary setup for <str<strong>on</strong>g>the</str<strong>on</strong>g> installati<strong>on</strong> <str<strong>on</strong>g>of</str<strong>on</strong>g> <str<strong>on</strong>g>the</str<strong>on</strong>g> new<br />

clock handlers though <str<strong>on</strong>g>the</str<strong>on</strong>g> new thread, itself, does not perform <str<strong>on</strong>g>the</str<strong>on</strong>g> installati<strong>on</strong>. The thread<br />

begins by establishing a kauth_filesec data structure <strong>on</strong> <str<strong>on</strong>g>the</str<strong>on</strong>g> stack with <str<strong>on</strong>g>the</str<strong>on</strong>g> following values:<br />

.fsec_magic = KAUTH_FILESEC_MAGIC; // 0x12CC16D<br />

.fsec_owner = ;<br />

.fsec_group = ;<br />

.fsec_acl.entrycount = KAUTH_FILESEC_NOACL; // -1<br />

The uaf_payload_buffer exploit buffer is appended to <str<strong>on</strong>g>the</str<strong>on</strong>g> end <str<strong>on</strong>g>of</str<strong>on</strong>g> <str<strong>on</strong>g>the</str<strong>on</strong>g> kauth_filesec structure<br />

in what is defined as <str<strong>on</strong>g>the</str<strong>on</strong>g> kauth_filesec.fsec_acl.acl_ace[] array area.The thread <str<strong>on</strong>g>the</str<strong>on</strong>g>n<br />

opens a port to IOKit and calls IOServiceGetMatchingService for AppleKeyStore.<br />

Using <str<strong>on</strong>g>the</str<strong>on</strong>g> same technique explained previously in <str<strong>on</strong>g>the</str<strong>on</strong>g> Kernel Locati<strong>on</strong> secti<strong>on</strong>, <str<strong>on</strong>g>the</str<strong>on</strong>g> thread<br />

obtains a valid kernel memory locati<strong>on</strong>. The <strong>on</strong>ly difference between <str<strong>on</strong>g>the</str<strong>on</strong>g> new thread’s use <str<strong>on</strong>g>of</str<strong>on</strong>g> <str<strong>on</strong>g>the</str<strong>on</strong>g><br />

AppleKeyStore disclosure vulnerability and <str<strong>on</strong>g>the</str<strong>on</strong>g> method used by 32Stage2 previously is <str<strong>on</strong>g>the</str<strong>on</strong>g><br />

property name that <str<strong>on</strong>g>the</str<strong>on</strong>g> thread uses (which is<br />

“ararararararararararararararararararararararararararararararararararar<br />

arararararararararararararararararararararararararararara").<br />

After obtaining <str<strong>on</strong>g>the</str<strong>on</strong>g> kernel address from <str<strong>on</strong>g>the</str<strong>on</strong>g> AppleKeyStore, a syscall is made to <str<strong>on</strong>g>the</str<strong>on</strong>g><br />

open_extended functi<strong>on</strong>. 32Stage2 passes <str<strong>on</strong>g>the</str<strong>on</strong>g> locati<strong>on</strong> <str<strong>on</strong>g>of</str<strong>on</strong>g> <str<strong>on</strong>g>the</str<strong>on</strong>g> lock file to <str<strong>on</strong>g>the</str<strong>on</strong>g> syscall al<strong>on</strong>g with<br />

both <str<strong>on</strong>g>the</str<strong>on</strong>g> KAUTH_UID_NONE and KAUTH_GID_NONE values and <str<strong>on</strong>g>the</str<strong>on</strong>g> kauth_filesec structure<br />

c<strong>on</strong>structed at <str<strong>on</strong>g>the</str<strong>on</strong>g> start <str<strong>on</strong>g>of</str<strong>on</strong>g> <str<strong>on</strong>g>the</str<strong>on</strong>g> thread. At <str<strong>on</strong>g>the</str<strong>on</strong>g> start <str<strong>on</strong>g>of</str<strong>on</strong>g> <str<strong>on</strong>g>the</str<strong>on</strong>g> open_extended functi<strong>on</strong>, <str<strong>on</strong>g>the</str<strong>on</strong>g> following<br />

code executes:<br />

if ((uap->xsecurity != USER_ADDR_NULL) &&<br />

((ciferror = kauth_copyinfilesec(uap->xsecurity, &xsecdst)) != 0))<br />

The kauth_copyinfilesec functi<strong>on</strong> copies <str<strong>on</strong>g>the</str<strong>on</strong>g> kauth_filesec structure passed from<br />

userland into a kauth_filesec structure in <str<strong>on</strong>g>the</str<strong>on</strong>g> kernel address space. Before explaining <str<strong>on</strong>g>the</str<strong>on</strong>g><br />

Page 22


vulnerability in this functi<strong>on</strong>, it is necessary to understand <str<strong>on</strong>g>the</str<strong>on</strong>g> layout <str<strong>on</strong>g>of</str<strong>on</strong>g> <str<strong>on</strong>g>the</str<strong>on</strong>g> kauth_filesec<br />

structure. The kauth_filesec structure makes up an access c<strong>on</strong>trol list (ACL) that c<strong>on</strong>tains<br />

access c<strong>on</strong>trol entries (ACE). The structure for kauth_filesec is defined as:<br />

/* File Security informati<strong>on</strong> */<br />

struct kauth_filesec {<br />

u_int32_t fsec_magic;<br />

guid_t<br />

fsec_owner;<br />

guid_t<br />

fsec_group;<br />

struct kauth_acl fsec_acl;<br />

};<br />

The ACL comp<strong>on</strong>ent for kauth_filesec is stored in a kauth_acl structure, which c<strong>on</strong>tains<br />

an array <str<strong>on</strong>g>of</str<strong>on</strong>g> ACE:<br />

/* Access C<strong>on</strong>trol List */<br />

struct kauth_acl {<br />

u_int32_t acl_entrycount;<br />

u_int32_t acl_flags;<br />

struct kauth_ace acl_ace[1];<br />

};<br />

The kauth_ace structure is 24-bytes in size and defined as:<br />

typedef u_int32_t kauth_ace_rights_t;<br />

/* Access C<strong>on</strong>trol List Entry (ACE) */<br />

struct kauth_ace {<br />

guid_t<br />

ace_applicable;<br />

u_int32_t ace_flags;<br />

kauth_ace_rights_t ace_rights; /* scope specific */<br />

};<br />

The kauth_acl field acl_entrycount is an unsigned integer that defines how many<br />

kauth_ace entries are within <str<strong>on</strong>g>the</str<strong>on</strong>g> acl_ace array. If an ACL c<strong>on</strong>tains no ACE records,<br />

acl_entrycount is set to KAUTH_FILESEC_NOACL, which is defined as -1. At <str<strong>on</strong>g>the</str<strong>on</strong>g> beginning<br />

<str<strong>on</strong>g>of</str<strong>on</strong>g> <str<strong>on</strong>g>the</str<strong>on</strong>g> kauth_copyinfilesec functi<strong>on</strong>, <str<strong>on</strong>g>the</str<strong>on</strong>g> following comment is found within <str<strong>on</strong>g>the</str<strong>on</strong>g> publicly<br />

available source code:<br />

/*<br />

* Make a guess at <str<strong>on</strong>g>the</str<strong>on</strong>g> size <str<strong>on</strong>g>of</str<strong>on</strong>g> <str<strong>on</strong>g>the</str<strong>on</strong>g> filesec. We start with <str<strong>on</strong>g>the</str<strong>on</strong>g> base<br />

* pointer, and look at how much room is left <strong>on</strong> <str<strong>on</strong>g>the</str<strong>on</strong>g> page, clipped<br />

* to a sensible upper bound. If it turns out this isn't enough,<br />

* we'll size based <strong>on</strong> <str<strong>on</strong>g>the</str<strong>on</strong>g> actual ACL c<strong>on</strong>tents and come back again.<br />

*<br />

* The upper bound must be less than KAUTH_ACL_MAX_ENTRIES. The<br />

* value here is fairly arbitrary. It's ok to have a zero count.<br />

*/<br />

When <str<strong>on</strong>g>the</str<strong>on</strong>g> new thread c<strong>on</strong>structs <str<strong>on</strong>g>the</str<strong>on</strong>g> kauth_filesec structure, it does so by directly<br />

manipulating <str<strong>on</strong>g>the</str<strong>on</strong>g> positi<strong>on</strong> <str<strong>on</strong>g>of</str<strong>on</strong>g> <str<strong>on</strong>g>the</str<strong>on</strong>g> structure <strong>on</strong> <str<strong>on</strong>g>the</str<strong>on</strong>g> stack as such:<br />

// get stack address?<br />

Page 23


p = (unsigned int)&stackAnchor & 0xFFFFF000;<br />

// kauth_filesec.fsec_magic<br />

(p + 0xEC0) = 0x12CC16D;<br />

// kauth_filesec.fsec_acl.entrycount = KAUTH_FILESEC_NOACL<br />

(p + 0xEE4) = -1;<br />

// kauth_filesec.fsec_acl.acl_ace[...]<br />

memcpy(&stackAnchor & 0xFFFFF000 | 0xEEC, pExploit, 128);<br />

The stack has <str<strong>on</strong>g>the</str<strong>on</strong>g> following layout at <str<strong>on</strong>g>the</str<strong>on</strong>g> start <str<strong>on</strong>g>of</str<strong>on</strong>g> <str<strong>on</strong>g>the</str<strong>on</strong>g> new thread’s executi<strong>on</strong>:<br />

char stackAnchor; // [sp+101Fh] [bp-2031h]@1<br />

unsigned int size; // [sp+2020h] [bp-1030h]@12<br />

char buffer[4096]; // [sp+2024h] [bp-102Ch]@12<br />

int v26; // [sp+3024h] [bp-2Ch]@7<br />

mach_port_t c<strong>on</strong>necti<strong>on</strong>; // [sp+3028h] [bp-28h]@4<br />

kern_return_t result; // [sp+302Ch] [bp-24h]@4<br />

mach_port_t masterPort; // [sp+3030h] [bp-20h]@3 MAPDST<br />

Using <str<strong>on</strong>g>the</str<strong>on</strong>g> variable dubbed stackAnchor, <str<strong>on</strong>g>the</str<strong>on</strong>g> new thread finds a page boundary address for<br />

<str<strong>on</strong>g>the</str<strong>on</strong>g> stack. Then, by allocating a large array to ensure that at least <strong>on</strong>e page <str<strong>on</strong>g>of</str<strong>on</strong>g> <str<strong>on</strong>g>the</str<strong>on</strong>g> stack is<br />

unused by functi<strong>on</strong> critical variables, <str<strong>on</strong>g>the</str<strong>on</strong>g> new thread can c<strong>on</strong>struct a kauth_filesec structure<br />

that c<strong>on</strong>tains significantly more informati<strong>on</strong> than is necessary. By setting <str<strong>on</strong>g>the</str<strong>on</strong>g><br />

acl_entrycount to indicate that <str<strong>on</strong>g>the</str<strong>on</strong>g>re are no ACE records, when open_extended<br />

processes <str<strong>on</strong>g>the</str<strong>on</strong>g> kauth_filesec structure, it will not attempt to parse any data bey<strong>on</strong>d <str<strong>on</strong>g>the</str<strong>on</strong>g><br />

acl_flags variable, thus preserving <str<strong>on</strong>g>the</str<strong>on</strong>g> integrity <str<strong>on</strong>g>of</str<strong>on</strong>g> <str<strong>on</strong>g>the</str<strong>on</strong>g> exploit buffer and preventing <str<strong>on</strong>g>the</str<strong>on</strong>g> kernel<br />

from possibly having issue with how <str<strong>on</strong>g>the</str<strong>on</strong>g> exploit buffer would be interpreted as an actual ACE<br />

record. The end result <str<strong>on</strong>g>of</str<strong>on</strong>g> calling open_extended is to copy <str<strong>on</strong>g>the</str<strong>on</strong>g> exploit buffer (al<strong>on</strong>g with <str<strong>on</strong>g>the</str<strong>on</strong>g><br />

clock_ops_overwrite buffer) into kernel memory.<br />

The new thread takes advantage <str<strong>on</strong>g>of</str<strong>on</strong>g> this behavioral oddity within <str<strong>on</strong>g>the</str<strong>on</strong>g> open_extended syscall in<br />

order to place <str<strong>on</strong>g>the</str<strong>on</strong>g> (unmodified) payload into <str<strong>on</strong>g>the</str<strong>on</strong>g> kernel memory. The address for that payload is<br />

<str<strong>on</strong>g>the</str<strong>on</strong>g>n recovered using <str<strong>on</strong>g>the</str<strong>on</strong>g> previously discussed vulnerability that allows kernel memory to be<br />

leaked back to user mode. When <str<strong>on</strong>g>the</str<strong>on</strong>g> AppleKeyStore vulnerability is exploited, <str<strong>on</strong>g>the</str<strong>on</strong>g> variable<br />

dubbed buffer is passed to io_service_open_extended (<str<strong>on</strong>g>the</str<strong>on</strong>g> same variable that resides<br />

adjacent to <str<strong>on</strong>g>the</str<strong>on</strong>g> stackAnchor). This behavior means AppleKeyStore returned a pointer for<br />

kernel memory that was ultimately next to <str<strong>on</strong>g>the</str<strong>on</strong>g> exploit code copied in by <str<strong>on</strong>g>the</str<strong>on</strong>g> syscall to<br />

open_extended. Therefore, <str<strong>on</strong>g>the</str<strong>on</strong>g> purpose <str<strong>on</strong>g>of</str<strong>on</strong>g> <str<strong>on</strong>g>the</str<strong>on</strong>g> new thread is not to overwrite <str<strong>on</strong>g>the</str<strong>on</strong>g> clock handler<br />

pointers, but ra<str<strong>on</strong>g>the</str<strong>on</strong>g>r to set <str<strong>on</strong>g>the</str<strong>on</strong>g> stage for such an attack.<br />

Once <str<strong>on</strong>g>the</str<strong>on</strong>g> new thread completes, <str<strong>on</strong>g>the</str<strong>on</strong>g> variable c<strong>on</strong>taining <str<strong>on</strong>g>the</str<strong>on</strong>g> memory address <str<strong>on</strong>g>of</str<strong>on</strong>g> <str<strong>on</strong>g>the</str<strong>on</strong>g> exploit<br />

buffer obtained by <str<strong>on</strong>g>the</str<strong>on</strong>g> new thread as part <str<strong>on</strong>g>of</str<strong>on</strong>g> <str<strong>on</strong>g>the</str<strong>on</strong>g> AppleKeyStore informati<strong>on</strong> leak is tested to<br />

determine if it was indeed set by <str<strong>on</strong>g>the</str<strong>on</strong>g> new thread (prior to calling <str<strong>on</strong>g>the</str<strong>on</strong>g> new thread, <str<strong>on</strong>g>the</str<strong>on</strong>g> variable<br />

that holds <str<strong>on</strong>g>the</str<strong>on</strong>g> kernel address is initialized to 0x12345678). If <str<strong>on</strong>g>the</str<strong>on</strong>g> new thread did not successfully<br />

obtain <str<strong>on</strong>g>the</str<strong>on</strong>g> kernel memory locati<strong>on</strong>, 32Stage2 will call <str<strong>on</strong>g>the</str<strong>on</strong>g> assert callback and exit.<br />

After <str<strong>on</strong>g>the</str<strong>on</strong>g> completi<strong>on</strong> <str<strong>on</strong>g>of</str<strong>on</strong>g> <str<strong>on</strong>g>the</str<strong>on</strong>g> new thread’s activities, and if <str<strong>on</strong>g>the</str<strong>on</strong>g> ph<strong>on</strong>e reports itself as “iPh<strong>on</strong>e4,1”<br />

(<str<strong>on</strong>g>the</str<strong>on</strong>g> iPh<strong>on</strong>e 4 series), <str<strong>on</strong>g>the</str<strong>on</strong>g> main thread will generate up to 1000 threads. Each <str<strong>on</strong>g>of</str<strong>on</strong>g> <str<strong>on</strong>g>the</str<strong>on</strong>g> threads will<br />

Page 24


generate a very tight while loop that waits until a global variable is set to less than 0 (it defaults<br />

to 1000 at <str<strong>on</strong>g>the</str<strong>on</strong>g> time <str<strong>on</strong>g>the</str<strong>on</strong>g> threads are generated). It is unclear why this behavior is restricted to <str<strong>on</strong>g>the</str<strong>on</strong>g><br />

iPh<strong>on</strong>e 4s, as <str<strong>on</strong>g>the</str<strong>on</strong>g> result <str<strong>on</strong>g>of</str<strong>on</strong>g> this behavior would seem to have value <strong>on</strong> all platforms. This thread<br />

resource exhausti<strong>on</strong> decreases <str<strong>on</strong>g>the</str<strong>on</strong>g> probability that ano<str<strong>on</strong>g>the</str<strong>on</strong>g>r thread will spawn and thus compete<br />

for <str<strong>on</strong>g>the</str<strong>on</strong>g> memory resources during <str<strong>on</strong>g>the</str<strong>on</strong>g> UAF exploitati<strong>on</strong>.<br />

Payload C<strong>on</strong>structi<strong>on</strong> and Kernel Inserti<strong>on</strong> (64-Bit)<br />

Given <str<strong>on</strong>g>the</str<strong>on</strong>g> differences in <str<strong>on</strong>g>the</str<strong>on</strong>g> triggering mechanism used within 64Stage2, <str<strong>on</strong>g>the</str<strong>on</strong>g> setup and payload<br />

c<strong>on</strong>structi<strong>on</strong> is somewhat different. Ra<str<strong>on</strong>g>the</str<strong>on</strong>g>r than creating pipes and overwriting <str<strong>on</strong>g>the</str<strong>on</strong>g> clock getattr<br />

handler, a sysctl handler is overwritten, ultimately resulting in an execute primitive that uses<br />

OSSerializer::serialize in a similar way to 32Stage2. In order to establish an execute<br />

primitive, 64Stage2 uses <str<strong>on</strong>g>the</str<strong>on</strong>g> sysctl interface for net.inet.ip.dummynet.extract_heap to<br />

which 64Stage2 passes a specially crafted data structure that allows <str<strong>on</strong>g>the</str<strong>on</strong>g> binary to overwrite <str<strong>on</strong>g>the</str<strong>on</strong>g><br />

functi<strong>on</strong> pointer resp<strong>on</strong>sible for interfacing with <str<strong>on</strong>g>the</str<strong>on</strong>g> kernel variable. The end result is a<br />

framework, similar in nature to <str<strong>on</strong>g>the</str<strong>on</strong>g> getattr handlers, that allows <str<strong>on</strong>g>the</str<strong>on</strong>g> 64Stage2 binary to<br />

execute arbitrary code ROP chains within <str<strong>on</strong>g>the</str<strong>on</strong>g> kernel from user space.<br />

Establishing Kernel Read/Write Primitives (32-Bit)<br />

With <str<strong>on</strong>g>the</str<strong>on</strong>g> exploit code now in kernel memory, 32Stage2 must activate <str<strong>on</strong>g>the</str<strong>on</strong>g> code in order to install<br />

<str<strong>on</strong>g>the</str<strong>on</strong>g> new clock_ops handlers that give userland access to <str<strong>on</strong>g>the</str<strong>on</strong>g> <str<strong>on</strong>g>the</str<strong>on</strong>g> kernel memory. 32Stage2<br />

uses a use-after-free (UAF) vulnerability within <str<strong>on</strong>g>the</str<strong>on</strong>g> io_service_open_extended<br />

deserializati<strong>on</strong> routine.<br />

While io_service_open_extended’s deserializati<strong>on</strong> functi<strong>on</strong>ality was previously shown in<br />

this report to allow <str<strong>on</strong>g>the</str<strong>on</strong>g> leakage <str<strong>on</strong>g>of</str<strong>on</strong>g> kernel address informati<strong>on</strong>, ano<str<strong>on</strong>g>the</str<strong>on</strong>g>r vulnerability in <str<strong>on</strong>g>the</str<strong>on</strong>g> same<br />

comp<strong>on</strong>ent can be used to execute arbitrary code within <str<strong>on</strong>g>the</str<strong>on</strong>g> kernel. When<br />

io_service_open_extended is passed a properties data blob, <str<strong>on</strong>g>the</str<strong>on</strong>g> functi<strong>on</strong> will copy <str<strong>on</strong>g>the</str<strong>on</strong>g><br />

c<strong>on</strong>tents from user space into kernel space before passing <str<strong>on</strong>g>the</str<strong>on</strong>g> informati<strong>on</strong> to<br />

OSUnserializeXML. OSUnserializeXML in turn passes <str<strong>on</strong>g>the</str<strong>on</strong>g> informati<strong>on</strong> to<br />

OSUnserializeBinary if <str<strong>on</strong>g>the</str<strong>on</strong>g> kOSSerializeBinarySignature value is present at <str<strong>on</strong>g>the</str<strong>on</strong>g><br />

beginning <str<strong>on</strong>g>of</str<strong>on</strong>g> <str<strong>on</strong>g>the</str<strong>on</strong>g> data blob. It is within OSUnserializeBinary that <str<strong>on</strong>g>the</str<strong>on</strong>g> vulnerability exists.<br />

The data blob supplied in <str<strong>on</strong>g>the</str<strong>on</strong>g> properties parameter represents an XML dicti<strong>on</strong>ary (or<br />

c<strong>on</strong>tainer) that has been serialized. In order to rec<strong>on</strong>struct <str<strong>on</strong>g>the</str<strong>on</strong>g> relati<strong>on</strong>ships,<br />

OSUnserializeBinary iterates through <str<strong>on</strong>g>the</str<strong>on</strong>g> entire data blob to parse out <str<strong>on</strong>g>the</str<strong>on</strong>g> various data<br />

objects. It is possible that during <str<strong>on</strong>g>the</str<strong>on</strong>g> encoding process (<str<strong>on</strong>g>the</str<strong>on</strong>g> process <str<strong>on</strong>g>of</str<strong>on</strong>g> turning <str<strong>on</strong>g>the</str<strong>on</strong>g> original XML<br />

into its binary representati<strong>on</strong>) that <str<strong>on</strong>g>the</str<strong>on</strong>g> same object is found repeatedly. In order to more<br />

efficiently handle repetitive data, objects are stored within an array (objsArray) and objects<br />

within <str<strong>on</strong>g>the</str<strong>on</strong>g> rec<strong>on</strong>structed XML dicti<strong>on</strong>ary can be represented by an index into <str<strong>on</strong>g>the</str<strong>on</strong>g> array <str<strong>on</strong>g>of</str<strong>on</strong>g><br />

objects.<br />

Page 25


Within OSUnserializeBinary, a while loop iterates through each encoded object within <str<strong>on</strong>g>the</str<strong>on</strong>g><br />

supplied data blob. The loop begins by determining <str<strong>on</strong>g>the</str<strong>on</strong>g> type <str<strong>on</strong>g>of</str<strong>on</strong>g> object (e.g.,<br />

kOSSerializeDicti<strong>on</strong>ary, kOSSerializeArray, kOSSerializeNumber, and so <strong>on</strong>) and<br />

its size.<br />

len = (key & kOSSerializeDataMask);<br />

...<br />

switch (kOSSerializeTypeMask & key)<br />

{<br />

case kOSSerializeDicti<strong>on</strong>ary:<br />

o = newDict = OSDicti<strong>on</strong>ary::withCapacity(len);<br />

newCollect = (len != 0);<br />

break;<br />

case kOSSerializeArray:<br />

o = newArray = OSArray::withCapacity(len);<br />

newCollect = (len != 0);<br />

break;<br />

case kOSSerializeSet:<br />

o = newSet = OSSet::withCapacity(len);<br />

newCollect = (len != 0);<br />

...<br />

case kOSSerializeObject:<br />

if (len >= objsIdx) break;<br />

o = objsArray[len];<br />

o->retain();<br />

isRef = true;<br />

break;<br />

...<br />

}<br />

The switch statement dispatches <str<strong>on</strong>g>the</str<strong>on</strong>g> appropriate instructi<strong>on</strong>s for handling each type <str<strong>on</strong>g>of</str<strong>on</strong>g> object<br />

found within <str<strong>on</strong>g>the</str<strong>on</strong>g> data blob. These instructi<strong>on</strong>s can generate new objects and set flags related to<br />

<str<strong>on</strong>g>the</str<strong>on</strong>g> objects depending <strong>on</strong> what <str<strong>on</strong>g>the</str<strong>on</strong>g> particular object requires during <str<strong>on</strong>g>the</str<strong>on</strong>g> deserializati<strong>on</strong> process.<br />

The kOSSerializeObject object type is a special case that represents an already<br />

deserialized object and, as such, sets a flag isRef to true indicating that <str<strong>on</strong>g>the</str<strong>on</strong>g> object is a<br />

reference to an existing object already within <str<strong>on</strong>g>the</str<strong>on</strong>g> objsArray array.<br />

If <str<strong>on</strong>g>the</str<strong>on</strong>g> isRef value is not set to true, <str<strong>on</strong>g>the</str<strong>on</strong>g> current object that just underwent deserializati<strong>on</strong> is<br />

added to <str<strong>on</strong>g>the</str<strong>on</strong>g> objsArray by means <str<strong>on</strong>g>of</str<strong>on</strong>g> setAtIndex:<br />

if (!isRef)<br />

{<br />

setAtIndex(objs, objsIdx, o);<br />

if (!ok) break;<br />

objsIdx++;<br />

}<br />

setAtIndex is a macro that, am<strong>on</strong>g o<str<strong>on</strong>g>the</str<strong>on</strong>g>r things, adds an object (o) to <str<strong>on</strong>g>the</str<strong>on</strong>g> objsArray. While<br />

more robust array objects exist within <str<strong>on</strong>g>the</str<strong>on</strong>g> <strong>iOS</strong> envir<strong>on</strong>ment, such as OSArray (an array<br />

c<strong>on</strong>tainer that will handle reference counting automatically), OSUnserializeBinary takes a<br />

more manual route for array-object management <str<strong>on</strong>g>of</str<strong>on</strong>g> <str<strong>on</strong>g>the</str<strong>on</strong>g> objects it has deserialized. Once<br />

Page 26


deserialized, <str<strong>on</strong>g>the</str<strong>on</strong>g> object’s reference count is decremented by calling o->release(), which will<br />

lead to <str<strong>on</strong>g>the</str<strong>on</strong>g> object being free()ed in most cases. The excepti<strong>on</strong> to this behavior occurs within<br />

kOSSerializeObject objects.<br />

Since a kOSSerializeObject object represents an object that is referenced by o<str<strong>on</strong>g>the</str<strong>on</strong>g>r entries,<br />

it is necessary to retain <str<strong>on</strong>g>the</str<strong>on</strong>g> object after serializati<strong>on</strong>. As a result, during deserializati<strong>on</strong><br />

kOSSerializeObject objects will call o->retain(), <str<strong>on</strong>g>the</str<strong>on</strong>g>reby incrementing <str<strong>on</strong>g>the</str<strong>on</strong>g> reference<br />

count for <str<strong>on</strong>g>the</str<strong>on</strong>g> object and preventing its removal from memory.<br />

A serialized data blob allows for <str<strong>on</strong>g>the</str<strong>on</strong>g> same key to be used more than <strong>on</strong>ce. In o<str<strong>on</strong>g>the</str<strong>on</strong>g>r words, it is<br />

possible to have XML code that looks like:<br />

<br />

KEY1<br />

1<br />

KEY1<br />

2<br />

<br />

The above XML, <strong>on</strong>ce serialized, will c<strong>on</strong>tain five objects. The first object will be <str<strong>on</strong>g>the</str<strong>on</strong>g> dicti<strong>on</strong>ary<br />

c<strong>on</strong>tainer ( as a kOSSerializeDicti<strong>on</strong>ary object), followed by a symbol<br />

representing <str<strong>on</strong>g>the</str<strong>on</strong>g> key (as a kOSSerializeSymbol entry c<strong>on</strong>taining “KEY1”) and its data object<br />

(a kOSSerializeNumber entry for <str<strong>on</strong>g>the</str<strong>on</strong>g> integer 1). The fourth entry specifies ano<str<strong>on</strong>g>the</str<strong>on</strong>g>r key<br />

object, assigned KEY1 again, which is now a string object (kOSSerializeString) c<strong>on</strong>taining<br />

<str<strong>on</strong>g>the</str<strong>on</strong>g> string “2”. As part <str<strong>on</strong>g>of</str<strong>on</strong>g> <str<strong>on</strong>g>the</str<strong>on</strong>g> deserializati<strong>on</strong> process, <str<strong>on</strong>g>the</str<strong>on</strong>g> reuse <str<strong>on</strong>g>of</str<strong>on</strong>g> KEY1 results in <str<strong>on</strong>g>the</str<strong>on</strong>g> object that<br />

follows replacing <str<strong>on</strong>g>the</str<strong>on</strong>g> original value assigned to KEY1. This reassignment <str<strong>on</strong>g>of</str<strong>on</strong>g> a key with new data<br />

is <str<strong>on</strong>g>the</str<strong>on</strong>g> situati<strong>on</strong> where OSUnserializeBinary is vulnerable to attack.<br />

As stated previously, when an object is deserialized, and so l<strong>on</strong>g as that object is not a<br />

kOSSerializeObject, <str<strong>on</strong>g>the</str<strong>on</strong>g> object is stored in <str<strong>on</strong>g>the</str<strong>on</strong>g> objsArray for later reference. This<br />

storage is <str<strong>on</strong>g>the</str<strong>on</strong>g> result <str<strong>on</strong>g>of</str<strong>on</strong>g> <str<strong>on</strong>g>the</str<strong>on</strong>g> setAtIndex macro seen here:<br />

#define setAtIndex(v, idx, o) \<br />

if (idx >= v##Capacity) \<br />

{ \<br />

uint32_t ncap = v##Capacity + 64; \<br />

type<str<strong>on</strong>g>of</str<strong>on</strong>g>(v##Array) nbuf = (type<str<strong>on</strong>g>of</str<strong>on</strong>g>(v##Array)) kalloc_c<strong>on</strong>tainer(ncap * size<str<strong>on</strong>g>of</str<strong>on</strong>g>(o)); \<br />

if (!nbuf) ok = false; \<br />

if (v##Array) \<br />

{ \<br />

bcopy(v##Array, nbuf, v##Capacity * size<str<strong>on</strong>g>of</str<strong>on</strong>g>(o)); \<br />

kfree(v##Array, v##Capacity * size<str<strong>on</strong>g>of</str<strong>on</strong>g>(o)); \<br />

} \<br />

v##Array = nbuf; \<br />

v##Capacity = ncap; \<br />

} \<br />

if (ok) v##Array[idx] = o;<br />

Page 27


The macro will expand <str<strong>on</strong>g>the</str<strong>on</strong>g> objsArray to accommodate <str<strong>on</strong>g>the</str<strong>on</strong>g> additi<strong>on</strong>al object and assign <str<strong>on</strong>g>the</str<strong>on</strong>g><br />

object to <str<strong>on</strong>g>the</str<strong>on</strong>g> end <str<strong>on</strong>g>of</str<strong>on</strong>g> <str<strong>on</strong>g>the</str<strong>on</strong>g> objsArray without increasing its reference count by means <str<strong>on</strong>g>of</str<strong>on</strong>g> a o-<br />

>retain() call. The problem with this method is that when a sec<strong>on</strong>d object replaces an<br />

existing object (in our example this is whenever <str<strong>on</strong>g>the</str<strong>on</strong>g> string object replaces <str<strong>on</strong>g>the</str<strong>on</strong>g> number object for<br />

KEY1), <str<strong>on</strong>g>the</str<strong>on</strong>g> first object is released and subsequently freed, but a pointer to <str<strong>on</strong>g>the</str<strong>on</strong>g> now freed object<br />

exists within <str<strong>on</strong>g>the</str<strong>on</strong>g> objsArray. Normally this would simply be a bad programming design issue,<br />

but <str<strong>on</strong>g>the</str<strong>on</strong>g> problem is compounded if a reference is made to <str<strong>on</strong>g>the</str<strong>on</strong>g> object via a<br />

kOSSerializeObject entry. If a kOSSerializeObject entry references, by index, <str<strong>on</strong>g>the</str<strong>on</strong>g> now<br />

dangling pointer <str<strong>on</strong>g>of</str<strong>on</strong>g> <str<strong>on</strong>g>the</str<strong>on</strong>g> freed object, <str<strong>on</strong>g>the</str<strong>on</strong>g> call to o->retain() will attempt to execute a virtual<br />

functi<strong>on</strong> that is attacker-c<strong>on</strong>trolled.<br />

In order to exploit this use-after-free c<strong>on</strong>diti<strong>on</strong>, 32Stage2 must take c<strong>on</strong>trol <str<strong>on</strong>g>of</str<strong>on</strong>g> <str<strong>on</strong>g>the</str<strong>on</strong>g> memory<br />

locati<strong>on</strong> that was deallocated and place a custom vtable that will have <str<strong>on</strong>g>the</str<strong>on</strong>g> entry for retain<br />

directed to a functi<strong>on</strong> <str<strong>on</strong>g>of</str<strong>on</strong>g> its own choosing. Installing a custom vtable requires having access to<br />

two deallocated, adjacent memory locati<strong>on</strong>s. Since it is not possible to directly overwrite <str<strong>on</strong>g>the</str<strong>on</strong>g><br />

vtable <str<strong>on</strong>g>of</str<strong>on</strong>g> an object during <str<strong>on</strong>g>the</str<strong>on</strong>g> serializati<strong>on</strong> process, by allocating and <str<strong>on</strong>g>the</str<strong>on</strong>g>n freeing two memory<br />

locati<strong>on</strong>s, 32Stage2 can use an OSData or OSString object to replace two memory locati<strong>on</strong>s<br />

at <strong>on</strong>ce with <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> memory locati<strong>on</strong>s c<strong>on</strong>taining <str<strong>on</strong>g>the</str<strong>on</strong>g> malicious vtable. The easiest way to<br />

understand this c<strong>on</strong>cept is to look at <str<strong>on</strong>g>the</str<strong>on</strong>g> payload that 32Stage2 generates to exploit <str<strong>on</strong>g>the</str<strong>on</strong>g><br />

OSUnserializeBinary UAF vulnerability.<br />

The malicious payload that 32Stage2 generates for this vulnerability depends <strong>on</strong> <str<strong>on</strong>g>the</str<strong>on</strong>g> <strong>iOS</strong><br />

versi<strong>on</strong>. For <strong>iOS</strong> versi<strong>on</strong> 9.3.2 through at least 9.3.3, <str<strong>on</strong>g>the</str<strong>on</strong>g> payload takes <str<strong>on</strong>g>the</str<strong>on</strong>g> following form:<br />

[0x00] kOSSerializeBinarySignature<br />

[0x04] kOSSerializeEndCollect<strong>on</strong> | kOSSerializeDicti<strong>on</strong>ary | 0x10<br />

[0x08] kOSSerializeString | 4<br />

[0x0C] “sy2”<br />

[0x10] kOSSerializeData | 0x14<br />

[0x14] {payload buffer}<br />

[0x28]kOSSerializeEndCollect<strong>on</strong> | kOSSerializeObject | 1<br />

For <strong>iOS</strong> 9.0 through 9.3.1, <str<strong>on</strong>g>the</str<strong>on</strong>g> payload takes <str<strong>on</strong>g>the</str<strong>on</strong>g> following form:<br />

[0x00] kOSSerializeBinarySignature<br />

[0x04] kOSSerializeEndCollect<strong>on</strong> | kOSSerializeDicti<strong>on</strong>ary | 0x10<br />

[0x08] kOSSerializeString | 4<br />

[0x0C] “sy2”<br />

[0x10] kOSSerializeEndCollect<strong>on</strong> | kOSSerializeArray | 0x10<br />

[0x14] kOSSerializeDicti<strong>on</strong>ary | 0x10<br />

[0x18] kOSSerializeSymbol | 4<br />

[0x1C] “sy1”<br />

[0x20] kOSSerializeData | 0x14<br />

[0x24] ”ffff\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0”<br />

[0x38] kOSSerializeSymbol | 4<br />

[0x3C] “sy1”<br />

[0x40] kOSSerializeEndCollect<strong>on</strong> | kOSSerializeSymbol | 4<br />

[0x44] “sy1”<br />

[0x48] kOSSerializeString | 0x1C<br />

[0x4C] {payload buffer}<br />

Page 28


[0x68] kOSSerializeString | 0x1C<br />

[0x6C] {payload buffer}<br />

[0x88] kOSSerializeString | 0x1C<br />

[0x8C] {payload buffer}<br />

[0xA8] kOSSerializeEndCollect<strong>on</strong> | kOSSerializeObject | 5<br />

While structurally <str<strong>on</strong>g>the</str<strong>on</strong>g>y look somewhat different, ultimately <str<strong>on</strong>g>the</str<strong>on</strong>g>y both exploit <str<strong>on</strong>g>the</str<strong>on</strong>g> same UAF<br />

vulnerability. The simpler payload (for <strong>iOS</strong> 9.3.2 and later) is <str<strong>on</strong>g>the</str<strong>on</strong>g> easiest to understand. When<br />

OSUnserializeBinary begins <str<strong>on</strong>g>the</str<strong>on</strong>g> parsing process to deserialize <str<strong>on</strong>g>the</str<strong>on</strong>g> payload, <str<strong>on</strong>g>the</str<strong>on</strong>g> functi<strong>on</strong><br />

will create a new dicti<strong>on</strong>ary object as a result <str<strong>on</strong>g>of</str<strong>on</strong>g> <str<strong>on</strong>g>the</str<strong>on</strong>g> entry at <str<strong>on</strong>g>of</str<strong>on</strong>g>fset 0x04. Within this dicti<strong>on</strong>ary<br />

are two unkeyed objects. The first object is an OSString object with <str<strong>on</strong>g>the</str<strong>on</strong>g> value sy2 (specified<br />

in <str<strong>on</strong>g>of</str<strong>on</strong>g>fsets 0x08 and 0x0C, respectively). Offset 0x10 specifies an OSData object <str<strong>on</strong>g>of</str<strong>on</strong>g> 0x14 (20)<br />

bytes in size. The OSData object c<strong>on</strong>tains <str<strong>on</strong>g>the</str<strong>on</strong>g> payload buffer data structure. Since <str<strong>on</strong>g>the</str<strong>on</strong>g> objects<br />

are unkeyed, OSUnserializeBinary will replace <str<strong>on</strong>g>the</str<strong>on</strong>g> OSString object with <str<strong>on</strong>g>the</str<strong>on</strong>g> OSData<br />

object but leave <str<strong>on</strong>g>the</str<strong>on</strong>g> pointers in place in objsArray. With <str<strong>on</strong>g>the</str<strong>on</strong>g> OSString object having no<br />

retain() calls, <str<strong>on</strong>g>the</str<strong>on</strong>g> OSString is deallocated, <str<strong>on</strong>g>the</str<strong>on</strong>g>reby putting two memory arrays into <str<strong>on</strong>g>the</str<strong>on</strong>g> free<br />

list (<strong>on</strong>e for <str<strong>on</strong>g>the</str<strong>on</strong>g> OSString object itself and <strong>on</strong>e for <str<strong>on</strong>g>the</str<strong>on</strong>g> string associated with <str<strong>on</strong>g>the</str<strong>on</strong>g> OSString<br />

object).<br />

When OSUnserializeBinary parses <str<strong>on</strong>g>the</str<strong>on</strong>g> kOSSerializeData entry, a new OSData object<br />

is allocated and thus c<strong>on</strong>sumes <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> freed memory locati<strong>on</strong>s from <str<strong>on</strong>g>the</str<strong>on</strong>g> free list. When <str<strong>on</strong>g>the</str<strong>on</strong>g><br />

data associated with <str<strong>on</strong>g>the</str<strong>on</strong>g> kOSSerializeData entry is copied into <str<strong>on</strong>g>the</str<strong>on</strong>g> OSData object, a new<br />

buffer is allocated for <str<strong>on</strong>g>the</str<strong>on</strong>g> data, which c<strong>on</strong>sumes <str<strong>on</strong>g>the</str<strong>on</strong>g> remaining data locati<strong>on</strong> from <str<strong>on</strong>g>the</str<strong>on</strong>g> free list. At<br />

this point, <str<strong>on</strong>g>the</str<strong>on</strong>g> dangling pointer in objsArray has been replaced with an OSData object’s<br />

data. It is <str<strong>on</strong>g>the</str<strong>on</strong>g> data associated with <str<strong>on</strong>g>the</str<strong>on</strong>g> OSData object that c<strong>on</strong>tains <str<strong>on</strong>g>the</str<strong>on</strong>g> malicious payload that<br />

will ultimately give 32Stage2 write access into <str<strong>on</strong>g>the</str<strong>on</strong>g> kernel in order to install <str<strong>on</strong>g>the</str<strong>on</strong>g> read/write<br />

primitives.<br />

Regardless <str<strong>on</strong>g>of</str<strong>on</strong>g> <str<strong>on</strong>g>the</str<strong>on</strong>g> <strong>iOS</strong> versi<strong>on</strong>, <str<strong>on</strong>g>the</str<strong>on</strong>g> malicious payload c<strong>on</strong>tains <str<strong>on</strong>g>the</str<strong>on</strong>g> same payload buffer. The<br />

payload buffer is a 20-byte structure c<strong>on</strong>sisting <str<strong>on</strong>g>of</str<strong>on</strong>g> <str<strong>on</strong>g>the</str<strong>on</strong>g> following elements:<br />

[00] address <str<strong>on</strong>g>of</str<strong>on</strong>g> uaf_payload_buffer + 8<br />

[04] {uninitialized data from stack}<br />

[08] address <str<strong>on</strong>g>of</str<strong>on</strong>g> uaf_payload_buffer<br />

[0C] static value <str<strong>on</strong>g>of</str<strong>on</strong>g> 20<br />

[10] address <str<strong>on</strong>g>of</str<strong>on</strong>g> OSSerializer::serialize<br />

The layout <str<strong>on</strong>g>of</str<strong>on</strong>g> <str<strong>on</strong>g>the</str<strong>on</strong>g> payload must c<strong>on</strong>tain <str<strong>on</strong>g>the</str<strong>on</strong>g> pointer to <str<strong>on</strong>g>the</str<strong>on</strong>g> new retain functi<strong>on</strong> at <str<strong>on</strong>g>of</str<strong>on</strong>g>fset 0x10.<br />

32Stage2 uses <str<strong>on</strong>g>the</str<strong>on</strong>g> OSSerializer::serialize functi<strong>on</strong> as <str<strong>on</strong>g>the</str<strong>on</strong>g> replacement retain. This<br />

layout means that <str<strong>on</strong>g>the</str<strong>on</strong>g> remainder <str<strong>on</strong>g>of</str<strong>on</strong>g> <str<strong>on</strong>g>the</str<strong>on</strong>g> payload must now mimic <str<strong>on</strong>g>the</str<strong>on</strong>g> vtable <str<strong>on</strong>g>of</str<strong>on</strong>g> an<br />

OSSerializer object. As explained previously in Establishing Read/Write/Execute Primitives<br />

<strong>on</strong> Previously Rooted Devices, <str<strong>on</strong>g>the</str<strong>on</strong>g> OSSerializer::serialize functi<strong>on</strong> will call <str<strong>on</strong>g>the</str<strong>on</strong>g> functi<strong>on</strong><br />

at <str<strong>on</strong>g>of</str<strong>on</strong>g>fset 0x10 <str<strong>on</strong>g>of</str<strong>on</strong>g> <str<strong>on</strong>g>the</str<strong>on</strong>g> supplied vtable while passing <str<strong>on</strong>g>of</str<strong>on</strong>g>fsets 0x08 and 0x0C <str<strong>on</strong>g>of</str<strong>on</strong>g> <str<strong>on</strong>g>the</str<strong>on</strong>g> vtable to <str<strong>on</strong>g>the</str<strong>on</strong>g><br />

called functi<strong>on</strong>. Given that <str<strong>on</strong>g>of</str<strong>on</strong>g>fset 0x10 is set to OSSerializer::serialize, <str<strong>on</strong>g>the</str<strong>on</strong>g> functi<strong>on</strong> is<br />

Page 29


eing called again, but <str<strong>on</strong>g>the</str<strong>on</strong>g> sec<strong>on</strong>d call will be given <str<strong>on</strong>g>the</str<strong>on</strong>g> vtable specified at <str<strong>on</strong>g>of</str<strong>on</strong>g>fset 0x08. This call<br />

kicks <str<strong>on</strong>g>of</str<strong>on</strong>g>f a series <str<strong>on</strong>g>of</str<strong>on</strong>g> subsequent calls that ultimately leads to a call to _copyin that replaces <str<strong>on</strong>g>the</str<strong>on</strong>g><br />

getattr handlers for <str<strong>on</strong>g>the</str<strong>on</strong>g> real-time and battery clocks, as described in Establishing<br />

Read/Write/Execute Primitives <strong>on</strong> Previously Rooted Devices.<br />

Following <str<strong>on</strong>g>the</str<strong>on</strong>g> executi<strong>on</strong> <str<strong>on</strong>g>of</str<strong>on</strong>g> <str<strong>on</strong>g>the</str<strong>on</strong>g> exploit, and if <str<strong>on</strong>g>the</str<strong>on</strong>g> victim’s ph<strong>on</strong>e is an “iPh<strong>on</strong>e4,1” model, <str<strong>on</strong>g>the</str<strong>on</strong>g><br />

global variable c<strong>on</strong>trolling <str<strong>on</strong>g>the</str<strong>on</strong>g> 1000 threads generated previously is set to -1 in order to<br />

terminate <str<strong>on</strong>g>the</str<strong>on</strong>g> threads.<br />

To verify that <str<strong>on</strong>g>the</str<strong>on</strong>g> battery clock’s getattr handler is working as a kernel memory address<br />

reader, clock_get_attributes is called with <str<strong>on</strong>g>the</str<strong>on</strong>g> read locati<strong>on</strong> specified as <str<strong>on</strong>g>the</str<strong>on</strong>g> base<br />

address for <str<strong>on</strong>g>the</str<strong>on</strong>g> kernel. If <str<strong>on</strong>g>the</str<strong>on</strong>g> result from clock_get_attributes is not <str<strong>on</strong>g>the</str<strong>on</strong>g> magic value <str<strong>on</strong>g>of</str<strong>on</strong>g><br />

0xFEEDFACE, <str<strong>on</strong>g>the</str<strong>on</strong>g> attempt is made <strong>on</strong>ce more. A sec<strong>on</strong>d failure results in <str<strong>on</strong>g>the</str<strong>on</strong>g> assert callback<br />

being called and 32Stage2 terminating.<br />

Establishing Kernel Read/Write Primitives (64-Bit)<br />

The same underlying vulnerability is exploited in <str<strong>on</strong>g>the</str<strong>on</strong>g> 64-bit versi<strong>on</strong> <str<strong>on</strong>g>of</str<strong>on</strong>g> Stage 2. In principle, <str<strong>on</strong>g>the</str<strong>on</strong>g><br />

exploit is structured in a very similar way. The primary difference is that <str<strong>on</strong>g>the</str<strong>on</strong>g> ultimate execute<br />

primitive is established by writing to <str<strong>on</strong>g>the</str<strong>on</strong>g> net.inet.ip.dummynet.extract_heap sysctl<br />

handler. The OSSerializer::serialize is used in a similar way as within 32Stage2.<br />

Arbitrary code executi<strong>on</strong> (through <str<strong>on</strong>g>the</str<strong>on</strong>g> executi<strong>on</strong> <str<strong>on</strong>g>of</str<strong>on</strong>g> arbitrary ROP chains) is <str<strong>on</strong>g>the</str<strong>on</strong>g>n achieved using<br />

<str<strong>on</strong>g>the</str<strong>on</strong>g> same mechanism described in Establishing Kernel Execute Primitive (32-Bit).<br />

Establishing a Kernel Execute Primitive (32-Bit)<br />

As explained previously in Installing Kernel Access Handlers <strong>on</strong> Rooted Devices, <str<strong>on</strong>g>the</str<strong>on</strong>g> real-time<br />

clock’s getattr handler points to OSSerializer::serialize, which allows <str<strong>on</strong>g>the</str<strong>on</strong>g> caller <str<strong>on</strong>g>of</str<strong>on</strong>g><br />

clock_get_attributes to pass a specially crafted structure to<br />

OSSerializer::serialize in order to execute instructi<strong>on</strong>s within kernel space. By virtue <str<strong>on</strong>g>of</str<strong>on</strong>g><br />

executing within kernel space, <str<strong>on</strong>g>the</str<strong>on</strong>g> userland 32Stage2 process must have a way <str<strong>on</strong>g>of</str<strong>on</strong>g> transferring<br />

data to <str<strong>on</strong>g>the</str<strong>on</strong>g> kernel address space in a reliable manner. 32Stage2 uses a clever quirk <str<strong>on</strong>g>of</str<strong>on</strong>g> <str<strong>on</strong>g>the</str<strong>on</strong>g><br />

pipe-created pipe set to accomplish this task.<br />

After establishing <str<strong>on</strong>g>the</str<strong>on</strong>g> battery clock’s new getattr handler as _bufattr_cpx, 32Stage2 has<br />

a reliable method for reading DWORDs from <str<strong>on</strong>g>the</str<strong>on</strong>g> kernel address space into userland. 32Stage2<br />

uses this functi<strong>on</strong>ality to find <str<strong>on</strong>g>the</str<strong>on</strong>g> addrperm value stored within <str<strong>on</strong>g>the</str<strong>on</strong>g> kernel. addrperm defines<br />

<str<strong>on</strong>g>the</str<strong>on</strong>g> <str<strong>on</strong>g>of</str<strong>on</strong>g>fset applied to <str<strong>on</strong>g>the</str<strong>on</strong>g> address from <str<strong>on</strong>g>the</str<strong>on</strong>g> kernel when passed to userland in order to obfuscate<br />

<str<strong>on</strong>g>the</str<strong>on</strong>g> true locati<strong>on</strong> <str<strong>on</strong>g>of</str<strong>on</strong>g> <str<strong>on</strong>g>the</str<strong>on</strong>g> data in <str<strong>on</strong>g>the</str<strong>on</strong>g> kernel. By obtaining this value, it is possible to deobfuscate<br />

kernel addresses back to <str<strong>on</strong>g>the</str<strong>on</strong>g>ir real address values. 32Stage2 calls fstat <strong>on</strong> <str<strong>on</strong>g>the</str<strong>on</strong>g> read pipe<br />

from <str<strong>on</strong>g>the</str<strong>on</strong>g> generated pipe set and <str<strong>on</strong>g>the</str<strong>on</strong>g>n calculates <str<strong>on</strong>g>the</str<strong>on</strong>g> difference between <str<strong>on</strong>g>the</str<strong>on</strong>g> locati<strong>on</strong> <str<strong>on</strong>g>of</str<strong>on</strong>g> <str<strong>on</strong>g>the</str<strong>on</strong>g> stat<br />

Page 30


structure and <str<strong>on</strong>g>the</str<strong>on</strong>g> kernel address space. This value is <str<strong>on</strong>g>the</str<strong>on</strong>g>n stored in a global variable for use by<br />

functi<strong>on</strong>s that must access kernel memory for <str<strong>on</strong>g>the</str<strong>on</strong>g> purposes <str<strong>on</strong>g>of</str<strong>on</strong>g> code executi<strong>on</strong>.<br />

Whenever 32Stage2 wants to execute code within <str<strong>on</strong>g>the</str<strong>on</strong>g> kernel, <str<strong>on</strong>g>the</str<strong>on</strong>g> following data structure is<br />

written to <str<strong>on</strong>g>the</str<strong>on</strong>g> write pipe <str<strong>on</strong>g>of</str<strong>on</strong>g> <str<strong>on</strong>g>the</str<strong>on</strong>g> generated pipe set:<br />

[00] argument 1<br />

[04] argument 2<br />

[08] address <str<strong>on</strong>g>of</str<strong>on</strong>g> code to execute<br />

In order to call <str<strong>on</strong>g>the</str<strong>on</strong>g> functi<strong>on</strong> specified in <str<strong>on</strong>g>of</str<strong>on</strong>g>fset 8 <str<strong>on</strong>g>of</str<strong>on</strong>g> <str<strong>on</strong>g>the</str<strong>on</strong>g> data structure, ano<str<strong>on</strong>g>the</str<strong>on</strong>g>r DWORD is<br />

prepended to <str<strong>on</strong>g>the</str<strong>on</strong>g> structure and passed to <str<strong>on</strong>g>the</str<strong>on</strong>g> real-time clock’s getattr handler (accessed via<br />

OSSerializer::serialize), which places argument 1 into R3 and argument 2 into R1<br />

before calling <str<strong>on</strong>g>the</str<strong>on</strong>g> address specified for <str<strong>on</strong>g>the</str<strong>on</strong>g> functi<strong>on</strong> to execute. By prepending <str<strong>on</strong>g>the</str<strong>on</strong>g> unused<br />

DWORD to <str<strong>on</strong>g>the</str<strong>on</strong>g> data structure, <str<strong>on</strong>g>the</str<strong>on</strong>g> data structure becomes <str<strong>on</strong>g>the</str<strong>on</strong>g> vtable replacement for<br />

OSSerializer. This technique is used in two different functi<strong>on</strong>s within 32Stage2. One functi<strong>on</strong><br />

allows arbitrary kernel functi<strong>on</strong> calls and <str<strong>on</strong>g>the</str<strong>on</strong>g> o<str<strong>on</strong>g>the</str<strong>on</strong>g>r is used to write DWORD values into <str<strong>on</strong>g>the</str<strong>on</strong>g><br />

kernel address space.<br />

Patching <str<strong>on</strong>g>the</str<strong>on</strong>g> Kernel to Allow Kernel Port Access<br />

With <str<strong>on</strong>g>the</str<strong>on</strong>g> ability to read, write, and execute arbitrary locati<strong>on</strong>s within <str<strong>on</strong>g>the</str<strong>on</strong>g> kernel address space,<br />

<str<strong>on</strong>g>the</str<strong>on</strong>g> next step involves gaining more direct access to <str<strong>on</strong>g>the</str<strong>on</strong>g> kernel through <str<strong>on</strong>g>the</str<strong>on</strong>g> kernel port. The<br />

functi<strong>on</strong> task_for_pid will return an error if called with <str<strong>on</strong>g>the</str<strong>on</strong>g> PID value <str<strong>on</strong>g>of</str<strong>on</strong>g> 0. In order to bypass<br />

this protecti<strong>on</strong>, Stage 2 modifies four different locati<strong>on</strong>s within <str<strong>on</strong>g>the</str<strong>on</strong>g> task_for_pid functi<strong>on</strong>.<br />

Before <str<strong>on</strong>g>the</str<strong>on</strong>g> patching <str<strong>on</strong>g>of</str<strong>on</strong>g> task_for_pid begins, Stage 2 determines if <str<strong>on</strong>g>the</str<strong>on</strong>g> area requiring <str<strong>on</strong>g>the</str<strong>on</strong>g><br />

patch is within a regi<strong>on</strong> <str<strong>on</strong>g>of</str<strong>on</strong>g> memory that is read/execute. If <str<strong>on</strong>g>the</str<strong>on</strong>g> memory is n<strong>on</strong>-writable, Stage 2<br />

will directly modify <str<strong>on</strong>g>the</str<strong>on</strong>g> permissi<strong>on</strong>s <str<strong>on</strong>g>of</str<strong>on</strong>g> <str<strong>on</strong>g>the</str<strong>on</strong>g> memory regi<strong>on</strong> to allow for write access and <str<strong>on</strong>g>the</str<strong>on</strong>g>n<br />

invalidate <str<strong>on</strong>g>the</str<strong>on</strong>g> dcache and flush <str<strong>on</strong>g>the</str<strong>on</strong>g> data and instructi<strong>on</strong> TLBs in order to ensure that <str<strong>on</strong>g>the</str<strong>on</strong>g><br />

memory regi<strong>on</strong> is updated with <str<strong>on</strong>g>the</str<strong>on</strong>g> new permissi<strong>on</strong>s.<br />

After patching <str<strong>on</strong>g>the</str<strong>on</strong>g> task_for_pid functi<strong>on</strong> to allow <str<strong>on</strong>g>the</str<strong>on</strong>g> caller to gain a port to <str<strong>on</strong>g>the</str<strong>on</strong>g> kernel, Stage<br />

2 will attempt to get a kernel port by calling task_for_pid(mach_task_self, 0, &port)<br />

up to five times with a 100 mllisec<strong>on</strong>d delay between each attempt before calling <str<strong>on</strong>g>the</str<strong>on</strong>g> assert<br />

callback and exiting.<br />

Page 31


Secti<strong>on</strong> 3: Privilege Escalati<strong>on</strong> and Activating <str<strong>on</strong>g>the</str<strong>on</strong>g><br />

Jailbreak Binary<br />

This secti<strong>on</strong> covers <str<strong>on</strong>g>the</str<strong>on</strong>g> final steps carried out in Stage 2 to gain root access <strong>on</strong><br />

<str<strong>on</strong>g>the</str<strong>on</strong>g> iPh<strong>on</strong>e, disable code signing, <str<strong>on</strong>g>the</str<strong>on</strong>g>n drop and activate <str<strong>on</strong>g>the</str<strong>on</strong>g> jailbreak binary. This<br />

stage leverages <str<strong>on</strong>g>the</str<strong>on</strong>g> final Trident vulnerability, where kernel memory corrupti<strong>on</strong><br />

leads to jailbreak (CVE-2016-4656).<br />

Page 32


System Modificati<strong>on</strong> for Privilege Escalati<strong>on</strong><br />

The next step for 32Stage2 is to gain root access over <str<strong>on</strong>g>the</str<strong>on</strong>g> victim’s ph<strong>on</strong>e. If <str<strong>on</strong>g>the</str<strong>on</strong>g> Stage 2 process<br />

is not currently running as root (UID = 0), which it will not be <strong>on</strong> a n<strong>on</strong>-jailbroken ph<strong>on</strong>e, <str<strong>on</strong>g>the</str<strong>on</strong>g>n<br />

Stage 2 patches <str<strong>on</strong>g>the</str<strong>on</strong>g> setreuid functi<strong>on</strong> to skip <str<strong>on</strong>g>the</str<strong>on</strong>g> check for privilege escalati<strong>on</strong>. Once <str<strong>on</strong>g>the</str<strong>on</strong>g><br />

modificati<strong>on</strong> to setreuid is complete, <str<strong>on</strong>g>the</str<strong>on</strong>g> functi<strong>on</strong> is called up to five times (with 500ms delays<br />

between each call) until setreuid(0, 0) returns successful. After five attempts (or after a<br />

successful setreuid call), <str<strong>on</strong>g>the</str<strong>on</strong>g> modificati<strong>on</strong> made to setreuid is reversed. A final check <str<strong>on</strong>g>of</str<strong>on</strong>g> <str<strong>on</strong>g>the</str<strong>on</strong>g><br />

process’s user value (UID) is made to ensure that it is, indeed, root (0). If <str<strong>on</strong>g>the</str<strong>on</strong>g> functi<strong>on</strong> getuid<br />

returns any value o<str<strong>on</strong>g>the</str<strong>on</strong>g>r than 0, <str<strong>on</strong>g>the</str<strong>on</strong>g> assert is called and Stage 2 exits.<br />

Stage 2 calls <str<strong>on</strong>g>the</str<strong>on</strong>g> kernel functi<strong>on</strong> kauth_cred_get_with_ref by means <str<strong>on</strong>g>of</str<strong>on</strong>g> <str<strong>on</strong>g>the</str<strong>on</strong>g> real-time clock<br />

clock_get_attributes vector in order to receive <str<strong>on</strong>g>the</str<strong>on</strong>g> credentials for <str<strong>on</strong>g>the</str<strong>on</strong>g> main thread.<br />

Following this, Stage 2 locates <str<strong>on</strong>g>the</str<strong>on</strong>g> mac_policy_list, which c<strong>on</strong>tains <str<strong>on</strong>g>the</str<strong>on</strong>g> list <str<strong>on</strong>g>of</str<strong>on</strong>g> access c<strong>on</strong>trol<br />

policy modules currently loaded into <str<strong>on</strong>g>the</str<strong>on</strong>g> <strong>iOS</strong> kernel. Stage 2 examines <str<strong>on</strong>g>the</str<strong>on</strong>g> list looking for a<br />

module that starts with <str<strong>on</strong>g>the</str<strong>on</strong>g> name “Seat”, referring to <str<strong>on</strong>g>the</str<strong>on</strong>g> “Seatbelt sandbox policy”. If <str<strong>on</strong>g>the</str<strong>on</strong>g> policy<br />

module is not found, Stage 2 calls <str<strong>on</strong>g>the</str<strong>on</strong>g> assert callback and terminates. If <str<strong>on</strong>g>the</str<strong>on</strong>g> module is found,<br />

however, <str<strong>on</strong>g>the</str<strong>on</strong>g> mpc_field_<str<strong>on</strong>g>of</str<strong>on</strong>g>f member is read and modified to allow <str<strong>on</strong>g>the</str<strong>on</strong>g> current process<br />

greater access to <str<strong>on</strong>g>the</str<strong>on</strong>g> victim’s iPh<strong>on</strong>e.<br />

With access to <str<strong>on</strong>g>the</str<strong>on</strong>g> kernel port now established and restricti<strong>on</strong>s removed that would prevent<br />

Stage 2 from performing privileged acti<strong>on</strong>s normally blocked by sandbox policy, Stage 2 no<br />

l<strong>on</strong>ger requires <str<strong>on</strong>g>the</str<strong>on</strong>g> hooked getattr handler for <str<strong>on</strong>g>the</str<strong>on</strong>g> real-time clock. To ensure that future calls<br />

to this handler do not crash <str<strong>on</strong>g>the</str<strong>on</strong>g> ph<strong>on</strong>e, <str<strong>on</strong>g>the</str<strong>on</strong>g> getattr functi<strong>on</strong> pointer is modified to point to <str<strong>on</strong>g>the</str<strong>on</strong>g><br />

instructi<strong>on</strong>s:<br />

BX LR<br />

This new handler functi<strong>on</strong> effectively turns future calls to <str<strong>on</strong>g>the</str<strong>on</strong>g> real-time clock’s getattr handler<br />

into a NOP. This is presumably d<strong>on</strong>e to ensure that future calls <str<strong>on</strong>g>the</str<strong>on</strong>g> to <str<strong>on</strong>g>the</str<strong>on</strong>g> getattr handler (by<br />

some o<str<strong>on</strong>g>the</str<strong>on</strong>g>r process) do not have unintended c<strong>on</strong>sequences and cause <str<strong>on</strong>g>the</str<strong>on</strong>g> kernel to crash.<br />

Disabling Code Signing<br />

By default, <strong>iOS</strong> <strong>on</strong> a standard iPh<strong>on</strong>e will prevent unsigned code from running through normal<br />

means, such as an execv or system call. Likewise, modificati<strong>on</strong>s to <str<strong>on</strong>g>the</str<strong>on</strong>g> root file system are<br />

prevented by setting <str<strong>on</strong>g>the</str<strong>on</strong>g> filesystem as read-<strong>on</strong>ly. These are situati<strong>on</strong>s that will prevent Stage 2<br />

from executing a jailbreak program and will prevent <str<strong>on</strong>g>the</str<strong>on</strong>g> jailbreak program, if it activates, from<br />

being able to modify <str<strong>on</strong>g>the</str<strong>on</strong>g> system. Stage 2 modifies several kernel functi<strong>on</strong>s and two kernel<br />

extensi<strong>on</strong>s (kext) in order to permit <str<strong>on</strong>g>the</str<strong>on</strong>g>se forbidden acti<strong>on</strong>s. Stage 2 starts by finding <str<strong>on</strong>g>the</str<strong>on</strong>g> kext for<br />

Page 33


com.apple.driver.AppleMobileFileIntegrity and com.apple.driver.LightweightVolumeManager.<br />

The com.apple.driver.AppleMobileFileIntegrity (AMFI) extensi<strong>on</strong> is resp<strong>on</strong>sible for enforcing<br />

<strong>iOS</strong>’s code signing functi<strong>on</strong>ality. The com.apple.driver.LightweightVolumeManager extensi<strong>on</strong> is<br />

resp<strong>on</strong>sible for <str<strong>on</strong>g>the</str<strong>on</strong>g> partiti<strong>on</strong> table <str<strong>on</strong>g>of</str<strong>on</strong>g> <str<strong>on</strong>g>the</str<strong>on</strong>g> main storage device.<br />

Stage 2 locates each <str<strong>on</strong>g>of</str<strong>on</strong>g> extensi<strong>on</strong>s by calling OSKextCopyLoadedKextInfo for each extenti<strong>on</strong>’s<br />

name, which returns a dicti<strong>on</strong>ary object c<strong>on</strong>taining informati<strong>on</strong> about <str<strong>on</strong>g>the</str<strong>on</strong>g> extensi<strong>on</strong>. Within <str<strong>on</strong>g>the</str<strong>on</strong>g><br />

dicti<strong>on</strong>ary is <str<strong>on</strong>g>the</str<strong>on</strong>g> loading <str<strong>on</strong>g>of</str<strong>on</strong>g>fset <str<strong>on</strong>g>of</str<strong>on</strong>g> <str<strong>on</strong>g>the</str<strong>on</strong>g> extensi<strong>on</strong> being queried that Stage 2 turns into <str<strong>on</strong>g>the</str<strong>on</strong>g> kernel<br />

memory address by adding <str<strong>on</strong>g>the</str<strong>on</strong>g> known kernel slide value.<br />

Armed with <str<strong>on</strong>g>the</str<strong>on</strong>g> kernel address <str<strong>on</strong>g>of</str<strong>on</strong>g> <str<strong>on</strong>g>the</str<strong>on</strong>g> AMFI, Stage 2 locates <str<strong>on</strong>g>the</str<strong>on</strong>g> following global variables:<br />

● amfi_get_out_<str<strong>on</strong>g>of</str<strong>on</strong>g>_my_way<br />

● cs_enforcement_disable<br />

These two variables, when set, disable AFMI (amfi_get_out_<str<strong>on</strong>g>of</str<strong>on</strong>g>_my_way) and disable code<br />

signing enforcement (cs_enforcement_disable). Stage 2 <str<strong>on</strong>g>the</str<strong>on</strong>g>n sets two more global<br />

variables: debug_flags and DEBUGflag. These two variables allow for debugging privilege<br />

<strong>on</strong> <str<strong>on</strong>g>the</str<strong>on</strong>g> victim’s iPh<strong>on</strong>e, fur<str<strong>on</strong>g>the</str<strong>on</strong>g>r reducing <str<strong>on</strong>g>the</str<strong>on</strong>g> restricti<strong>on</strong>s that <str<strong>on</strong>g>the</str<strong>on</strong>g> sandbox (Seatbelt) imposes <strong>on</strong><br />

<str<strong>on</strong>g>the</str<strong>on</strong>g> device.<br />

Next, Stage 2 patches <str<strong>on</strong>g>the</str<strong>on</strong>g> kernel functi<strong>on</strong> vm_map_enter and vm_map_protect in order to<br />

disable code signing verificati<strong>on</strong>s (making it possible to allocate RWX regi<strong>on</strong>s) within <str<strong>on</strong>g>the</str<strong>on</strong>g> virtual<br />

memory manager. Following this, Stage 2 patches <str<strong>on</strong>g>the</str<strong>on</strong>g> _mapForIO functi<strong>on</strong> within <str<strong>on</strong>g>the</str<strong>on</strong>g><br />

LightweightVolumeManager before patching <str<strong>on</strong>g>the</str<strong>on</strong>g> kernel functi<strong>on</strong> csops to disable even more<br />

code signing protecti<strong>on</strong>s.<br />

Remounting <str<strong>on</strong>g>the</str<strong>on</strong>g> Drive<br />

In order to jailbreak a device, <str<strong>on</strong>g>the</str<strong>on</strong>g> root file system must be accessible for writing. Stage 2 tests<br />

<str<strong>on</strong>g>the</str<strong>on</strong>g> writability <str<strong>on</strong>g>of</str<strong>on</strong>g> <str<strong>on</strong>g>the</str<strong>on</strong>g> root file system by calling <str<strong>on</strong>g>the</str<strong>on</strong>g> access functi<strong>on</strong> against /sbin/launchd to<br />

determine if Stage 2 has write access to <str<strong>on</strong>g>the</str<strong>on</strong>g> root file system. If <str<strong>on</strong>g>the</str<strong>on</strong>g> file is read-<strong>on</strong>ly, Stage 2<br />

patches <str<strong>on</strong>g>the</str<strong>on</strong>g> kernel functi<strong>on</strong> _mac_mount to disable <str<strong>on</strong>g>the</str<strong>on</strong>g> protecti<strong>on</strong> policy that prevents<br />

remounting <str<strong>on</strong>g>the</str<strong>on</strong>g> filesystem as read/write and <str<strong>on</strong>g>the</str<strong>on</strong>g>n remounts <str<strong>on</strong>g>the</str<strong>on</strong>g> root filesystem as read/write by<br />

calling mount(“hfs”, “/”, MNT_UPDATE, mountData) where mountData specifies <str<strong>on</strong>g>the</str<strong>on</strong>g><br />

/dev/disk0s1s1 device.<br />

Stage 2 is written such that it will <strong>on</strong>ly operate <strong>on</strong> <strong>iOS</strong> 9 series iPh<strong>on</strong>es, but code exists that<br />

suggest it was <strong>on</strong>ce used <strong>on</strong> older <strong>iOS</strong> versi<strong>on</strong>s. As evidence to support this claim, <str<strong>on</strong>g>the</str<strong>on</strong>g>re exists<br />

a functi<strong>on</strong> that is called after Stage 2 remounts <str<strong>on</strong>g>the</str<strong>on</strong>g> root file system that modifies its executi<strong>on</strong><br />

path if it is running <strong>on</strong> <strong>iOS</strong> 7, <strong>iOS</strong> 8, or <strong>iOS</strong> 9. Depending <strong>on</strong> <str<strong>on</strong>g>the</str<strong>on</strong>g> <strong>iOS</strong> versi<strong>on</strong>, <str<strong>on</strong>g>the</str<strong>on</strong>g> functi<strong>on</strong> calls<br />

fsctl <strong>on</strong> ei<str<strong>on</strong>g>the</str<strong>on</strong>g>r /bin/launchctl (for <strong>iOS</strong> 7 and 8) or /bin/launchd (for <strong>iOS</strong> 9). The<br />

fsctl call will modify <str<strong>on</strong>g>the</str<strong>on</strong>g> low disk space warning threshold as well as <str<strong>on</strong>g>the</str<strong>on</strong>g> very low disk space<br />

warning threshold, setting <str<strong>on</strong>g>the</str<strong>on</strong>g> values to 8192 and 8208, respectively.<br />

Page 34


Cleanup<br />

Stage 2 is activated as <str<strong>on</strong>g>the</str<strong>on</strong>g> result <str<strong>on</strong>g>of</str<strong>on</strong>g> a bug in Safari that allows for arbitrary code executi<strong>on</strong>. As<br />

<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> last activities Stage 2 performs prior to dropping and activating <str<strong>on</strong>g>the</str<strong>on</strong>g> jailbreak binary,<br />

Stage 2 attempts to cover its infecti<strong>on</strong> vector by cleaning up <str<strong>on</strong>g>the</str<strong>on</strong>g> history and cache files from<br />

Safari. The process <str<strong>on</strong>g>of</str<strong>on</strong>g> clearing <str<strong>on</strong>g>the</str<strong>on</strong>g> Safari browser history and cache files is straightforward and<br />

<strong>iOS</strong> versi<strong>on</strong>-specific.<br />

For <strong>iOS</strong> 8 and <strong>iOS</strong> 9 (Stage 2 will terminate at <str<strong>on</strong>g>the</str<strong>on</strong>g> beginning if it is not running <strong>on</strong> <strong>iOS</strong> 9), <str<strong>on</strong>g>the</str<strong>on</strong>g><br />

following files are summarily deleted from <str<strong>on</strong>g>the</str<strong>on</strong>g> victim’s iPh<strong>on</strong>e to remove browser and cache<br />

informati<strong>on</strong>:<br />

●<br />

●<br />

●<br />

●<br />

●<br />

●<br />

●<br />

●<br />

●<br />

●<br />

/Library/Safari/SuspendState.plist<br />

/Library/Safari/History.db<br />

/Library/Safari/History.db-shm<br />

/Library/Safari/History.db-wal<br />

/Library/Safari/History.db-journal<br />

/Library/Caches/com.apple.mobilesafari/Cache.db<br />

/Library/Caches/com.apple.mobilesafari/Cache.db-shm<br />

/Library/Caches/com.apple.mobilesafari/Cache.db-wal<br />

/Library/Caches/com.apple.mobilesafari/Cache.db-journal<br />

(files in <str<strong>on</strong>g>the</str<strong>on</strong>g> directory) /Library/Caches/com.apple.mobilesafari/fsCachedData/<br />

For <strong>iOS</strong> 7, <str<strong>on</strong>g>the</str<strong>on</strong>g> following files are removed:<br />

●<br />

●<br />

●<br />

●<br />

●<br />

/Library/Safari/SuspendState.plist<br />

/Library/Caches/com.apple.mobilesafari/Cache.db<br />

/Library/Caches/com.apple.mobilesafari/Cache.db-shm<br />

/Library/Caches/com.apple.mobilesafari/Cache.db-wal<br />

/Library/Caches/com.apple.mobilesafari/Cache.db-journal<br />

The functi<strong>on</strong> c<strong>on</strong>cludes by calling sync to ensure <str<strong>on</strong>g>the</str<strong>on</strong>g> deleti<strong>on</strong>s are written to disk.<br />

Next Stage Installati<strong>on</strong><br />

Again, showing evidence <str<strong>on</strong>g>of</str<strong>on</strong>g> <str<strong>on</strong>g>the</str<strong>on</strong>g> use <str<strong>on</strong>g>of</str<strong>on</strong>g> code originally targeting an older <strong>iOS</strong> versi<strong>on</strong>, <str<strong>on</strong>g>the</str<strong>on</strong>g> next<br />

functi<strong>on</strong> <str<strong>on</strong>g>the</str<strong>on</strong>g> main thread calls decompresses and drops two files <strong>on</strong>to <str<strong>on</strong>g>the</str<strong>on</strong>g> victim’s filesystem.<br />

The following code snippet illustrates how Stage 2 determines <str<strong>on</strong>g>the</str<strong>on</strong>g> locati<strong>on</strong> <str<strong>on</strong>g>of</str<strong>on</strong>g> <str<strong>on</strong>g>the</str<strong>on</strong>g> jailbreaker<br />

binary <strong>on</strong> <str<strong>on</strong>g>the</str<strong>on</strong>g> victim’s device:<br />

if ( (unsigned int)(majorVersi<strong>on</strong> - 8) >= 2 )<br />

{<br />

if ( majorVersi<strong>on</strong> == 7 )<br />

{<br />

pszJBFilenamePath = "/bin/sh";<br />

if ( flag)<br />

Page 35


pszJBFilenamePath = "/private/var/tmp/jb-install";<br />

}<br />

else<br />

{<br />

assert();<br />

writeLog(3, "%.2s%5.5d\n", "bh.c", 134);<br />

exit(-1);<br />

pszJBFilenamePath = 0;<br />

}<br />

}<br />

else<br />

{<br />

pszJBFilenamePath = "/sbin/mount_nfs.temp";<br />

}<br />

The code snippet shows that for <strong>iOS</strong> versi<strong>on</strong> 7, <str<strong>on</strong>g>the</str<strong>on</strong>g> install path for <str<strong>on</strong>g>the</str<strong>on</strong>g> next stage’s binary is<br />

ei<str<strong>on</strong>g>the</str<strong>on</strong>g>r /bin/sh or /private/var/tmp/jb-install (if flag is n<strong>on</strong>-zero). For <strong>iOS</strong> versi<strong>on</strong>s<br />

older than 7, <str<strong>on</strong>g>the</str<strong>on</strong>g> assert callback is called and <str<strong>on</strong>g>the</str<strong>on</strong>g> program terminates. For <strong>iOS</strong> 8 and greater,<br />

<str<strong>on</strong>g>the</str<strong>on</strong>g> install path is specified as /sbin/mount_nfs.temp.<br />

The size <str<strong>on</strong>g>of</str<strong>on</strong>g> <str<strong>on</strong>g>the</str<strong>on</strong>g> data blob c<strong>on</strong>taining <str<strong>on</strong>g>the</str<strong>on</strong>g> next stage binary is verified to be n<strong>on</strong>-zero. If <str<strong>on</strong>g>the</str<strong>on</strong>g> size is<br />

zero, <str<strong>on</strong>g>the</str<strong>on</strong>g> assert callback occurs and Stage 2 is terminated. The BZ2_* API functi<strong>on</strong>s are <str<strong>on</strong>g>the</str<strong>on</strong>g>n<br />

used by Stage 2 to decompress <str<strong>on</strong>g>the</str<strong>on</strong>g> data into two files: <str<strong>on</strong>g>the</str<strong>on</strong>g> first file is <str<strong>on</strong>g>the</str<strong>on</strong>g> next stage binary,<br />

which, for <strong>iOS</strong> 9, is stored at /sbin/mount_nfs.temp. The sec<strong>on</strong>d file is <str<strong>on</strong>g>the</str<strong>on</strong>g> c<strong>on</strong>figurati<strong>on</strong><br />

file, which is stored at /private/var/tmp/jb_cfg.<br />

The permissi<strong>on</strong>s <str<strong>on</strong>g>of</str<strong>on</strong>g> <str<strong>on</strong>g>the</str<strong>on</strong>g> two files are changed to 0755 (making <str<strong>on</strong>g>the</str<strong>on</strong>g> files executable) before<br />

c<strong>on</strong>trol returns to <str<strong>on</strong>g>the</str<strong>on</strong>g> main thread.<br />

The final functi<strong>on</strong> that Stage 2 calls before terminating is resp<strong>on</strong>sible for moving <str<strong>on</strong>g>the</str<strong>on</strong>g> binary<br />

dropped by <str<strong>on</strong>g>the</str<strong>on</strong>g> previous step. For <strong>iOS</strong> versi<strong>on</strong>s 8 and 9, <str<strong>on</strong>g>the</str<strong>on</strong>g> file /sbin/mount_nfs.temp is<br />

renamed to /sbin/mount_nfs. If <str<strong>on</strong>g>the</str<strong>on</strong>g> <strong>iOS</strong> <strong>on</strong> <str<strong>on</strong>g>the</str<strong>on</strong>g> victim’s ph<strong>on</strong>e is <strong>iOS</strong> 9, an attempt is made<br />

to delete /sbin/mount_nfs prior to <str<strong>on</strong>g>the</str<strong>on</strong>g> renaming operati<strong>on</strong>. After renaming <str<strong>on</strong>g>the</str<strong>on</strong>g> file, <str<strong>on</strong>g>the</str<strong>on</strong>g> assert<br />

callback functi<strong>on</strong> is called followed by <str<strong>on</strong>g>the</str<strong>on</strong>g> exit functi<strong>on</strong>, terminating Stage 2.<br />

Once executi<strong>on</strong> returns to <str<strong>on</strong>g>the</str<strong>on</strong>g> main thread, Stage 2 terminates silently.<br />

Existing Jailbreak Detecti<strong>on</strong><br />

As menti<strong>on</strong>ed previously, <str<strong>on</strong>g>the</str<strong>on</strong>g> Stage 2 binary operates in two distinct modes. The first, which has<br />

already been discussed, c<strong>on</strong>stitutes a complete <strong>iOS</strong> exploit and jailbreak. The sec<strong>on</strong>d is <str<strong>on</strong>g>the</str<strong>on</strong>g><br />

code path taken when <str<strong>on</strong>g>the</str<strong>on</strong>g> Stage 2 binary is run <strong>on</strong> a system that has already been jailbroken. In<br />

this mode, Stage 2 simply takes advantage <str<strong>on</strong>g>of</str<strong>on</strong>g> <str<strong>on</strong>g>the</str<strong>on</strong>g> existing jailbreak backdoors to install <str<strong>on</strong>g>the</str<strong>on</strong>g><br />

<str<strong>on</strong>g>Pegasus</str<strong>on</strong>g>-specific kernel patches.<br />

Page 36


To determine whe<str<strong>on</strong>g>the</str<strong>on</strong>g>r <str<strong>on</strong>g>the</str<strong>on</strong>g> <str<strong>on</strong>g>the</str<strong>on</strong>g> device has already been jailbroken, Stage 2 attempts to acquire a<br />

valid mach port (a handle) into <str<strong>on</strong>g>the</str<strong>on</strong>g> <strong>iOS</strong> kernel using a comm<strong>on</strong> jailbreak backdoor. This check is<br />

performed simply by calling task_for_pid with <str<strong>on</strong>g>the</str<strong>on</strong>g> PID value set to 0. Patching<br />

task_for_pid in this way is a comm<strong>on</strong> backdoor mechanism used by <strong>iOS</strong> jailbreaks that<br />

provides direct kernel memory access to a user mode process. Calling task_for_pid with a<br />

PID <str<strong>on</strong>g>of</str<strong>on</strong>g> 0 is not normally allowed by <strong>iOS</strong>. If task_for_pid returns a valid task port, <str<strong>on</strong>g>the</str<strong>on</strong>g>n <str<strong>on</strong>g>the</str<strong>on</strong>g><br />

Stage 2 process has elevated access to <str<strong>on</strong>g>the</str<strong>on</strong>g> kernel and can forgo <str<strong>on</strong>g>the</str<strong>on</strong>g> privilege escalati<strong>on</strong> steps<br />

described previously.<br />

Stage 2 also checks for <str<strong>on</strong>g>the</str<strong>on</strong>g> presence <str<strong>on</strong>g>of</str<strong>on</strong>g> <str<strong>on</strong>g>the</str<strong>on</strong>g> binary /bin/sh. On a n<strong>on</strong>-jailbroken ph<strong>on</strong>e, this<br />

binary should never exist. When Stage 2 detects <str<strong>on</strong>g>the</str<strong>on</strong>g> presence <str<strong>on</strong>g>of</str<strong>on</strong>g> this binary, it assumes that <str<strong>on</strong>g>the</str<strong>on</strong>g><br />

existing jailbreak is ei<str<strong>on</strong>g>the</str<strong>on</strong>g>r incompatible with <str<strong>on</strong>g>Pegasus</str<strong>on</strong>g> or that all required kernel patches are<br />

already in place and no fur<str<strong>on</strong>g>the</str<strong>on</strong>g>r acti<strong>on</strong> is needed. When /bin/sh is identified <strong>on</strong> a device, prior<br />

to exploitati<strong>on</strong>, Stage 2 simply exits cleanly.<br />

Page 37


Secti<strong>on</strong> 4: <str<strong>on</strong>g>Pegasus</str<strong>on</strong>g> Persistence Mechanism<br />

This secti<strong>on</strong> details <str<strong>on</strong>g>the</str<strong>on</strong>g> persistence mechanism used by <str<strong>on</strong>g>Pegasus</str<strong>on</strong>g> to remain <strong>on</strong> <str<strong>on</strong>g>the</str<strong>on</strong>g><br />

device after compromise via an exploit <str<strong>on</strong>g>of</str<strong>on</strong>g> <str<strong>on</strong>g>the</str<strong>on</strong>g> Trident vulnerabilities, and c<strong>on</strong>tinue<br />

to execute unsigned code each time <str<strong>on</strong>g>the</str<strong>on</strong>g> device reboots.<br />

Page 38


<str<strong>on</strong>g>Pegasus</str<strong>on</strong>g> Persistence Mechanism<br />

The persistence mechanism used by <str<strong>on</strong>g>Pegasus</str<strong>on</strong>g> to reliably execute unsigned code each time <str<strong>on</strong>g>the</str<strong>on</strong>g><br />

device boots (and, ultimately, execute <str<strong>on</strong>g>the</str<strong>on</strong>g> kernel exploit to again jailbreak <str<strong>on</strong>g>the</str<strong>on</strong>g> device) relies <strong>on</strong> a<br />

combinati<strong>on</strong> <str<strong>on</strong>g>of</str<strong>on</strong>g> two distinct issues.<br />

The first issue is <str<strong>on</strong>g>the</str<strong>on</strong>g> presence <str<strong>on</strong>g>of</str<strong>on</strong>g> <str<strong>on</strong>g>the</str<strong>on</strong>g> rtbuddyd service within a plist (to be launched <strong>on</strong> device<br />

boot). Note that prior to <strong>iOS</strong> 10, rtbuddyd is present <strong>on</strong> some iPh<strong>on</strong>e devices for example <str<strong>on</strong>g>the</str<strong>on</strong>g><br />

iPh<strong>on</strong>e 6S, but not <strong>on</strong> o<str<strong>on</strong>g>the</str<strong>on</strong>g>rs like <str<strong>on</strong>g>the</str<strong>on</strong>g> iPh<strong>on</strong>e 6. As a result, any signed binary that can be copied<br />

into <str<strong>on</strong>g>the</str<strong>on</strong>g> specified path (/usr/libexec/rtbuddyd) will be executed at boot time with <str<strong>on</strong>g>the</str<strong>on</strong>g><br />

arguments specified in <str<strong>on</strong>g>the</str<strong>on</strong>g> plist (specifically “--early-boot”).<br />

rtbuddyProgramArgumentsrtbuddyd --<br />

early-bootPerformInRestoreRequireSuccess<br />

Program/usr/libexec/rtbuddyd<br />

As a result <str<strong>on</strong>g>of</str<strong>on</strong>g> this behavior, any signed binary <strong>on</strong> <str<strong>on</strong>g>the</str<strong>on</strong>g> system can be executed at boot with a<br />

single argument. By creating a symlink named --early-boot within <str<strong>on</strong>g>the</str<strong>on</strong>g> current working<br />

directory, an arbitrary file can be passed as <str<strong>on</strong>g>the</str<strong>on</strong>g> first argument to <str<strong>on</strong>g>the</str<strong>on</strong>g> arbitrary signed binary that<br />

has been copied to <str<strong>on</strong>g>the</str<strong>on</strong>g> rtbuddyd locati<strong>on</strong>.<br />

The sec<strong>on</strong>d issue leveraged in this persistence mechanism is a vulnerability within <str<strong>on</strong>g>the</str<strong>on</strong>g><br />

JavaScriptCore binary. <str<strong>on</strong>g>Pegasus</str<strong>on</strong>g> leverages <str<strong>on</strong>g>the</str<strong>on</strong>g> previously described behavior in order to<br />

execute <str<strong>on</strong>g>the</str<strong>on</strong>g> jsc binary (JavaScriptCore) by copying it to <str<strong>on</strong>g>the</str<strong>on</strong>g> path /usr/libexec/rtbuddyd.<br />

Arbitrary JavaScript code can <str<strong>on</strong>g>the</str<strong>on</strong>g>n be executed by creating a symlink named --early-boot<br />

that points to a file c<strong>on</strong>taining <str<strong>on</strong>g>the</str<strong>on</strong>g> code to be executed at boot time. <str<strong>on</strong>g>Pegasus</str<strong>on</strong>g> <str<strong>on</strong>g>the</str<strong>on</strong>g>n exploits a bad<br />

cast in <str<strong>on</strong>g>the</str<strong>on</strong>g> jsc binary to execute unsigned code and re-exploit <str<strong>on</strong>g>the</str<strong>on</strong>g> kernel.<br />

JavaScriptCore Memory Corrupti<strong>on</strong> Issue<br />

The issue exists within <str<strong>on</strong>g>the</str<strong>on</strong>g> setImpureGetterDelegate() JavaScript binding (which is backed by<br />

functi<strong>on</strong>SetImpureGetterDelegate).<br />

EncodedJSValue JSC_HOST_CALL functi<strong>on</strong>SetImpureGetterDelegate(ExecState* exec)<br />

{<br />

JSLockHolder lock(exec);<br />

JSValue base = exec->argument(0);<br />

if (!base.isObject())<br />

return JSValue::encode(jsUndefined());<br />

JSValue delegate = exec->argument(1);<br />

if (!delegate.isObject())<br />

return JSValue::encode(jsUndefined());<br />

ImpureGetter* impureGetter = jsCast(asObject(base.asCell()));<br />

impureGetter->setDelegate(exec->vm(), asObject(delegate.asCell()));<br />

Page 39


}<br />

return JSValue::encode(jsUndefined());<br />

This binding takes two arguments: <str<strong>on</strong>g>the</str<strong>on</strong>g> first is an ImpureGetter, and <str<strong>on</strong>g>the</str<strong>on</strong>g> sec<strong>on</strong>d is a generic<br />

JSObject that will be set as <str<strong>on</strong>g>the</str<strong>on</strong>g> ImpureGetter’s delegate. The issue results from <str<strong>on</strong>g>the</str<strong>on</strong>g> lack <str<strong>on</strong>g>of</str<strong>on</strong>g><br />

validati<strong>on</strong> that <str<strong>on</strong>g>the</str<strong>on</strong>g> JSObject passed as <str<strong>on</strong>g>the</str<strong>on</strong>g> first argument is in fact a well-formed ImpureGetter.<br />

When ano<str<strong>on</strong>g>the</str<strong>on</strong>g>r object type is passed as <str<strong>on</strong>g>the</str<strong>on</strong>g> first argument, <str<strong>on</strong>g>the</str<strong>on</strong>g> object pointer will be improperly<br />

downcast to an ImpureGetter pointer.<br />

Subsequently, when <str<strong>on</strong>g>the</str<strong>on</strong>g> m_delegate member is set via setDelegate(), a pointer to <str<strong>on</strong>g>the</str<strong>on</strong>g> JSObject<br />

passed as <str<strong>on</strong>g>the</str<strong>on</strong>g> sec<strong>on</strong>d argument will be written to <str<strong>on</strong>g>the</str<strong>on</strong>g> <str<strong>on</strong>g>of</str<strong>on</strong>g>fset that aligns with m_delegate (16<br />

bytes into <str<strong>on</strong>g>the</str<strong>on</strong>g> supplied object). This issue can be used to create a primitive that allows a pointer<br />

to an arbitrary JSObject to be written 16 bytes into any o<str<strong>on</strong>g>the</str<strong>on</strong>g>r JSObject.<br />

Exploitati<strong>on</strong><br />

<str<strong>on</strong>g>Pegasus</str<strong>on</strong>g> leverages this issue to achieve unsigned code executi<strong>on</strong> from within an <strong>iOS</strong><br />

applicati<strong>on</strong> c<strong>on</strong>text. In order to gain c<strong>on</strong>trol <str<strong>on</strong>g>of</str<strong>on</strong>g> executi<strong>on</strong> flow, <str<strong>on</strong>g>the</str<strong>on</strong>g> exploit uses a number <str<strong>on</strong>g>of</str<strong>on</strong>g><br />

DataView objects. DataViews are used because <str<strong>on</strong>g>the</str<strong>on</strong>g>y provide a trivial mechanism to read and<br />

write arbitrary <str<strong>on</strong>g>of</str<strong>on</strong>g>fsets into a vector. The DataView object also c<strong>on</strong>veniently has a pointer to <str<strong>on</strong>g>the</str<strong>on</strong>g><br />

backing buffer at its 16 byte <str<strong>on</strong>g>of</str<strong>on</strong>g>fset. Using <str<strong>on</strong>g>the</str<strong>on</strong>g>se corrupted DataView objects, <str<strong>on</strong>g>the</str<strong>on</strong>g> exploit sets up<br />

<str<strong>on</strong>g>the</str<strong>on</strong>g> tools needed to gain arbitrary native code executi<strong>on</strong> - namely, a read/write primitive and <str<strong>on</strong>g>the</str<strong>on</strong>g><br />

ability to leak <str<strong>on</strong>g>the</str<strong>on</strong>g> address <str<strong>on</strong>g>of</str<strong>on</strong>g> an arbitrary JavaScript object. Once this setup is complete, <str<strong>on</strong>g>the</str<strong>on</strong>g><br />

exploit can create an executable mapping c<strong>on</strong>taining <str<strong>on</strong>g>the</str<strong>on</strong>g> native code payload. The following<br />

secti<strong>on</strong>s detail <str<strong>on</strong>g>the</str<strong>on</strong>g> various stages <str<strong>on</strong>g>of</str<strong>on</strong>g> this process.<br />

Acquiring an arbitrary read/write primitive<br />

A read/write primitive for arbitrary <str<strong>on</strong>g>of</str<strong>on</strong>g>fsets into a DataView object can be obtained using <str<strong>on</strong>g>the</str<strong>on</strong>g><br />

following code snippet.<br />

var dummy_ab = new ArrayBuffer(0x20);<br />

var dataview_init_rw = new DataView(dummy_ab);<br />

...<br />

var dataview_rw = new DataView(dummy_ab);<br />

…<br />

setImpureGetterDelegate(dataview_init_rw, dataview_rw);<br />

First, two DataViews are created using a dummy ArrayBuffer as <str<strong>on</strong>g>the</str<strong>on</strong>g> backing vector for both.<br />

Next, <str<strong>on</strong>g>the</str<strong>on</strong>g> issue is exploited to corrupt <str<strong>on</strong>g>the</str<strong>on</strong>g> m_vector member <str<strong>on</strong>g>of</str<strong>on</strong>g> <str<strong>on</strong>g>the</str<strong>on</strong>g> dataview_init_rw object with<br />

a pointer to <str<strong>on</strong>g>the</str<strong>on</strong>g> dataview_rw object. Subsequent reads and writes into <str<strong>on</strong>g>the</str<strong>on</strong>g> dataview_init_rw<br />

DataView will allow arbitrary members <str<strong>on</strong>g>of</str<strong>on</strong>g> <str<strong>on</strong>g>the</str<strong>on</strong>g> dataview_rw to be leaked or overwritten. Next,<br />

c<strong>on</strong>trol over this object is used to gain a read/write primitive for <str<strong>on</strong>g>the</str<strong>on</strong>g> entirety <str<strong>on</strong>g>of</str<strong>on</strong>g> process memory.<br />

Page 40


var DATAVIEW_ARRAYBUFFER_OFFSET = 0x10;<br />

var DATAVIEW_BYTELENGTH_OFFSET = DATAVIEW_ARRAYBUFFER_OFFSET + 4;<br />

var DATAVIEW_MODE_OFFSET = DATAVIEW_BYTELENGTH_OFFSET + 4;<br />

var FAST_TYPED_ARRAY_MODE = 0;<br />

dataview_init_rw.setUint32(DATAVIEW_ARRAYBUFFER_OFFSET, 0, true);<br />

…<br />

dataview_init_rw.setUint32(DATAVIEW_BYTELENGTH_OFFSET, 0xFFFFFFFF, true);<br />

…<br />

dataview_init_rw.setUint8(DATAVIEW_MODE_OFFSET, FAST_TYPED_ARRAY_MODE, true);<br />

Three <str<strong>on</strong>g>of</str<strong>on</strong>g>fsets into <str<strong>on</strong>g>the</str<strong>on</strong>g> dataview_rw DataView are written. First, <str<strong>on</strong>g>the</str<strong>on</strong>g> pointer to <str<strong>on</strong>g>the</str<strong>on</strong>g> backing vector<br />

is pointed to <str<strong>on</strong>g>the</str<strong>on</strong>g> zero address. Then <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> DataView is set to 0xFFFFFFFF,<br />

effectively setting <str<strong>on</strong>g>the</str<strong>on</strong>g> DataView to map all <str<strong>on</strong>g>of</str<strong>on</strong>g> <str<strong>on</strong>g>the</str<strong>on</strong>g> virtual memory <str<strong>on</strong>g>of</str<strong>on</strong>g> <str<strong>on</strong>g>the</str<strong>on</strong>g> process. Last, <str<strong>on</strong>g>the</str<strong>on</strong>g> mode<br />

is set to a simple type (i.e., FastTypedArray), allowing trivial calculati<strong>on</strong>s <str<strong>on</strong>g>of</str<strong>on</strong>g> <str<strong>on</strong>g>the</str<strong>on</strong>g> <str<strong>on</strong>g>of</str<strong>on</strong>g>fset into a<br />

DataView given a virtual address. The dataview_rw DataView now provides an arbitrary<br />

read/write primitive via <str<strong>on</strong>g>the</str<strong>on</strong>g> getType and setType methods it exposes.<br />

Leaking an object address<br />

The last primitive needed is <str<strong>on</strong>g>the</str<strong>on</strong>g> ability to leak <str<strong>on</strong>g>the</str<strong>on</strong>g> virtual memory address <str<strong>on</strong>g>of</str<strong>on</strong>g> an arbitrary<br />

JavaScript object. The primitive is achieved using <str<strong>on</strong>g>the</str<strong>on</strong>g> same issue exploited above to leak <str<strong>on</strong>g>the</str<strong>on</strong>g><br />

address <str<strong>on</strong>g>of</str<strong>on</strong>g> a single object instead <str<strong>on</strong>g>of</str<strong>on</strong>g> exposing <str<strong>on</strong>g>the</str<strong>on</strong>g> entire memory space.<br />

var dummy_ab = new ArrayBuffer(0x20);<br />

...<br />

var dataview_leak_addr = new DataView(dummy_ab);<br />

var dataview_dv_leak = new DataView(dummy_ab);<br />

setImpureGetterDelegate(dataview_dv_leak, dataview_leak_addr);<br />

...<br />

setImpureGetterDelegate(dataview_leak_addr, object_to_leak);<br />

leaked_addr = dataview_dv_leak.getUint32(DATAVIEW_ARRAYBUFFER_OFFSET, true);<br />

Again, two DataViews are created using a dummy ArrayBuffer as <str<strong>on</strong>g>the</str<strong>on</strong>g> backing vector for both.<br />

Next, <str<strong>on</strong>g>the</str<strong>on</strong>g> issue is exploited to corrupt <str<strong>on</strong>g>the</str<strong>on</strong>g> m_vector member <str<strong>on</strong>g>of</str<strong>on</strong>g> <str<strong>on</strong>g>the</str<strong>on</strong>g> dataview_dv_leak object with<br />

a pointer to <str<strong>on</strong>g>the</str<strong>on</strong>g> dataview_leak_addr object. To leak <str<strong>on</strong>g>the</str<strong>on</strong>g> address <str<strong>on</strong>g>of</str<strong>on</strong>g> an arbitrary JavaScript<br />

object, <str<strong>on</strong>g>the</str<strong>on</strong>g> issue is triggered a sec<strong>on</strong>d time. This time, <str<strong>on</strong>g>the</str<strong>on</strong>g> m_vector <str<strong>on</strong>g>of</str<strong>on</strong>g> <str<strong>on</strong>g>the</str<strong>on</strong>g> dataview_leak_addr<br />

DataView is corrupted with <str<strong>on</strong>g>the</str<strong>on</strong>g> address <str<strong>on</strong>g>of</str<strong>on</strong>g> <str<strong>on</strong>g>the</str<strong>on</strong>g> object that is being leaked. Finally, <str<strong>on</strong>g>the</str<strong>on</strong>g> dword<br />

residing at <str<strong>on</strong>g>the</str<strong>on</strong>g> 16th byte <str<strong>on</strong>g>of</str<strong>on</strong>g>fset into <str<strong>on</strong>g>the</str<strong>on</strong>g> dataview_dv_leak DataView can be read to obtain <str<strong>on</strong>g>the</str<strong>on</strong>g><br />

address <str<strong>on</strong>g>of</str<strong>on</strong>g> <str<strong>on</strong>g>the</str<strong>on</strong>g> target object.<br />

Unsigned native code executi<strong>on</strong><br />

<str<strong>on</strong>g>Pegasus</str<strong>on</strong>g> uses <str<strong>on</strong>g>the</str<strong>on</strong>g> same mechanism to gain code executi<strong>on</strong> in this exploit as used in <str<strong>on</strong>g>the</str<strong>on</strong>g> stage 1<br />

Safari exploit. The exploit creates an executable mapping that will c<strong>on</strong>tain <str<strong>on</strong>g>the</str<strong>on</strong>g> shellcode to be<br />

executed. To accomplish this purpose, a JSFuncti<strong>on</strong> object is created (c<strong>on</strong>taining hundreds <str<strong>on</strong>g>of</str<strong>on</strong>g><br />

empty try/catch blocks that will later be overwritten). To help ensure that <str<strong>on</strong>g>the</str<strong>on</strong>g> JavaScript will be<br />

compiled into native code by <str<strong>on</strong>g>the</str<strong>on</strong>g> JIT, <str<strong>on</strong>g>the</str<strong>on</strong>g> functi<strong>on</strong> is subsequently called repeatedly. Given <str<strong>on</strong>g>the</str<strong>on</strong>g><br />

nature <str<strong>on</strong>g>of</str<strong>on</strong>g> <str<strong>on</strong>g>the</str<strong>on</strong>g> JavaScriptCore library, this JIT-compiled native code will reside in an area <str<strong>on</strong>g>of</str<strong>on</strong>g><br />

memory that is mapped as read/write/execute.<br />

Page 41


var body = ''<br />

for (var k = 0; k < 0x600; k++) {<br />

body += 'try {} catch(e) {};';<br />

}<br />

var to_overwrite = new Functi<strong>on</strong>('a', body);<br />

for (var i = 0; i < 0x10000; i++) {<br />

to_overwrite();<br />

}<br />

The address <str<strong>on</strong>g>of</str<strong>on</strong>g> this JSFuncti<strong>on</strong> object can <str<strong>on</strong>g>the</str<strong>on</strong>g>n be leaked and <str<strong>on</strong>g>the</str<strong>on</strong>g> various members can be<br />

read to acquire <str<strong>on</strong>g>the</str<strong>on</strong>g> address <str<strong>on</strong>g>of</str<strong>on</strong>g> <str<strong>on</strong>g>the</str<strong>on</strong>g> RWX mapping. The JITed try/catch blocks are <str<strong>on</strong>g>the</str<strong>on</strong>g>n<br />

overwritten with shellcode, and <str<strong>on</strong>g>the</str<strong>on</strong>g> to_overwrite() functi<strong>on</strong> can simply be called to achieve<br />

arbitrary code executi<strong>on</strong>.<br />

1-888-988-5795 | lookout.com<br />

Page 42<br />

© 2016 Lookout, Inc. All rights reserved. Lookout, <str<strong>on</strong>g>the</str<strong>on</strong>g> Shield Logo, and Everything is OK are registered trademarks <str<strong>on</strong>g>of</str<strong>on</strong>g> Lookout, Inc. All o<str<strong>on</strong>g>the</str<strong>on</strong>g>r brand and product names<br />

are trademarks or registered trademarks <str<strong>on</strong>g>of</str<strong>on</strong>g> <str<strong>on</strong>g>the</str<strong>on</strong>g>ir respective holders. 20161103-Lookout-USv1

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

Saved successfully!

Ooh no, something went wrong!