Вы находитесь на странице: 1из 44

TechnicalAnalysisofthePegasus

ExploitsoniOS

Section 1: Pegasus Exploitation of Safari (CVE­2016­4657)

2

Background

3

Exploitation

7

Setting up and triggering the vulnerability

7

Acquiring an arbitrary read/write primitive

9

Leaking an object address

9

Native code execution

9

Evading detection

10

Section 2: Exploitation of KASLR by Pegasus

11

Differences Between 32 and 64­Bit Binaries

12

API Loading

12

Environment Setup and Platform Determination

13

Defeating KASLR

16

Establishing Read/Write/Execute Primitives on Previously Rooted Devices (32­Bit)

19

Thread Manipulation

21

Establishing Communication Channel (32­Bit)

21

Payload Construction and Kernel Insertion (32­Bit)

22

Payload Construction and Kernel Insertion (64­Bit)

25

Establishing Kernel Read/Write Primitives (32­Bit)

25

Establishing Kernel Read/Write Primitives (64­Bit)

30

Establishing a Kernel Execute Primitive (32­Bit)

31

Patching the Kernel to Allow Kernel Port Access

31

Section 3: Privilege Escalation and Activating the Jailbreak Binary

33

System Modification for Privilege Escalation

34

Disabling Code Signing

34

Remounting the Drive

35

Cleanup

36

Next Stage Installation

36

Existing Jailbreak Detection

37

Section 4: Pegasus Persistence Mechanism

39

Pegasus Persistence Mechanism

40

JavaScriptCore Memory Corruption Issue

40

Exploitation

41

Acquiring an arbitrary read/write primitive

41

Leaking an object address

42

Unsigned native code execution

42

Page1

Section1:PegasusExploitationofSafari

(CVE­2016­4657)

TheFirstStageofInfection

ThissectionreportsonfirststageofthePegasusexploitofthe“Trident”zero­day vulnerabilitiesoniOS,discoveredbyresearchersatLookoutandCitizenLab.The firststageoftheattackistriggeredwhentheuserclicksaspear­phishinglinkthat openstheSafaribrowser.ThisenablestheexploitofavulnerabilityinWebKit’s

JavaScriptCorelibrary(CVE­2016­4657).

Page2

Analysis of the Pegasus Safari Exploit

ThefirststageofPegasusexploitsavulnerabilityinWebKit’sJavaScriptCorelibrary

(CVE­2016­4657).TheexploitusestheSafariwebbrowsertorunaJavaScriptpayloadthat

exploitstheinitialvulnerabilitytogainarbitrarycodeexecutioninthecontextoftheSafari

WebContentprocess.

Background

ThevulnerabilityexistswithintheslowAppend()methodofMarkedArgumentBufferandcanbe

exploitedviatheusageofaMarkedArgumentBufferinthestaticdefineProperties()method.The

defineProperties()methodacceptsasinputanobjectwhoseownenumerableproperties

constitutedescriptorsforthepropertiestobedefinedormodifiedonanothertargetobject.The

algorithmusedtoassociateeachofthesepropertieswiththetargetobjectdoestwoiterationsof

theprovidedlistofproperties.Inthefirstpass,eachofthepropertydescriptorsischeckedfor

properformattingandaPropertyDescriptorobjectiscreatedthatreferencestheunderlying

value.

size_t numProperties = propertyNames.size(); Vector<PropertyDescriptor> descriptors; MarkedArgumentBuffer markBuffer; for (size_t i = 0; i < numProperties; i++) { JSValue prop = properties­>get(exec, propertyNames[i]); if (exec­>hadException()) return jsNull(); PropertyDescriptor descriptor; if (!toPropertyDescriptor(exec, prop, descriptor)) return jsNull(); descriptors.append(descriptor);

Thesecondpassisperformedaftereachpropertyhasbeenvalidated.Thispassassociates

eachoftheuser­suppliedpropertieswiththetargetobject,usingthetypespecific

defineOwnProperty()method.

for (size_t i = 0; i < numProperties; i++) { Identifier propertyName = propertyNames[i]; if (exec­>propertyNames().isPrivateName(propertyName)) continue;

object­>methodTable(exec­>vm())­>defineOwnProperty(object, exec, propertyName, descriptors[i], true);

Thismethodmayresultinuser­definedJavaScriptmethods(thatareassociatedwiththe

propertybeingdefined)beingcalled.Withinanyoftheseuser­definedmethods,itispossible

thatagarbagecollectioncyclemaybetriggered,resultinginanyunmarkedheapbacked

objectsbeingfree()ed.Therefore,itisimportantthateachofthetemporaryreferencestothese

Page3

objects,storedwithintheindividualPropertyDescriptorsinthedescriptorsvector,beindividually

markedtoensurethatthesereferencestonotbecomestale.Toachievethis,a

MarkedArgumentBufferisused.Thisclassisintendedtotemporarilypreventthevalues

appendedtoitfrombeinggarbagecollectedduringtheperiodforwhichitisinscope.

TounderstandhowMarkedArgumentBuffersworkwemustfirstunderstandsomebasicsof

JavaScriptCoregarbagecollection.Thegarbagecollectorisresponsiblefordeallocatingobjects

thatarenolongerreferencedandrunsatrandomintervalsthatincreaseinfrequencyasmore

memoryisusedbytheWebContentprocess.Todeterminewhetheranobjectisreferenced,the

garbagecollectorwalksthestackandlooksforreferencestotheobject.Referencestoanobject

mayalsoexistontheapplicationheap,however,soanalternatemechanism(whichwillbe

explainedindetailbelow)mustbeusedforthesecases.

AMarkedArgumentBufferinitiallyattemptstomaintainaninlinestackbuffercontainingeach

value.Whenthestackiswalkedwithingarbagecollection,eachvaluewillbenotedandthe

underlyingobjectswillavoiddeallocation.

class MarkedArgumentBuffer {

private:

static const size_t inlineCapacity = 8;

public:

MarkedArgumentBuffer() : m_size(0)

, m_capacity(inlineCapacity)

, m_buffer(m_inlineBuffer)

, m_markSet(0)

{

}

void append(JSValue v)

{

 

if (m_size >= m_capacity) return slowAppend(v);

slotFor(m_size) = JSValue::encode(v); ++m_size;

}

private:

int m_size; int m_capacity; EncodedJSValue m_inlineBuffer[inlineCapacity]; EncodedJSValue* m_buffer; ListSet* m_markSet;

Page4

Thesizeofthisinlinestackbufferislimitedtoeightvalues.Whentheninthvalueisaddedtoa

MarkedArgumentBuffer,theunderlyingbufferismovedtotheheapandthecapacityis

expanded.

void MarkedArgumentBuffer::slowAppend(JSValue v)

{

int newCapacity = m_capacity * 4; EncodedJSValue* newBuffer = new EncodedJSValue[newCapacity]; for (int i = 0; i < m_capacity; ++i) newBuffer[i] = m_buffer[i];

if (EncodedJSValue* base = mallocBase()) delete [] base;

m_buffer = newBuffer; m_capacity = newCapacity;

Oncetheunderlyingbufferhasmovedtotheheap,valuesarenotautomaticallyprotectedfrom

garbagecollection.Toensurethattheseobjectsarenotdeallocated,thegarbagecollector

performsaMarkingArgumentBuffersphaseinwhicheachvaluecontainedwithina

MarkedArgumentBufferthathasbeenaddedtotheHeap’sm_markListSetismarked(markinga

cellensuresthatitwillnotbedeallocatedinaparticulargarbagecollectioncycle).Forthis

methodofmarkingtowork,theMarkedArgumentBuffermustbeaddedtothemarkListSetatthe

sametimethattheMarkedArgumentBuffer’sunderlyingvaluesaremovedtotheheap.

// As long as our size stays within our Vector's inline // capacity, all our values are allocated on the stack, and // therefore don't need explicit marking. Once our size exceeds // our Vector's inline capacity, though, our values move to the // heap, where they do need explicit marking. for (int i = 0; i < m_size; ++i) { Heap* heap = Heap::heap(JSValue::decode(slotFor(i))); if (!heap) continue;

m_markSet = &heap­>markListSet(); m_markSet­>add(this); break;

}

Theabovecodeattemptstoacquiretheheapcontextforavalueandaddthe

MarkedArgumentBuffertotheHeap’smarkListSet.However,thisisonlyattemptedonceforthe

ninthvalueaddedtotheMarkedArgumentBuffer.

inline Heap* Heap::heap(const JSValue v)

{

if (!v.isCell()) return 0; return heap(v.asCell());

}

Page5

AJSValuecontainsatagwhichdescribesthetypeofthevaluethatitencodes.Inthecaseof

complexobjectsthistagwillbeCellTagandtheJSValuewillencodeapointertoanunderlying

itemontheHeap.Alternatively,forsimpletypeswheretheentireunderlyingvalueofthe

variablecanbeencodeddirectlyintoaJSValue(ex.Integers,Booleans,null,andundefined),

storingthevalueontheheapwouldberedundantandadifferentidentifyingtagwillbeused.

ThefunctionJSValue::isCell()isusedtodeterminewhetheraJSValueencodesapointertoa

cellontheHeap.Becausesimpletypesdonotpointtotheheap,attemptingtoacquiretheHeap

(viaacalltoHeap::heap())forthesetypeshasnomeaningandwillthereforereturnNULL.

inline bool JSValue::isCell() const

{

return !(u.asInt64 & TagMask);

}

Asaresult,iftheninthvalueaddedtoaMarkedArgumentBufferisnotaheapbackedvalue,

attemptingtoacquiretheHeapcontextwillreturnNULLandtheMarkedArgumentBufferwill

neverbeaddedtotheHeap’smarkListSet.ThismeansthattheMarkedArgumentBufferwillno

longerserveitspurpose(toprotecttheitemsthatitcontainsfromdeallocation)foranyitemafter

theninth.Anyreferencetoaheapbackedproperty(aftertheninth)containedwithinthe

descriptorsvectorhasthepotentialtogostale.Inreality,atleastoneotherreferencetothese

valuesstillexists(theJavaScriptvariablethatwaspassedtodefineProperties()).Inorderforthe

referenceswithinthedescriptorsvectortogostale,theseremainingreferencestotheJSValue

mustalsoberemovedbeforegarbagecollectionoccurs.

mustalsoberemovedbeforegarbagecollectionoccurs.

ThecalltodefineOwnProperty()(withinthesecondloopofdefineProperties())mayresultin

callinguser­controlledmethodsdefinedonpropertyvalues.Asaresult,thelastmarked

Page6

referencestoapropertyvaluecouldberemovedwithinthisuser­definedJavaScriptcode.If

garbagecollectioncanbetriggeredbetweentheremovalofallremainingreferencestoa

propertyvalueandthe(nowstale)valuefromthedescriptorsvectorbeingdefinedonthetarget

object,areferencetofree()edmemorywillbedefinedasapropertyonthetargetobject.

Exploitation

ThePegasusexploittriggersthisvulnerabilitybypassingaspecificallycraftedsequenceof

propertiestothedefineProperties()method.Whentheseindividualpropertiesaresubsequently

insertedintoaMarkedArgumentBufferthevulnerabilityistriggeredsuchthataJSArrayobject

willbeimproperlydeallocatedifgarbagecollectioncanbetriggeredatacriticalpointintime.

Becausegarbagecollectioncannotbetriggereddeterministically,theexploitmakesrepeated

attemptstotriggertheimproperdeallocationandsubsequentreallocation(foratotaloften

attempts),testingeachtimewhetherastalereferencehasbeensuccessfullyacquired.

Assuminggarbagecollectionhasbeentriggeredatthecorrecttime,anotherobjectisallocated

overthetopofthenowstaleJSArray.Theexploitthensetsupthetoolsneededtogainarbitrary

nativecodeexecution,namelyaread/writeprimitiveandtheabilitytoleaktheaddressofan

arbitraryJavaScriptobject.Oncethisiscompletetheexploitcancreateanexecutablemapping

containingthenativecodepayload.Thefollowingsectionsdetailthevariousstagesofthis

process.

Settingupandtriggeringthevulnerability

Inordertoachievearbitrarycodeexecution,theexploittriggersthevulnerablecodepathusing

aJSArrayobject.Thefollowingpseudocodeisusedtotriggerthevulnerability.

var arr = new Array(2047); var not_number = {}; not_number.toString = function() { arr = null; props["stale"]["value"] = null; … // Trigger garbage collection and reallocation over stale object return 10;

}; var props = { p0 : { value : 0 }, p1 : { value : 1 }, p2 : { value : 2 }, p3 : { value : 3 }, p4 : { value : 4 }, p5 : { value : 5 }, p6 : { value : 6 }, p7 : { value : 7 }, p8 : { value : 8 }, length : { value : not_number }, stale : { value : arr }, after : { value : 666 }

Page7

var target = []; Object.defineProperties(target, props);

Thespecifiedpropsobjecthasbeenspecificallycraftedtotriggerthevulnerabilityin

slowAppend().WhentheninthpropertyisaddedtotheMarkedArgumentBuffer(p8),

slowAppend()willfailtoacquireaheapcontext(becausethevalueissimpletype,aninteger,

andnotbackedbyanitemontheheap).SubsequentHeap­backedvalues(not_numberand

arr)willnotbeexplicitlyprotectedfromdeallocationbytheMarkedArgumentBufferduring

garbagecollection.

WhendefineOwnProperty()iscalledforthelengthproperty,itwillattempttoconvertthevalue

(not_number)toanumber.Aspartofthiscodepath,thetoString()methodwillbecalled,

allowingthelasttworeferencestothearrJSArraytoberemoved.Onceremoved,thisJSArray

isnolongermarked,andthenextgarbagecollectionpasswilldeallocatetheobject.Pegasus

createsmemorypressure(allocatesalargeamountofmemory)withinthetoString()methodin

anattempttoforcegarbagecollectiontorun(anddeallocatethearrobject).

var attempts = new Array(4250000); var pressure = new Array(100);

not_number.toString = function() {

for (var i = 0; i < pressure.length; i++) { pressure[i] = new Uint32Array(262144);

}

var buffer = new ArrayBuffer(80); var uintArray = new Uint32Array(buffer); uintArray[0] = 0xAABBCCDD; for (i = 0; i < attempts.length; i++) { attempts[i] = new Uint32Array(buffer);

}

}

Eachofthe4.25millionUint32Arraysallocatedfortheattemptsarrayusethesamebacking

ArrayBuffer.TheseobjectsareusedtoattempttoreallocateaseriesofUint32Arraysintothe

samememoryreferencedbytheJSArrayobject(arr).

Oncecomplete,theexploitcheckstoseewhethergarbagecollectionwassuccessfully

triggered.

var before_len = arr.length; Object.defineProperties(target, props); stale = target.stale; var after_len = stale.length; if (before_len == after_len) { throw new RecoverableException(8);

}

Page8

IfthelengthoftheJSArrayremainsthesameitmeansthateithergarbagecollectionwasnot

triggeredorthatnoneoftheallocatedUint32Arrayswereallocatedintothesameaddressasthe

staleobject.Inthesecases,theexploithasfailedandtheexploitisretried.

Acquiringanarbitraryread/writeprimitive

Assumingtheexploithassucceededtothispoint,therearenowtwoobjectsofdifferenttypes thatarerepresentedbythesamememory.Thefirstisthe(nowstale)JSArray,andthesecond

isoneofthemanyUint32Arraysthatwereallocated(infact,theunderlyingtemplatedtypeis

JSGenericTypedArrayView).Byreadingfromandwritingtooffsetsintothestaleobject,member variablesoftheJSGenericTypedArrayViewcanbereadorcorrupted.Specifically,theexploit writestoanoffsetintothestaleJSArraythatoverlapswiththelengthofthe

JSGenericTypedArrayView,effectivelysettingthelengthoftheUint32Arrayto0xFFFFFFFF.

Corruptingthisvaluewillallowthearraytobetreatedasaviewoftheentirevirtualaddress

spaceoftheWebContentprocess(anarbitraryread/writeprimitive).

Theexploitstillmustdeterminewhichofthe4.25millionUint32Arraysthatwereallocatedaligns

withthestaleobject.Thiscanbedeterminedbyiteratingthrougheachofthearraysand

checkingwhetherthelengthhaschangedto0xFFFFFFFF.Allotherarrayswillstillhavethe

originalbackingArrayBuffer(oralengthof80/4).

for (x = attempts.length ­ 1; x >= 1; x­­) { if (attempts[x].length != 80 / 4) { if (attempts[x].length == 0xFFFFFFFF) { memory_view = attempts[x];

break;

Leakinganobjectaddress

Thefinalcomponentneededtocompletetheexploitistheabilitytoleaktheaddressofan arbitraryJavaScriptobject.ThePegasusexploitaccomplishesthisusingthesamemechanism

thatwasusedtocorruptthelengthoftheUint32Arrayusedfortheread/writeprimitive.By

writingtoanoffsetintothestaleobject,thebufferofaUint32Arrayiscorruptedtopointtoa

user­controlledJSArray.BysettingthefirstelementofthatJSArraytotheJavaScriptobjectto

beleaked(bycorruptingthepointertotheunderlyingstorageoftheUint32Array),theobject’s

addresscanbereadbackoutoftheUint32Array.

Nativecodeexecution

AllthatislefttodoforthefirststageofthePegasusexploitistocreateanexecutablemapping

thatwillcontaintheshellcodetobeexecuted.Toaccomplishthispurpose,aJSFunctionobject

iscreated(containinghundredsofemptytry/catchblocksthatwilllaterbeoverwritten).Tohelp

ensurethattheJavaScriptwillbecompiledintonativecodebytheJIT,thefunctionis

Page9

subsequentlycalledrepeatedly.ThisbehaviorensuresthattheJITcompiled(JITed)versionof

thefunction(whichwilllaterbeoverwritten)willbemarkedashighprioritycodethatislikelyto

becalledregularlyandshouldthereforenotbereleased.Becauseofthewaythatthe

JavaScriptCoreenginehandlesJITedcode,thiswillresideinanareaofmemorythatismapped

asread/write/execute.

var body = '' for (var k = 0; k < 0x600; k++) { body += 'try {} catch(e) {};';

}

var to_overwrite = new Function('a', body); for (var i = 0; i < 0x10000; i++) { to_overwrite();

}

TheaddressofthisJSFunctionobjectcanthenbeleakedandthevariousmemberscanberead

toacquiretheaddressoftheRWXmapping.TheJITedversionofthetry/catchblocksarethen

overwrittenwithshellcode,andtheto_overwrite()functioncansimplybecalledtoachieve

arbitrarycodeexecution.

Evadingdetection

Whenexploitationfails,thePegasusexploitcontainsabailoutcodepath,presumablytoensure

thatcrashdumpsdonotexposetheexploitablevulnerability.Thisbailoutcodetriggersacrash

onacleanNULLdereference.Mostlikely,ananalystanalyzingsuchacrashdumpwould

quicklyidentifythebugasanon­exploitableNULLpointerdereferenceandnotsuspectanything

moresinister.Thefollowingcodeisusedtotriggerthis“clean”crash.

window

x = new String("a");

x

x.Audio;

proto

proto

proto

proto

= null;

proto

= window;

Page10

Section2:ExploitationofKASLRbyPegasus

StageTwoofInfection:KernelLocationDisclosure

Once the attack is launched in the first stage, the second stage exploits a kernel information leak (CVE­2016­4655). This prepares the device for the

kernelmemorycorruption(CVE­2016­4656)thatultimatelyleadstojailbreak.

Page11

AnalysisofPegasusKASLRExploit

Thesecondstage,Stage2,isresponsibleforescalatingprivilegesonthevictim’siPhoneand

establishinganenvironmentwherejailbreakingthevictim’sdeviceispossible.TheStage2

binaryisusedintwodistinctcontextswithinPegasus.Bydefault,Stage2constitutesa

completeiOSkernelexploit.Alternatively,theStage2binaryattemptstodetectiOSdevicesthat

havealreadybeenjailbrokenand,incaseswhereanexistingjailbreakisdetected(andhas

installedaknownbackdoor),usesthepre­existingbackdoormechanismstoinstallPegasus

specifickernelpatches.

Inordertoperformthesetasks,Stage2mustfirstdeterminethelocationofthekernelin

memory,escalateitsownprivileges,disablesafeguards,andtheninstallthenecessarytoolsfor

jailbreakingadevice.InordertoaccommodatemultipleiPhoneversions,Stage2comesintwo

flavors,32­bitand64­bit.Together,thetwoversionsoftheStage2binarytargetatotalof199

iPhonecombinations.

TheStage2variantssharealotofdesignsimilarities,butdeviateenoughintheirapproachthat

itisbesttolookateachvariantinrelativeisolation.Thesubsectionsthatfollowwillwalkthrough

thestepsinvolvedineachoftheStage2variantswhilepointingoutareasofsimilaritybetween

thevariantswhentheyarise.

DifferencesBetween32and64­BitBinaries

The32­bitStage2binary(orsimply“32Stage2”)operatesontheolderiPhonemodels(iPhone

4SthroughiPhone5c)andtargetsiOS9.0throughiOS9.3.3.The64­bitStage2binary(or

simply“64Stage2”)operatesontheneweriPhonemodels(iPhone5Sandlater)andtargetsiOS

9.0throughiOS9.3.3.Bothbinariesperformthesamegeneralstepsandexploitthesame

underlyingvulnerabilities.However,theexploitationofthesevulnerabilitiesvariesbetween

versions.Inareaswherethemechanismsdiffersubstantiallythedifferenceswillbespecifically

notedordiscussedseparately.

APILoading

Stage2requiresanumberofAPIfunctionstobepresentinordertosucceed.Inordertoensure

thefunctionsareavailable,Stage2dynamicallyloadsthenecessaryAPIfunctionaddressesvia

dlsym calls.WhiledynamicallyresolvingAPIfunctionaddressesisbynomeansanovel

techniqueformalware,whatisinterestingaboutStage2’sAPIloadingisthefactthatthe

authorsofthebinaryreloadmanyoftheAPIfunctionsmultipletimes.Inthemain function alone,alargenumberof APIfunctionaddressesareloadedwithonlyasmallsubsetofthose

functionseverfindingthemselvesusedduringthecourseofStage2’sexecution(forexample,

theaddressofsocket isloadedintomemorybutisnevercalled).Afterloadingtheinitialsetof

Page12

APIfunctions,32stage2callsasubroutine(identifiedinthisreportasinitialize)thatinturn

callsseveralothersubroutines,eachofwhichisresponsibleforloadingadditionalAPIfunctions

inadditiontoperformingvariousstartuptasks.

ThegroupingoftheAPIfunctionsbeingloaded(intermsofwhichAPIfunctionsareloadingby

whichStage2functions)andtheinclusionofmultipleAPIfunctionsbeingloadedmultipletimes

suggeststhattheAPIloadingisspecifictoindividualcomponentsoroperationsoftheStage2

binary.Forinstance,asdiscussedlater,apairoffunctionsareresponsiblefordecompressing thejailbreakfiles,changingtheirpermissionsviachmod,andpositioningthefilesinthecorrect locationonthevictim’siPhone.TheAPIfunctionsresponsiblefortheseoperationsareall loadedbyaself­containedfunction.TheloadingfunctiononlyloadsthoseAPIfunctionsthatare necessaryforthedescribedoperations,andtheAPIsarenotsharedwithanyotherpartofthe

Stage2system.

TheanalysisofStage2wasalsomadesomewhateasiergiventheheavyuseofdebuglogging

throughoutthebinary.Callstotheloggingsub­systemgenerallyreferencetheoriginalfile

namesusedbytheexploitdevelopers.Thepresenceofthisdebuggingcodedisclosesthe

presenceofatleastthefollowingindividualmodules(orsubsystems):

1. fs.c­LoadsAPIfunctionsrelatedtofileandfilesystemmanagementsuchasftw, open,read,rename,andmount

2. kaslr.c­LoadsAPIfunctionssuchasIORegistryEntryGetChildIterator, IORegistryEntryGetProperty,andIOServiceGetMatchingService that relatetofindingtheaddressofthekernelusingavulnerabilityinthe io_service_open_extended function

3. bh.c­LoadsAPIfunctionsthatrelatetothedecompressionofnextstagepayloadsand theirproperplacementonthevictim’siPhonebyusingfunctionssuchas

BZ2_bzDecompress,chmod,andmalloc

4. safari.c­LoadsAPIfunctionssuchassync,exit,andstrcpy thatareusedfor clearingSafaricachefilesandterminatingtheSafariprocess.Thiscleanupisrequired forthecasewherewesucceedandexitcleanly,astheSafaricrashcleanup(described

intheStage1writeup)willneveroccur.

TheseartifactssuggestthattheStage2binaryisbasedonamodulardesignphilosophyor,at

theveryleast,ismadeupofvariouslibrarysourcecodefilesthatareultimatelytiedtogetherto

formtheStage2binary.ThevariouscomponentsthatmakeuptheStage2exploitwerelikely

designedtobereusedacrossmultipleiOSexploitchains.

EnvironmentSetupandPlatformDetermination

Afterinitializecompletes,Stage2callsafunctionthatspecifiesaglobalcallbackfunction

thatisusedwheneverStage2terminatesduetoanerror.Basedonthefilenamesuppliedinthe

writeLog,mostlikelythefunctionisanassert­stylecallback.

Page13

Inordertodeterminetheplatform(hardware)ofthevictim’sdevice,acallismadeto sysctlbyname forthehw.machineobject.Anothercalltosysctlbyname ismadeforthe

kern.osversioninformation.Fromthesetwocalls,Stage2isabletoaccuratelydetermine

theplatformandiOSkernelversions.Thisinformationisthenusedtofindadatastructurethat

definesthevariousmemoryoffsetsthatStage2willuseforitsexploitationoperations.IfStage2

isunabletofindtheappropriatedatastructurefortheplatform/iOScombination,theprocess

executestheassertcallbackandexits.

Stage2usesalockfileduringitsexecution.Aspartofthesetupoftheworkingenvironment,

Stage2establishesthefilenameandpathglobalvariablesforthelockfileas

$HOME/tmp/lock (Note:$HOMEisanapplicationspecificvariable).

The32bitversionoftheStage2binaryhas100differentcombinationsofplatformandiOSthat

itsupports,asidentifiedinthetablebelow.

iOSVersion

iPhone4S

iPhone5

iPhone5

iPhone5c

iPhone5c

(“iPhone4,1”)

(“iPhone5,1”)

(“iPhone5,2”)

(“iPhone5,3”)

(“iPhone5,4”)

9.0

✅ ✅

 

9.0.1

✅ ✅

 

9.0.2

✅ ✅

 

9.1

✅ ✅

 

9.2

✅ ✅

 

9.2.1

✅ ✅

 

9.3(13E233)

✅ ✅

 

9.3(13E237)

✅ ✅

 

9.3Beta

   

 

9.3Beta3

     

 

9.3Beta6

     

 

9.3Beta7

     

 

9.3.1

✅ ✅

 

9.3.2Beta

✅ ✅

 

Page14

9.3.2Beta2

✅ ✅

✅ ✅

9.3.2Beta3

✅ ✅

✅ ✅

9.3.2Beta4

✅ ✅

✅ ✅

9.3.2

✅ ✅

✅ ✅

9.3.3Beta

✅ ✅

✅ ✅

9.3.3Beta2

✅ ✅

✅ ✅

9.3.3Beta3

✅ ✅

✅ ✅

9.3.3Beta4

✅ ✅

✅ ✅

9.3.3

✅ ✅

✅ ✅

Similarly,the64­bitversionoftheStage2binarysupports99differentiOSandiPhone

combinations.ThesupportediPhoneandiOSversionsof64Stage2areidentifiedinthetable

below.

iOS

iPhone

iPhone

iPhone6

iPhone6

iPhone

iPhone

iPhone

Version

5s

5s

Plus

(iPhone7

6s

6sPlus

SE

(iPhone6

(iPhone6

(iPhone7

,2)

(iPhone8

(iPhone8

(iPhone

,1)

,2)

,1)

,1)

,2)

8,4)

9.2.1

✅ ✅

 

 

(13D15)

iOS9.2.1

   

 

(13D20)

9.3

✅ ✅

 

   

(13E233)

9.3

       

 

(13E234)

9.3

✅ ✅

           

(13E237)

9.3Beta

   

       

4

9.3Beta

   

       

6

Page15

9.3Beta

   

     

7

9.3.1

✅ ✅

 

✅ ✅

9.3.2

✅ ✅

 

✅ ✅

Beta

9.3.2

✅ ✅

 

✅ ✅

Beta2

9.3.2

✅ ✅

 

✅ ✅

Beta3

9.3.2

✅ ✅

 

✅ ✅

Beta4

9.3.2

✅ ✅

 

✅ ✅

9.3.3

✅ ✅

 

✅ ✅

Beta

9.3.3

✅ ✅

 

✅ ✅

Beta2

9.3.3

✅ ✅

 

✅ ✅

Beta3

9.3.3

✅ ✅

 

✅ ✅

Beta4

9.3.3

✅ ✅

 

✅ ✅

DefeatingKASLR

ThemajorityofStage2’sfunctionalitydealswithmanipulatingthekernelinordertodisable

securityfeaturesonthevictim’sdevice.Inordertomanipulatethekernel,Stage2mustfirst

locatethekernel.Undernormalcircumstancesthekernelwillbemappedintoarandomized locationduetothekerneladdressspacelayoutrandomization(KASLR)mechanismthatiOS employs.KASLRisdesignedtopreventprocessesfromlocatingthekernelinmemoryby mappingthekerneltoapseudorandomlocationinmemoryeachtimethedeviceispoweredon

bytheuser.Inordertolocatethekernel,Stage2mustfindawaytoexposeamemoryaddress

withinkernelmemoryspacetoaprocessinuserspace.Stage2usesthevulnerability

1

CVE­2016­4655 inorderexposeamemoryaddressinkernelspace.

Page16

Inordertofindthekernel,Stage2beginsbyopeningaporttotheIOKitsubsystem.Failingthis,

Stage2callstheassertcallbackandexits.AcalltoIOServiceMatching fortheservice

namedAppleKeyStoreismadebyStage2,andtheresultsofthecallaregivento

IOServiceGetMatchingService inordertoobtainaio_service_tobjectcontainingthe desiredregisteredIOKitIOService(inthiscase,AppleKeyStore).WiththeIOServicehandle,

Stage2callsio_service_open_extendedandpassesaspeciallycraftedpropertiesfieldto

theservice.Thepropertiesfieldisa(serialized)binaryrepresentationofXMLdatathat

io_service_open_extendedultimatelypassestotheOSUnserializeBinaryfunction

locatedinthekernel.WithintheOSUnserializeBinaryfunctionisaswitchstatementthat

handlesthevarioustypesofdatastructuresfoundwithinabinaryXMLdatastructure.Thedata

typeforkOSSerializeNumberblindlyacceptsthelengthofthedatawithoutperformingany

typeofreasonableboundchecking,whichultimatelygivesthecallertheabilitytorequestmore

memorythanshouldbeallowed.Thisconditionoccursduetothefollowingcodefragments:

2

len = (key & kOSSerializeDataMask);

case kOSSerializeNumber:

bufferPos += sizeof(long long); if (bufferPos > bufferSize) break; value = next[1]; value <<= 32; value |= next[0]; o = OSNumber::withNumber(value, len); next += 2; break;

TheerroristhatthelenvariablepassedtoOSNumber::withNumber isnotvalidatedbefore beingpassedto OSNumber::withNumber. Ultimately,thefunction OSNumber::initis called,whichblindlytruststhisuser­controlledvalue.

bool OSNumber::init(unsigned long long inValue, unsigned int newNumberOfBits)

{

 

if (!super::init()) return false;

size = newNumberOfBits; value = (inValue & sizeMask);

return true;

}

ThisvulnerabilityallowsStage2tocontrolthesizeofOSNumber.The

io_service_open_extendedfunctionpreparestheenvironmentfortheuseof

OSUnserializedBinary,asecondfunctionthatisrequiredtoperformtheexploitation.

However,beforelookingattheexploitation,itisworthwhiletolookatthemalicious

propertiesfieldpassedtoio_service_open_extended:

Page17

unsigned char properties[] = { // kOSSerializeBinarySignature 0xD3, 0x00, 0x00, 0x00,

// kOSSerializeEndCollecton | kOSSerializeDictionary | 2 0x02, 0x00, 0x00, 0x81, // KEY 1 specified as 30 bytes long (0x1E) // kOSSerializeSymbol | 0x1E 0x1E, 0x00, 0x00, 0x08,

"HIDKeyboardModifierMappingSrc", 0x00, // padding (30 + 3 / 4 = 8 DWORDS) 0x00, 0x00, // VALUE 1

// kOSSerializeNumber specified as 0x800 bits (256 bytes) 0x00, 0x08, 0x00, 0x04, // value of OSNumber (4) 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // KEY 2 specified as 30 bytes long (0x1E) // kOSSerializeSymbol | 0x1E 0x1E, 0x00, 0x00, 0x08,

"HIDKeyboardModifierMappingDst", 0x00,

// padding (30 + 3 / 4 = 8 DWORDS) 0x00, 0x00, // VALUE 2 // kOSSerializeEndCollecton | kOSSerializeNumber | 32 0x20, 0x00, 0x00, 0x84, // value of OSNumber (0x193) 0x93, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00

// (30 bytes)

// (30 chars)

Stage2callsIORegistryEntryGetPropertyinordertofindtheentryfor

HIDKeyboardModifierMappingSrc,whichresultsinthepropertiesarraycreatingan

OSNumberlargerthanthemaximum64­bits(8bytes).Stage2usesthefollowingcodefragment

tocallis_io_registry_entry_get_property_bytes,whichwillreadpasttheendofa

kernelstackbufferandcopythedatatoakernelheapbuffer.The

IORegistryEntryGetPropertyfunctionthenreturnsthisheapbuffertouserspace.

Pointersfromthisstackoverreadwillthereforebeleakedtousermodeandcanbeusedto

calculatethebaseaddressfortheiOSkernel:

do

{

} while ( IORegistryEntryGetProperty_0(v13, "HIDKeyboardModifierMappingSrc", dataBuffer, &size) ); writeLog(7, "%.2s%5.5d\n", "kaslr.c", 127); if ( size > 8 )

{

writeLog(7, "%.2s%5.5d\n", "kaslr.c", 138); return dataBuffer[index] & 0xFFF00000;

}

Page18

Twoaspectsofthiscodeshouldbeexplicitlynoted.First,thepropertiesarrayspecifiesthatthe

OSNumbervalueis256bytesinsize,whichiswhatultimatelyleadstodataleakage.Second,

theindexvalueusedtofindthememorylocationwithinthereturneddataBufferarrayvaries

withtheplatform/iOScombination.ThedevelopersofStage2havemappedouteach

combinationofplatform/iOStodeterminewhatpositionwithinthedataBufferarrayavalid

kernellocationispresent.

IfStage2isunabletofindthebaseaddressforthekernelusingtheabovedescribedmethodor

ifStage2findsthatitisoperatingunderaversionofiOSotherthan9,theassertcallbackis

calledandtheapplicationterminates.

EstablishingRead/Write/ExecutePrimitivesonPreviouslyRooted

Devices(32­Bit)

Afterfindingthekernel’sbaseaddress,32Stage2generatesanIPCpipesetviathepipe

function.Ifthepipecommandfails,32Stage2callstheassertcallbackfunctionandexits.

Followingthegenerationofthepipeset,32Stage2usesakernel porttoobtaintheclock servicesforthebatteryclock(alsoknownasthecalendarclock)andreal­timeclockviatwo callstohost_get_clock_service.Ifeitheroftheclocksareinaccessible,theassertion

callbackiscalledand32Stage2exits.Thepipesetandtheclockportsarecriticaltoestablishing

abeachheadforgainingaccesstothekernelmemoryspaceasthecombinationofthethree

objects(thepipesetandthetwoclocks)arelaterusedforkernelmemoryreadandwriteaccess

aswellaskernelprocessspaceexecutionaccess.

Immediatelyfollowingthepipe andhost_get_clock_servicecalls,32Stage2checksthe valueoftheporttothekernelthatwasgeneratedpreviouslybycallingtask_from_pid.If task_from_pid returnedavalid(non­NULL)port,32Stage2modifiesthekernel’smemoryby

writinga20­bytedatastructureusingvm_write.The20­bytedatastructureoverwritespartsof

theclock_opsstructuresforcalend_opsandrtclock_ops. 3

The20­bytedatastructurescontainpointerstohandlerfunctionsforthebatteryclockand

real­timeclockthatthekernelwillcallwhenfunctionssuchasclock_get_attributesare

called(callbackfunctions).The20­bytedatastructurereplacesthegetattrhandlerforbothof

theclocktypeswithexistingkernelfunctions.Specifically,thereal­timeclock’sgetattris

modifiedtopointtoOSSerializer::serialize,andthebatteryclock’sgetattrismodified

topointto_bufattr_cpx.

Thechoiceofthereplacementfunctionschangesthenatureoftheclock_get_attributes

callmadetothetwoclocktypes.Forcallstoclock_get_attributesforthebatteryclock,

thefunctionnowoperatesasakernelmemoryreadinterface.The_bufattr_cpxfunction

containsonlytwoinstructions:

Page19

_bufattr_cpx:

LDR

BX

R0, [R0]

LR

Thefirstparametertothefunction(inR0)isusedasamemoryaddressthatthefunctionreads

andreturnsinR0beforereturningtothecallingfunction.Whilethegetattr functionsuse threeparameters,giventhattheiPhone’sARM­basedfunctioncallsuseregistersforthefirst fourfunctionarguments,thelackofafullycompliantfunctionprototypeisirrelevant.

Thereplacementfunctionforthereal­timeclock’sgetattrfunctionisabitmorecomplex.The OSSerializer::serializefunctionexpectsaOSSerializerobjectasathis pointer(i.e.,anobjectthatincludesavirtualfunctiontable(vtable)).Theaddressstoredatoffset

0x10withintheOSSerializerobjectisusedasthefunctiontopasscontroltoviatheBX

instructionandusestheDWORDsatoffset8and12asparameterstothenextfunction.

_DWORD OSSerializer::serialize(OSSerialize *):

LDR

R3, [R0,#8]

MOV

R2, R1

LDR

R1, [R0,#0xC]

LDR.W

R12, [R0,#0x10]

MOV

R0, R3

BX

R12

Theresultofreplacingthegetattrhandlerforthereal­timeclockisthatnowthecallerto clock_get_attributescanexecutearbitraryfunctionswithinthekernelbysupplyinga speciallydesigneddatastructure,astructurethatwillbeexplainedingreaterdetaillaterinthis report. Whatisimportanttorememberatthispointisthattheclockmodificationsonlyoccurat thisphaseifthevictim’skernelisalreadyexposedinsomemanner.Thatis,theseclock modificationswouldnotbepossibleona non­jailbrokenphone.

If32Stage2alreadyhasaccesstothekernelportandhasperformedtheabove­mentioned

modificationstothevariousclocks,32Stage2willskipthenextseveralstepsthatitwould

normallyperforminordertogainsuchaccess,andpickupattheprivilegeescalationphase.If thekernelmodificationwasnotmadebecausethekernel’staskportwascurrentlyinaccessible,

32Stage2createsandlocksthelockfilespecifiedduringtheearlierinitializationphase.Thisfile

becomesimportantlaterasapieceoftheprocessthatultimatelygains32Stage2theabilityto

modifythekernel’smemory.

The64­bitversionoftheStage2binarydoesnotattempttotakeadvantageofpre­existing

backdoorsinpreviouslyjailbrokendevices.

Page20

ThreadManipulation

Stage2willeventuallyleverageause­after­free(UAF)vulnerabilityinordertoexecutearbitrary

codewithinthekernelspace.WhenusingaUAFvulnerability,itispossiblethataracecondition mayoccurwherethememorythatwasdereferenced(andwhichtheexploitwishestocontrol)is reallocatedforanotherthreadbeforetheexploitcanexecute.Inordertoreducetheprobability

ofanotherthreadaccidentallyallocatingintoacriticaldeallocatedchunk,Stage2willgeneratea

listofallofitsrunningthreadsandimmediatelyplaceeachthread(outsideofitsmainthread)in

asuspendedstate.Next,Stage2modifiestheschedulingpoliciesforthemainthreadtofurther

increasetheprobabilitythattheUAFexploitwillnotfacecompetitionforthememoryin

question.

Anadditionalstepisperformedinthe64­bitversionofStage2.Withthethreadscheduler

modificationscomplete,64Stage2generatesupto1000threads.Eachthreadconsistsofa

singletightloopthatmerelywaitforaglobalvariabletodropbelowapredefinedvalue(inthis

case,thevalueislessthan0).Thisbehaviorisintendedtoensure(or,atleast,significantly

increasethechances)thatnoadditionalthreadsmayspawnthatcancompetefortheUAF’s

targetedmemory.

EstablishingCommunicationChannel(32­Bit)

32Stage2generatesanotherpipesetusingthepipecommand,reusingthesamevariablethat

heldtheoriginalpipeset32Stage2generated.Thisactionisimmediatelyfollowedbycallsto

host_get_clock_serviceinordertogetaccesstothereal­timeandbatteryclocks.Aswith

thepipeset,thecallstohost_get_clock_servicereusethesamevariablesfromthe

previouscallstohost_get_clock_servicethatgainaporttothevariousclocks.

Thepreviousgenerationofthepipesetandtheclockportswerenecessarybecausethese itemsareusedlaterforkernelmanipulationandifthekerneltaskportwasavailablealready,

32Stage2wouldsimplyskiptheexploitationprocessnecessarytomodifythekernelandinstead

modifythekerneldirectlythroughvm_writecalls.However,if32Stage2doesnothaveaccess

tothekerneltaskport(thedefaultcaseonanon­jailbrokendevice),exploitationisnecessaryin

ordertoacquiresuchaccess.Aspartofthisexploitationprocess,32Stage2needstohavethe

pipesetandclocksavailablepriortotheexploit’sactivation,andthusthebinaryensuresthat

theyareavailable.Whilethisisunnecessarilyrepetitive,itdoesservetoensurethatthecritical

objectsarereadilyavailable.

The64­bitversionoftheStage2binarydoesnotneedtoperformthisstep,giventhatthe

triggeringmechanismusedtoultimatelycallthefunctionislittlemorethanaredirectionofan

existingfunctionpointertoasysctlhandler.

Page21

PayloadConstructionandKernelInsertion(32­Bit)

Withoutameanstomodifykernelmemorythroughthekernelport,32Stage2mustleveragea

vulnerabilitywithiniOStogainaccesstothekernel.Inordertoperformthistask,32Stage2

constructstwodatabuffers:a20­bytebuffercontainingthenecessaryoverwritedatatomodify

thereal­timeandbatteryclocksanda38­bytebuffercontainingapayloadthatrunsaseriesof

ROPgadgetstoinstalltheclockhandleroverwrites.Thetwodatabuffershavethefollowing

layoutaftertheirconstruction:

clock_ops_overwriteBuffer:

[00] (rtclock.getattr): address of OSSerializer::serialize [04] (calend_config): NULL [08] (calend_init): NULL [0C] (calend_gettime): address of calen_getattr [10] (calend_getattr): address of _bufattr_cpx

uaf_payload_bufferExploitBuffer:

[00] ptr to clock_ops_overwrite buffer [04] address of clock_ops array in kern memory [08] address of _copyin [0C] NULL [10] address of OSSerializer::serialize [14] address of "BX LR" code fragment [18] NULL [1C] address of OSSymbol::getMetaClass [20] address of "BX LR" code fragment [24] address of "BX LR" code fragment

32Stage2generatesanewthreadtohandlethenecessarysetupfortheinstallationofthenew

clockhandlersthoughthenewthread,itself,doesnotperformtheinstallation.Thethread

beginsbyestablishingakauth_filesecdatastructureonthestackwiththefollowingvalues:

// 0x12CC16D

.fsec_owner = <undetermined, random stack value> ; .fsec_group = <undetermined, random stack value>; .fsec_acl.entrycount = KAUTH_FILESEC_NOACL; // ­1

.fsec_magic = KAUTH_FILESEC_MAGIC;

Theuaf_payload_bufferexploitbufferisappendedtotheendofthekauth_filesecstructure inwhatisdefinedasthekauth_filesec.fsec_acl.acl_ace[]arrayarea.Thethreadthen opensaporttoIOKitandcallsIOServiceGetMatchingService forAppleKeyStore. UsingthesametechniqueexplainedpreviouslyintheKernelLocationsection,thethread obtainsavalidkernelmemorylocation.Theonlydifferencebetweenthenewthread’suseofthe

AppleKeyStoredisclosurevulnerabilityandthemethodusedby32Stage2previouslyisthe

propertynamethatthethreaduses(whichis

ararararararararararararararararararararararararararararararararararar

arararararararararararararararararararararararararararara").

Page22

AfterobtainingthekerneladdressfromtheAppleKeyStore,asyscallismadetothe

open_extendedfunction.32Stage2passesthelocationofthelockfiletothesyscallalongwith

boththeKAUTH_UID_NONEandKAUTH_GID_NONEvaluesandthekauth_filesecstructure constructedatthestartofthethread.Atthestartoftheopen_extended function,thefollowing codeexecutes:

if ((uap­>xsecurity != USER_ADDR_NULL) && ((ciferror = kauth_copyinfilesec(uap­>xsecurity, &xsecdst)) != 0))

Thekauth_copyinfilesecfunctioncopiesthekauth_filesecstructurepassedfrom

userlandintoakauth_filesecstructureinthekerneladdressspace.Beforeexplainingthe

vulnerabilityinthisfunction,itisnecessarytounderstandthelayoutofthekauth_filesec

structure.Thekauth_filesecstructuremakesupanaccesscontrollist(ACL)thatcontains

accesscontrolentries(ACE).Thestructureforkauth_filesecisdefinedas:

/* File Security information */ struct kauth_filesec { u_int32_t fsec_magic; guid_t fsec_owner; guid_t fsec_group; struct kauth_acl fsec_acl;

TheACLcomponentforkauth_filesecisstoredinakauth_aclstructure,whichcontains

anarrayofACE:

/* Access Control List */ struct kauth_acl { u_int32_t acl_entrycount; u_int32_t acl_flags; struct kauth_ace acl_ace[1];

Thekauth_acestructureis24­bytesinsizeanddefinedas:

typedef u_int32_t kauth_ace_rights_t;

/* Access Control List Entry (ACE) */ struct kauth_ace {

guid_t

ace_applicable;

u_int32_t

ace_flags;

kauth_ace_rights_t ace_rights;

/* scope specific */

Thekauth_aclfieldacl_entrycountisanunsignedintegerthatdefineshowmany kauth_aceentriesarewithintheacl_acearray.IfanACLcontainsnoACErecords,

acl_entrycountissettoKAUTH_FILESEC_NOACL,whichisdefinedas­1.Atthebeginning

Page23

ofthekauth_copyinfilesecfunction,thefollowingcommentisfoundwithinthepublicly

availablesourcecode:

/*

*

Make a guess at the size of the filesec.

We start with the base

*

pointer, and look at how much room is left on the page, clipped

*

to a sensible upper bound. If it turns out this isn't enough,

*

we'll size based on the actual ACL contents and come back again.

*

*

The upper bound must be less than KAUTH_ACL_MAX_ENTRIES. The

*

value here is fairly arbitrary. It's ok to have a zero count.

*/

Whenthenewthreadconstructsthekauth_filesecstructure,itdoessobydirectly

manipulatingthepositionofthestructureonthestackassuch:

// get stack address?

p = (unsigned int)&stackAnchor & 0xFFFFF000; // kauth_filesec.fsec_magic

(p + 0xEC0) = 0x12CC16D;

// kauth_filesec.fsec_acl.entrycount = KAUTH_FILESEC_NOACL

(p + 0xEE4) = ­1;

// kauth_filesec.fsec_acl.acl_ace[

memcpy(&stackAnchor & 0xFFFFF000 | 0xEEC, pExploit, 128);

]

Thestackhasthefollowinglayoutatthestartofthenewthread’sexecution:

char stackAnchor; // [sp+101Fh] [bp­2031h]@1 unsigned int size; // [sp+2020h] [bp­1030h]@12 char buffer[4096]; // [sp+2024h] [bp­102Ch]@12 int v26; // [sp+3024h] [bp­2Ch]@7 mach_port_t connection; // [sp+3028h] [bp­28h]@4 kern_return_t result; // [sp+302Ch] [bp­24h]@4 mach_port_t masterPort; // [sp+3030h] [bp­20h]@3 MAPDST

UsingthevariabledubbedstackAnchor,thenewthreadfindsapageboundaryaddressfor thestack.Then,byallocatingalargearraytoensurethatatleastonepageofthestackis unusedbyfunctioncriticalvariables,thenewthreadcanconstructakauth_filesecstructure thatcontainssignificantlymoreinformationthanisnecessary.Bysettingthe acl_entrycount toindicatethattherearenoACErecords,whenopen_extendedprocessesthe kauth_filesecstructure,itwillnotattempttoparseanydatabeyondtheacl_flags variable,thuspreservingtheintegrityoftheexploitbufferandpreventingthekernelfrom possiblyhavingissuewithhowtheexploitbufferwouldbeinterpretedasanactualACErecord. Theendresultofcallingopen_extendedistocopytheexploitbuffer(alongwiththe clock_ops_overwritebuffer)intokernelmemory.

Thenewthreadtakesadvantageofthisbehavioralodditywithintheopen_extendedsyscallin

ordertoplacethe(unmodified)payloadintothekernelmemory.Theaddressforthatpayloadis

thenrecoveredusingthepreviouslydiscussedvulnerabilitythatallowskernelmemorytobe

Page24

leakedbacktousermode.WhentheAppleKeyStorevulnerabilityisexploited,thevariable

dubbedbufferispassedtoio_service_open_extended(thesamevariablethatresides

adjacenttothestackAnchor).ThisbehaviormeansAppleKeyStorereturnedapointerfor

kernelmemorythatwasultimatelynexttotheexploitcodecopiedinbythesyscallto

open_extended.Therefore,thepurposeofthenewthreadisnottooverwritetheclockhandler

pointers,butrathertosetthestageforsuchanattack.

Oncethenewthreadcompletes,thevariablecontainingthememoryaddressoftheexploit bufferobtainedbythenewthreadaspartoftheAppleKeyStoreinformationleakistestedto determineifitwasindeedsetbythenewthread(priortocallingthenewthread,thevariablethat

holdsthekerneladdressisinitializedto0x12345678).Ifthenewthreaddidnotsuccessfully

obtainthekernelmemorylocation,32Stage2willcalltheassertcallbackandexit.

Afterthecompletionofthenewthread’sactivities,andifthephonereportsitselfas“iPhone4,1”

(theiPhone4series),themainthreadwillgenerateupto1000threads.Eachofthethreadswill

generateaverytightwhileloopthatwaitsuntilaglobalvariableissettolessthan0(itdefaults

to1000atthetimethethreadsaregenerated).Itisunclearwhythisbehaviorisrestrictedtothe

iPhone4s,astheresultofthisbehaviorwouldseemtohavevalueonallplatforms.Thisthread

resourceexhaustiondecreasestheprobabilitythatanotherthreadwillspawnandthuscompete

forthememoryresourcesduringtheUAFexploitation.

PayloadConstructionandKernelInsertion(64­Bit)

Giventhedifferencesinthetriggeringmechanismusedwithin64Stage2,thesetupandpayload

constructionissomewhatdifferent.Ratherthancreatingpipesandoverwritingtheclockgetattr handler,asysctlhandlerisoverwritten,ultimatelyresultinginanexecuteprimitivethatuses

OSSerializer::serializeinasimilarwayto32Stage2.Inordertoestablishanexecute

primitive,64Stage2usesthesysctlinterfacefornet.inet.ip.dummynet.extract_heapto

which64Stage2passesaspeciallycrafteddatastructurethatallowsthebinarytooverwritethe

functionpointerresponsibleforinterfacingwiththekernelvariable.Theendresultisa framework,similarinnaturetothegetattr handlers,thatallowsthe64Stage2binaryto executearbitrarycodeROPchainswithinthekernelfromuserspace.

EstablishingKernelRead/WritePrimitives(32­Bit)

Withtheexploitcodenowinkernelmemory,32Stage2mustactivatethecodeinordertoinstall

thenewclock_opshandlersthatgiveuserlandaccesstothethekernelmemory.32Stage2

usesause­after­free(UAF)vulnerabilitywithintheio_service_open_extended

deserializationroutine.

Whileio_service_open_extended’sdeserializationfunctionalitywaspreviouslyshownin

thisreporttoallowtheleakageofkerneladdressinformation,anothervulnerabilityinthesame

Page25

componentcanbeusedtoexecutearbitrarycodewithinthekernel.When io_service_open_extendedispassedaproperties datablob,thefunctionwillcopythe contentsfromuserspaceintokernelspacebeforepassingtheinformationto OSUnserializeXML.OSUnserializeXML inturnpassestheinformationto OSUnserializeBinaryifthekOSSerializeBinarySignature valueispresentatthe beginningofthedatablob.ItiswithinOSUnserializeBinary thatthevulnerabilityexists.

Thedatablobsuppliedintheproperties parameterrepresentsanXMLdictionary(or container)thathasbeenserialized.Inordertoreconstructtherelationships, OSUnserializeBinary iteratesthroughtheentiredatablobtoparseoutthevariousdata objects.Itispossiblethatduringtheencodingprocess(theprocessofturningtheoriginalXML intoitsbinaryrepresentation)thatthesameobjectisfoundrepeatedly.Inordertomore efficientlyhandlerepetitivedata,objectsarestoredwithinanarray(objsArray)andobjects withinthereconstructedXMLdictionarycanberepresentedbyanindexintothearrayof objects.

WithinOSUnserializeBinary,awhileloopiteratesthrougheachencodedobjectwithinthe

supplieddatablob.Theloopbeginsbydeterminingthetypeofobject(e.g.,

kOSSerializeDictionary,kOSSerializeArray,kOSSerializeNumber,andsoon)and

itssize.

len = (key & kOSSerializeDataMask);

switch (kOSSerializeTypeMask & key)

{

case kOSSerializeDictionary:

o = newDict = OSDictionary::withCapacity(len); newCollect = (len != 0);

break;

case kOSSerializeArray:

o = newArray = OSArray::withCapacity(len); newCollect = (len != 0);

break;

case kOSSerializeSet:

o = newSet = OSSet::withCapacity(len); newCollect = (len != 0);

case kOSSerializeObject:

if (len >= objsIdx) break;

o = objsArray[len]; o­>retain(); isRef = true;

break;

}

Theswitchstatementdispatchestheappropriateinstructionsforhandlingeachtypeofobject

foundwithinthedatablob.Theseinstructionscangeneratenewobjectsandsetflagsrelatedto

theobjectsdependingonwhattheparticularobjectrequiresduringthedeserializationprocess.

Page26

ThekOSSerializeObjectobjecttypeisaspecialcasethatrepresentsanalready

deserializedobjectand,assuch,setsaflagisReftotrueindicatingthattheobjectisa

referencetoanexistingobjectalreadywithintheobjsArrayarray.

IftheisRef valueisnotsettotrue,thecurrentobjectthatjustunderwentdeserializationis addedtotheobjsArray bymeansofsetAtIndex:

if (!isRef)

{

setAtIndex(objs, objsIdx, o); if (!ok) break; objsIdx++;

}

setAtIndexisamacrothat,amongotherthings,addsanobject(o)totheobjsArray.While morerobustarrayobjectsexistwithintheiOSenvironment,suchasOSArray (anarray containerthatwillhandlereferencecountingautomatically),OSUnserializeBinarytakesa moremanualrouteforarray­objectmanagementoftheobjectsithasdeserialized.Once deserialized,theobject’sreferencecountisdecrementedbycallingo­>release(),whichwill leadtotheobjectbeingfree()edinmostcases.Theexceptiontothisbehavioroccurswithin kOSSerializeObjectobjects.

SinceakOSSerializeObject objectrepresentsanobjectthatisreferencedbyotherentries, itisnecessarytoretaintheobjectafterserialization.Asaresult,duringdeserialization kOSSerializeObjectobjectswillcallo­>retain(),therebyincrementingthereference countfortheobjectandpreventingitsremovalfrommemory.

Aserializeddatabloballowsforthesamekeytobeusedmorethanonce.Inotherwords,itis

possibletohaveXMLcodethatlookslike:

<dict>

 

<key>KEY1<key>

<number>1</number>

<key>KEY1</key>

<string>2</string>

</dict>

TheaboveXML,onceserialized,willcontainfiveobjects.Thefirstobjectwillbethedictionary container(<dict>asakOSSerializeDictionaryobject),followedbyasymbol representingthekey(asakOSSerializeSymbol entrycontaining“KEY1”)anditsdataobject (akOSSerializeNumber entryfortheinteger1).Thefourthentryspecifiesanotherkey object,assignedKEY1 again,whichisnowastringobject(kOSSerializeString)containing thestring“2”.Aspartofthedeserializationprocess,thereuseofKEY1 resultsintheobjectthat

followsreplacingtheoriginalvalueassignedtoKEY1.Thisreassignmentofakeywithnewdata

isthesituationwhereOSUnserializeBinaryisvulnerabletoattack.

Page27

Asstatedpreviously,whenanobjectisdeserialized,andsolongasthatobjectisnota kOSSerializeObject,theobjectisstoredintheobjsArray forlaterreference.This storageistheresultofthesetAtIndexmacroseenhere:

#define setAtIndex(v, idx, o) \ if (idx >= v##Capacity) \ \

{

uint32_t ncap = v##Capacity + 64; \ typeof(v##Array) nbuf = (typeof(v##Array)) kalloc_container(ncap * sizeof(o)); \ if (!nbuf) ok = false; \ if (v##Array) \ \

{

bcopy(v##Array, nbuf, v##Capacity * sizeof(o)); \ kfree(v##Array, v##Capacity * sizeof(o)); \

}

\

v##Array

v##Capacity = ncap; \

= nbuf; \

\ if (ok) v##Array[idx] = o;

}

ThemacrowillexpandtheobjsArraytoaccommodatetheadditionalobjectandassignthe objecttotheendoftheobjsArraywithoutincreasingitsreferencecountbymeansofa o­>retain()call.Theproblemwiththismethodisthatwhenasecondobjectreplacesan existingobject(inourexamplethisiswheneverthestringobjectreplacesthenumberobjectfor

KEY1),thefirstobjectisreleasedandsubsequentlyfreed,butapointertothenowfreedobject

existswithintheobjsArray.Normallythiswouldsimplybeabadprogrammingdesignissue, buttheproblemiscompoundedifareferenceismadetotheobjectviaa kOSSerializeObject entry.IfakOSSerializeObjectentryreferences,byindex,thenow danglingpointerofthefreedobject,thecalltoo­>retain()willattempttoexecuteavirtual functionthatisattacker­controlled.

Inordertoexploitthisuse­after­freecondition,32Stage2musttakecontrolofthememory

locationthatwasdeallocatedandplaceacustomvtablethatwillhavetheentryforretain directedtoafunctionofitsownchoosing.Installingacustomvtablerequireshavingaccessto twodeallocated,adjacentmemorylocations.Sinceitisnotpossibletodirectlyoverwritethe vtableofanobjectduringtheserializationprocess,byallocatingandthenfreeingtwomemory locations,32Stage2canuseanOSData orOSString objecttoreplacetwomemorylocations atoncewithoneofthememorylocationscontainingthemaliciousvtable.Theeasiestwayto

understandthisconceptistolookatthepayloadthat32Stage2generatestoexploitthe

OSUnserializeBinaryUAFvulnerability.

Themaliciouspayloadthat32Stage2generatesforthisvulnerabilitydependsontheiOS

version.ForiOSversion9.3.2throughatleast9.3.3,thepayloadtakesthefollowingform:

[0x00] kOSSerializeBinarySignature [0x04] kOSSerializeEndCollecton | kOSSerializeDictionary | 0x10 [0x08] kOSSerializeString | 4

Page28

[0x0C] “sy2” [0x10] kOSSerializeData | 0x14 [0x14] {payload buffer} [0x28]kOSSerializeEndCollecton | kOSSerializeObject | 1

ForiOS9.0through9.3.1,thepayloadtakesthefollowingform:

[0x00] kOSSerializeBinarySignature [0x04] kOSSerializeEndCollecton | kOSSerializeDictionary | 0x10 [0x08] kOSSerializeString | 4 [0x0C] “sy2” [0x10] kOSSerializeEndCollecton | kOSSerializeArray | 0x10 [0x14] kOSSerializeDictionary | 0x10 [0x18] kOSSerializeSymbol | 4 [0x1C] “sy1” [0x20] kOSSerializeData | 0x14 [0x24] ”ffff\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0” [0x38] kOSSerializeSymbol | 4 [0x3C] “sy1” [0x40] kOSSerializeEndCollecton | kOSSerializeSymbol | 4 [0x44] “sy1” [0x48] kOSSerializeString | 0x1C [0x4C] {payload buffer} [0x68] kOSSerializeString | 0x1C [0x6C] {payload buffer} [0x88] kOSSerializeString | 0x1C [0x8C] {payload buffer} [0xA8] kOSSerializeEndCollecton | kOSSerializeObject | 5

Whilestructurallytheylooksomewhatdifferent,ultimatelytheybothexploitthesameUAF

vulnerability.Thesimplerpayload(foriOS9.3.2andlater)istheeasiesttounderstand.When

OSUnserializeBinarybeginstheparsingprocesstodeserializethepayload,thefunction

willcreateanewdictionaryobjectasaresultoftheentryatoffset0x04.Withinthisdictionary

aretwounkeyedobjects.ThefirstobjectisanOSString objectwiththevaluesy2 (specified inoffsets0x08and0x0C,respectively).Offset0x10specifiesanOSData objectof0x14(20) bytesinsize.TheOSData objectcontainsthepayloadbufferdatastructure.Sincetheobjects areunkeyed,OSUnserializeBinarywillreplacetheOSString objectwiththeOSData objectbutleavethepointersinplaceinobjsArray.WiththeOSString objecthavingno retain()calls,theOSString isdeallocated,therebyputtingtwomemoryarraysintothefree list(onefortheOSString objectitselfandoneforthestringassociatedwiththeOSString object).

WhenOSUnserializeBinaryparsesthekOSSerializeData entry,anewOSData object isallocatedandthusconsumesoneofthefreedmemorylocationsfromthefreelist.Whenthe dataassociatedwiththekOSSerializeData entryiscopiedintotheOSData object,anew bufferisallocatedforthedata,whichconsumestheremainingdatalocationfromthefreelist.At thispoint,thedanglingpointerinobjsArray hasbeenreplacedwithanOSData object’s data.ItisthedataassociatedwiththeOSDataobjectthatcontainsthemaliciouspayloadthat

Page29

willultimatelygive32Stage2writeaccessintothekernelinordertoinstalltheread/write

primitives.

RegardlessoftheiOSversion,themaliciouspayloadcontainsthesamepayloadbuffer.The

payloadbufferisa20­bytestructureconsistingofthefollowingelements:

[00]addressofuaf_payload_buffer+8

[04]{uninitializeddatafromstack}

[08]addressofuaf_payload_buffer

[0C]staticvalueof20

[10]addressofOSSerializer::serialize

Thelayoutofthepayloadmustcontainthepointertothenewretainfunctionatoffset0x10.

32Stage2usestheOSSerializer::serializefunctionasthereplacementretain.This

layoutmeansthattheremainderofthepayloadmustnowmimicthevtableofan OSSerializer object.AsexplainedpreviouslyinEstablishingRead/Write/ExecutePrimitives onPreviouslyRootedDevices,theOSSerializer::serializefunctionwillcallthefunction

atoffset0x10ofthesuppliedvtablewhilepassingoffsets0x08and0x0Cofthevtabletothe

calledfunction.Giventhatoffset0x10issettoOSSerializer::serialize,thefunctionis

beingcalledagain,butthesecondcallwillbegiventhevtablespecifiedatoffset0x08.Thiscall

kicksoffaseriesofsubsequentcallsthatultimatelyleadstoacallto_copyinthatreplacesthe

getattrhandlersforthereal­timeandbatteryclocks,asdescribedinEstablishing

Read/Write/ExecutePrimitivesonPreviouslyRootedDevices.

Followingtheexecutionoftheexploit,andifthevictim’sphoneisan“iPhone4,1”model,the

globalvariablecontrollingthe1000threadsgeneratedpreviouslyissetto­1inorderto

terminatethethreads.

Toverifythatthebatteryclock’sgetattrhandlerisworkingasakernelmemoryaddress reader,clock_get_attributesiscalledwiththereadlocationspecifiedasthebaseaddress forthekernel.Iftheresultfromclock_get_attributesisnotthemagicvalueof

0xFEEDFACE,theattemptismadeoncemore.Asecondfailureresultsintheassertcallback

beingcalledand32Stage2terminating.

EstablishingKernelRead/WritePrimitives(64­Bit)

Thesameunderlyingvulnerabilityisexploitedinthe64­bitversionofStage2.Inprinciple,the

exploitisstructuredinaverysimilarway.Theprimarydifferenceisthattheultimateexecute primitiveisestablishedbywritingtothenet.inet.ip.dummynet.extract_heapsysctl

handler.TheOSSerializer::serializeisusedinasimilarwayaswithin32Stage2.

Arbitrarycodeexecution(throughtheexecutionofarbitraryROPchains)isthenachievedusing

thesamemechanismdescribedinEstablishingKernelExecutePrimitive(32­Bit).

Page30

EstablishingaKernelExecutePrimitive(32­Bit)

AsexplainedpreviouslyinInstallingKernelAccessHandlersonRootedDevices,thereal­time clock’sgetattrhandlerpointstoOSSerializer::serialize,whichallowsthecallerof clock_get_attributestopassaspeciallycraftedstructureto OSSerializer::serializeinordertoexecuteinstructionswithinkernelspace.Byvirtueof

executingwithinkernelspace,theuserland32Stage2processmusthaveawayoftransferring

datatothekerneladdressspaceinareliablemanner.32Stage2usesacleverquirkofthe

pipe­createdpipesettoaccomplishthistask.

Afterestablishingthebatteryclock’snewgetattr handleras_bufattr_cpx,32Stage2has

areliablemethodforreadingDWORDsfromthekerneladdressspaceintouserland.32Stage2

usesthisfunctionalitytofindtheaddrperm valuestoredwithinthekernel.addrperm defines theoffsetappliedtotheaddressfromthekernelwhenpassedtouserlandinordertoobfuscate thetruelocationofthedatainthekernel.Byobtainingthisvalue,itispossibletodeobfuscate kerneladdressesbacktotheirrealaddressvalues.32Stage2callsfstat onthereadpipe fromthegeneratedpipesetandthencalculatesthedifferencebetweenthelocationofthestat structureandthekerneladdressspace.Thisvalueisthenstoredinaglobalvariableforuseby functionsthatmustaccesskernelmemoryforthepurposesofcodeexecution.

Whenever32Stage2wantstoexecutecodewithinthekernel,thefollowingdatastructureis

writtentothewritepipeofthegeneratedpipeset:

[00]argument1

[04]argument2

[08]addressofcodetoexecute

Inordertocallthefunctionspecifiedinoffset8ofthedatastructure,anotherDWORDis

prependedtothestructureandpassedtothereal­timeclock’sgetattr handler(accessedvia

OSSerializer::serialize),whichplacesargument1intoR3andargument2intoR1

beforecallingtheaddressspecifiedforthefunctiontoexecute.Byprependingtheunused DWORDtothedatastructure,thedatastructurebecomesthevtablereplacementfor

OSSerializer.Thistechniqueisusedintwodifferentfunctionswithin32Stage2.Onefunction

allowsarbitrarykernelfunctioncallsandtheotherisusedtowriteDWORDvaluesintothe

kerneladdressspace.

PatchingtheKerneltoAllowKernelPortAccess

Withtheabilitytoread,write,andexecutearbitrarylocationswithinthekerneladdressspace,

thenextstepinvolvesgainingmoredirectaccesstothekernelthroughthekernelport.The

Page31

functiontask_for_pidwillreturnanerrorifcalledwiththePIDvalueof0.Inordertobypass

thisprotection,Stage2modifiesfourdifferentlocationswithinthetask_for_pid function.

Beforethepatchingoftask_for_pidbegins,Stage2determinesifthearearequiringthe

patchiswithinaregionofmemorythatisread/execute.Ifthememoryisnon­writable,Stage2

willdirectlymodifythepermissionsofthememoryregiontoallowforwriteaccessandthen

invalidatethedcacheandflushthedataandinstructionTLBsinordertoensurethatthe

memoryregionisupdatedwiththenewpermissions.

Afterpatchingthetask_for_pidfunctiontoallowthecallertogainaporttothekernel, Stage 2willattempttogetakernelportbycallingtask_for_pid(mach_task_self, 0, &port)

uptofivetimeswitha100mlliseconddelaybetweeneachattemptbeforecallingtheassert

callbackandexiting.

Page32

Section3:PrivilegeEscalationandActivatingthe

JailbreakBinary

ThissectioncoversthefinalstepscarriedoutinStage2togainrootaccesson

theiPhone,disablecodesigning,thendropandactivatethejailbreakbinary.This stage leverages the final Trident vulnerability, where kernel memory corruption

leadstojailbreak(CVE­2016­4656).

Page33

SystemModificationforPrivilegeEscalation

Thenextstepfor32Stage2istogainrootaccessoverthevictim’sphone.IftheStage2process

isnotcurrentlyrunningasroot(UID=0),whichitwillnotbeonanon­jailbrokenphone,then

Stage2patchesthesetreuidfunctiontoskipthecheckforprivilegeescalation.Oncethe

modificationtosetreuidiscomplete,thefunctioniscalleduptofivetimes(with500msdelays

betweeneachcall)untilsetreuid(0, 0)returnssuccessful.Afterfiveattempts(oraftera successfulsetreuidcall),themodificationmadetosetreuidisreversed.Afinalcheckofthe

process’suservalue(UID)ismadetoensurethatitis,indeed,root(0).Ifthefunctiongetuid

returnsanyvalueotherthan0,theassertiscalledandStage2exits.

Stage2callsthekernelfunctionkauth_cred_get_with_refbymeansofthereal­timeclock

clock_get_attributesvectorinordertoreceivethecredentialsforthemainthread.

Followingthis,Stage2locatesthemac_policy_list,whichcontainsthelistofaccesscontrol

policymodulescurrentlyloadedintotheiOSkernel.Stage2examinesthelistlookingfora

modulethatstartswiththename“Seat”,referringtothe“Seatbeltsandboxpolicy”.Ifthepolicy

moduleisnotfound,Stage2callstheassertcallbackandterminates.Ifthemoduleisfound,

however,thempc_field_offmemberisreadandmodifiedtoallowthecurrentprocess

greateraccesstothevictim’siPhone.

Withaccesstothekernelportnowestablishedandrestrictionsremovedthatwouldprevent

Stage2fromperformingprivilegedactionsnormallyblockedbysandboxpolicy,Stage2no

longerrequiresthehookedgetattr handlerforthereal­timeclock.Toensurethatfuturecalls tothishandlerdonotcrashthephone,thegetattr functionpointerismodifiedtopointtothe instructions:

BX LR

Thisnewhandlerfunctioneffectivelyturnsfuturecallstothereal­timeclock’sgetattr handler intoaNOP.Thisispresumablydonetoensurethatfuturecallsthetothegetattrhandler(by someotherprocess)donothaveunintendedconsequencesandcausethekerneltocrash.

DisablingCodeSigning

Bydefault,iOSonastandardiPhonewillpreventunsignedcodefromrunningthroughnormal means,suchasanexecvorsystemcall.Likewise,modificationstotherootfilesystemare

preventedbysettingthefilesystemasread­only.ThesearesituationsthatwillpreventStage2

fromexecutingajailbreakprogramandwillpreventthejailbreakprogram,ifitactivates,from

beingabletomodifythesystem.Stage2modifiesseveralkernelfunctionsandtwokernel

extensions(kext)inordertopermittheseforbiddenactions.Stage2startsbyfindingthekextfor

com.apple.driver.AppleMobileFileIntegrityandcom.apple.driver.LightweightVolumeManager.

Page34

Thecom.apple.driver.AppleMobileFileIntegrity(AMFI)extensionisresponsibleforenforcing

iOS’scodesigningfunctionality.Thecom.apple.driver.LightweightVolumeManagerextensionis

responsibleforthepartitiontableofthemainstoragedevice.

Stage2locateseachofextensionsbycallingOSKextCopyLoadedKextInfoforeachextention’s

name,whichreturnsadictionaryobjectcontaininginformationabouttheextension.Withinthe

dictionaryistheloadingoffsetoftheextensionbeingqueriedthatStage2turnsintothekernel

memoryaddressbyaddingtheknownkernelslidevalue.

ArmedwiththekerneladdressoftheAMFI,Stage2locatesthefollowingglobalvariables:

amfi_get_out_of_my_way

cs_enforcement_disable

Thesetwovariables,whenset,disableAFMI(amfi_get_out_of_my_way)anddisablecode

signingenforcement(cs_enforcement_disable).Stage2thensetstwomoreglobal

variables:debug_flags andDEBUGflag.Thesetwovariablesallowfordebuggingprivilege onthevictim’siPhone,furtherreducingtherestrictionsthatthesandbox(Seatbelt)imposeson thedevice.

Next,Stage2patchesthekernelfunctionvm_map_enterandvm_map_protectinorderto

disablecodesigningverifications(makingitpossibletoallocateRWXregions)withinthevirtual

memorymanager.Followingthis,Stage2patchesthe_mapForIOfunctionwithinthe

LightweightVolumeManagerbeforepatchingthekernelfunctioncsopstodisableevenmore

codesigningprotections.

RemountingtheDrive

Inordertojailbreakadevice,therootfilesystemmustbeaccessibleforwriting.Stage2tests

thewritabilityoftherootfilesystembycallingtheaccess functionagainst/sbin/launchdto

determineifStage2haswriteaccesstotherootfilesystem.Ifthefileisread­only,Stage2

patchesthekernelfunction_mac_mounttodisabletheprotectionpolicythatprevents remountingthefilesystemasread/writeandthenremountstherootfilesystemasread/writeby callingmount(“hfs”, “/”, MNT_UPDATE, mountData) wheremountData specifiesthe

/dev/disk0s1s1device.

Stage2iswrittensuchthatitwillonlyoperateoniOS9seriesiPhones,butcodeexiststhat

suggestitwasonceusedonolderiOSversions.Asevidencetosupportthisclaim,thereexists

afunctionthatiscalledafterStage2remountstherootfilesystemthatmodifiesitsexecution

pathifitisrunningoniOS7,iOS8,oriOS9.DependingontheiOSversion,thefunctioncalls

fsctloneither/bin/launchctl(foriOS7and8)or/bin/launchd(foriOS9).The

fsctlcallwillmodifythelowdiskspacewarningthresholdaswellastheverylowdiskspace

warningthreshold,settingthevaluesto8192and8208,respectively.

Page35

Cleanup

Stage2isactivatedastheresultofabuginSafarithatallowsforarbitrarycodeexecution.As

oneofthelastactivitiesStage2performspriortodroppingandactivatingthejailbreakbinary,

Stage2attemptstocoveritsinfectionvectorbycleaningupthehistoryandcachefilesfrom

Safari.TheprocessofclearingtheSafaribrowserhistoryandcachefilesisstraightforwardand

iOSversion­specific.

ForiOS8andiOS9(Stage2willterminateatthebeginningifitisnotrunningoniOS9),the

followingfilesaresummarilydeletedfromthevictim’siPhonetoremovebrowserandcache

information:

● /Library/Safari/SuspendState.plist

● /Library/Safari/History.db

● /Library/Safari/History.db­shm

● /Library/Safari/History.db­wal

● /Library/Safari/History.db­journal

● /Library/Caches/com.apple.mobilesafari/Cache.db

● /Library/Caches/com.apple.mobilesafari/Cache.db­shm

● /Library/Caches/com.apple.mobilesafari/Cache.db­wal

● /Library/Caches/com.apple.mobilesafari/Cache.db­journal

● (filesinthedirectory)/Library/Caches/com.apple.mobilesafari/fsCachedData/

ForiOS7,thefollowingfilesareremoved:

● /Library/Safari/SuspendState.plist

● /Library/Caches/com.apple.mobilesafari/Cache.db

● /Library/Caches/com.apple.mobilesafari/Cache.db­shm

● /Library/Caches/com.apple.mobilesafari/Cache.db­wal

● /Library/Caches/com.apple.mobilesafari/Cache.db­journal

Thefunctionconcludesbycallingsync toensurethedeletionsarewrittentodisk.

NextStageInstallation

Again,showingevidenceoftheuseofcodeoriginallytargetinganolderiOSversion,thenext functionthemainthreadcallsdecompressesanddropstwofilesontothevictim’sfilesystem.

ThefollowingcodesnippetillustrateshowStage2determinesthelocationofthejailbreaker

binaryonthevictim’sdevice:

if ( (unsigned int)(majorVersion ­ 8) >= 2 )

{

Page36

if ( majorVersion == 7 )

{

pszJBFilenamePath = "/bin/sh"; if ( flag) pszJBFilenamePath = "/private/var/tmp/jb­install";

}

else

{

assert(); writeLog(3, "%.2s%5.5d\n", "bh.c", 134);

exit(­1);

pszJBFilenamePath = 0;

}

}

else

{

pszJBFilenamePath = "/sbin/mount_nfs.temp";

}

ThecodesnippetshowsthatforiOSversion7,theinstallpathforthenextstage’sbinaryis

either/bin/shor/private/var/tmp/jb­install(ifflag isnon­zero).ForiOSversions

olderthan7,theassertcallbackiscalledandtheprogramterminates.ForiOS8andgreater,

theinstallpathisspecifiedas /sbin/mount_nfs.temp.

Thesizeofthedatablobcontainingthenextstagebinaryisverifiedtobenon­zero.Ifthesizeis

zero,theassertcallbackoccursandStage2isterminated.TheBZ2_*APIfunctionsarethen

usedbyStage2todecompressthedataintotwofiles:thefirstfileisthenextstagebinary,

which,foriOS9,isstoredat/sbin/mount_nfs.temp.Thesecondfileistheconfiguration

file,whichisstoredat/private/var/tmp/jb_cfg.

Thepermissionsofthetwofilesarechangedto0755 (makingthefilesexecutable)before controlreturnstothemainthread.

ThefinalfunctionthatStage2callsbeforeterminatingisresponsibleformovingthebinary

droppedbythepreviousstep.ForiOSversions8and9,thefile/sbin/mount_nfs.tempis

renamedto/sbin/mount_nfs.IftheiOSonthevictim’sphoneisiOS9,anattemptismade

todelete/sbin/mount_nfspriortotherenamingoperation.Afterrenamingthefile,theassert callbackfunctioniscalledfollowedbytheexit function,terminatingStage2.

Onceexecutionreturnstothemainthread,Stage2terminatessilently.

ExistingJailbreakDetection

Asmentionedpreviously,theStage2binaryoperatesintwodistinctmodes.Thefirst,whichhas

alreadybeendiscussed,constitutesacompleteiOSexploitandjailbreak.Thesecondisthe

codepathtakenwhentheStage2binaryisrunonasystemthathasalreadybeenjailbroken.In

Page37

thismode,Stage2simplytakesadvantageoftheexistingjailbreakbackdoorstoinstallthe

Pegasus­specifickernelpatches.

Todeterminewhetherthethedevicehasalreadybeenjailbroken,Stage2attemptstoacquirea

validmachport(ahandle)intotheiOSkernelusingacommonjailbreakbackdoor.Thischeckis

performedsimplybycallingtask_for_pidwiththePIDvaluesetto0.Patching

task_for_pidinthiswayisacommonbackdoormechanismusedbyiOSjailbreaksthat providesdirectkernelmemoryaccesstoausermodeprocess.Callingtask_for_pidwitha

PIDof0isnotnormallyallowedbyiOS.Iftask_for_pidreturnsavalidtaskport,thenthe

Stage2processhaselevatedaccesstothekernelandcanforgotheprivilegeescalationsteps

describedpreviously.

Stage2alsochecksforthepresenceofthebinary/bin/sh.Onanon­jailbrokenphone,this

binaryshouldneverexist.WhenStage2detectsthepresenceofthisbinary,itassumesthatthe

existingjailbreakiseitherincompatiblewithPegasusorthatallrequiredkernelpatchesare alreadyinplaceandnofurtheractionisneeded.When/bin/shisidentifiedonadevice,prior

toexploitation,Stage2simplyexitscleanly.

Page38

Section4:PegasusPersistenceMechanism

ThissectiondetailsthepersistencemechanismusedbyPegasustoremainonthe

deviceaftercompromiseviaanexploitoftheTridentvulnerabilities,andcontinue

toexecuteunsignedcodeeachtimethedevicereboots.

Page39

PegasusPersistenceMechanism

ThepersistencemechanismusedbyPegasustoreliablyexecuteunsignedcodeeachtimethe

deviceboots(and,ultimately,executethekernelexploittoagainjailbreakthedevice)reliesona

combinationoftwodistinctissues.

Thefirstissueisthepresenceofthertbuddydservicewithinaplist(tobelaunchedondevice

boot).NotethatpriortoiOS10,rtbuddydispresentonsomeiPhonedevicesforexamplethe

iPhone6S,butnotonothersliketheiPhone6.Asaresult,anysignedbinarythatcanbecopied

intothespecifiedpath(/usr/libexec/rtbuddyd)willbeexecutedatboottimewiththe

argumentsspecifiedintheplist(specifically“­­early­boot”).

<key>rtbuddy</key><dict><key>ProgramArguments</key><array><string>rtbuddyd</string>

<string>­­early­boot</string></array><key>PerformInRestore</key><true/><key>RequireSuccess</key>

<true/><key>Program</key><string>/usr/libexec/rtbuddyd</string></dict>

Asaresultofthisbehavior,anysignedbinaryonthesystemcanbeexecutedatbootwitha singleargument.Bycreatingasymlinknamed­­early­boot withinthecurrentworking directory,anarbitraryfilecanbepassedasthefirstargumenttothearbitrarysignedbinarythat hasbeencopiedtothertbuddyd location.

Thesecondissueleveragedinthispersistencemechanismisavulnerabilitywithinthe

JavaScriptCorebinary.Pegasusleveragesthepreviouslydescribedbehaviorinordertoexecute

thejscbinary(JavaScriptCore)bycopyingittothepath/usr/libexec/rtbuddyd.Arbitrary

JavaScriptcodecanthenbeexecutedbycreatingasymlinknamed­­early­bootthatpoints

toafilecontainingthecodetobeexecutedatboottime.Pegasusthenexploitsabadcastinthe

jscbinarytoexecuteunsignedcodeandre­exploitthekernel.

JavaScriptCoreMemoryCorruptionIssue

TheissueexistswithinthesetImpureGetterDelegate()JavaScriptbinding(whichisbackedby

functionSetImpureGetterDelegate).

EncodedJSValue JSC_HOST_CALL functionSetImpureGetterDelegate(ExecState* exec)

{

JSLockHolder lock(exec); JSValue base = exec­>argument(0); if (!base.isObject()) return JSValue::encode(jsUndefined()); JSValue delegate = exec­>argument(1); if (!delegate.isObject()) return JSValue::encode(jsUndefined()); ImpureGetter* impureGetter = jsCast<ImpureGetter*>(asObject(base.asCell())); impureGetter­>setDelegate(exec­>vm(), asObject(delegate.asCell()));

Page40

return JSValue::encode(jsUndefined());

}

Thisbindingtakestwoarguments:thefirstisanImpureGetter,andthesecondisageneric

JSObjectthatwillbesetastheImpureGetter’sdelegate.Theissueresultsfromthelackof

validationthattheJSObjectpassedasthefirstargumentisinfactawell­formedImpureGetter.

Whenanotherobjecttypeispassedasthefirstargument,theobjectpointerwillbeimproperly

downcasttoanImpureGetterpointer.

Subsequently,whenthem_delegatememberissetviasetDelegate(),apointertotheJSObject

passedasthesecondargumentwillbewrittentotheoffsetthatalignswithm_delegate(16

bytesintothesuppliedobject).Thisissuecanbeusedtocreateaprimitivethatallowsapointer

toanarbitraryJSObjecttobewritten16bytesintoanyotherJSObject.

Exploitation

PegasusleveragesthisissuetoachieveunsignedcodeexecutionfromwithinaniOS applicationcontext.Inordertogaincontrolofexecutionflow,theexploitusesanumberof DataViewobjects.DataViewsareusedbecausetheyprovideatrivialmechanismtoreadand writearbitraryoffsetsintoavector.TheDataViewobjectalsoconvenientlyhasapointertothe

backingbufferatits16byteoffset.UsingthesecorruptedDataViewobjects,theexploitsetsup

thetoolsneededtogainarbitrarynativecodeexecution­namely,aread/writeprimitiveandthe

abilitytoleaktheaddressofanarbitraryJavaScriptobject.Oncethissetupiscomplete,the

exploitcancreateanexecutablemappingcontainingthenativecodepayload.Thefollowing

sectionsdetailthevariousstagesofthisprocess.

Acquiringanarbitraryread/writeprimitive

Aread/writeprimitiveforarbitraryoffsetsintoaDataViewobjectcanbeobtainedusingthe

followingcodesnippet.

var dummy_ab = new ArrayBuffer(0x20); var dataview_init_rw = new DataView(dummy_ab);

var dataview_rw = new DataView(dummy_ab);

setImpureGetterDelegate(dataview_init_rw, dataview_rw);

First,twoDataViewsarecreatedusingadummyArrayBufferasthebackingvectorforboth.

Next,theissueisexploitedtocorruptthem_vectormemberofthedataview_init_rwobjectwith

apointertothedataview_rwobject.Subsequentreadsandwritesintothedataview_init_rw

DataViewwillallowarbitrarymembersofthedataview_rwtobeleakedoroverwritten.Next,

controloverthisobjectisusedtogainaread/writeprimitivefortheentiretyofprocessmemory.

Page41

var DATAVIEW_ARRAYBUFFER_OFFSET = 0x10; var DATAVIEW_BYTELENGTH_OFFSET = DATAVIEW_ARRAYBUFFER_OFFSET + 4; var DATAVIEW_MODE_OFFSET = DATAVIEW_BYTELENGTH_OFFSET + 4; var FAST_TYPED_ARRAY_MODE = 0;

dataview_init_rw.setUint32(DATAVIEW_ARRAYBUFFER_OFFSET, 0, true);

dataview_init_rw.setUint32(DATAVIEW_BYTELENGTH_OFFSET, 0xFFFFFFFF, true);

dataview_init_rw.setUint8(DATAVIEW_MODE_OFFSET, FAST_TYPED_ARRAY_MODE, true);

Threeoffsetsintothedataview_rwDataViewarewritten.First,thepointertothebackingvector

ispointedtothezeroaddress.ThenthelengthoftheDataViewissetto0xFFFFFFFF,

effectivelysettingtheDataViewtomapallofthevirtualmemoryoftheprocess.Last,themode

issettoasimpletype(i.e.,FastTypedArray),allowingtrivialcalculationsoftheoffsetintoa

DataViewgivenavirtualaddress.Thedataview_rwDataViewnowprovidesanarbitrary

read/writeprimitiveviathegetTypeandsetTypemethodsitexposes.

Leakinganobjectaddress

Thelastprimitiveneededistheabilitytoleakthevirtualmemoryaddressofanarbitrary

JavaScriptobject.Theprimitiveisachievedusingthesameissueexploitedabovetoleakthe

addressofasingleobjectinsteadofexposingtheentirememoryspace.

var dummy_ab = new ArrayBuffer(0x20);

var dataview_leak_addr = new DataView(dummy_ab); var dataview_dv_leak = new DataView(dummy_ab); setImpureGetterDelegate(dataview_dv_leak, dataview_leak_addr);

setImpureGetterDelegate(dataview_leak_addr, object_to_leak); leaked_addr = dataview_dv_leak.getUint32(DATAVIEW_ARRAYBUFFER_OFFSET, true);

Again,twoDataViewsarecreatedusingadummyArrayBufferasthebackingvectorforboth. Next,theissueisexploitedtocorruptthem_vectormemberofthedataview_dv_leakobjectwith apointertothedataview_leak_addrobject.ToleaktheaddressofanarbitraryJavaScript object,theissueistriggeredasecondtime.Thistime,them_vectorofthedataview_leak_addr DataViewiscorruptedwiththeaddressoftheobjectthatisbeingleaked.Finally,thedword

residingatthe16thbyteoffsetintothedataview_dv_leakDataViewcanbereadtoobtainthe

addressofthetargetobject.

Unsignednativecodeexecution

Pegasususesthesamemechanismtogaincodeexecutioninthisexploitasusedinthestage1

Safariexploit.Theexploitcreatesanexecutablemappingthatwillcontaintheshellcodetobe

executed.Toaccomplishthispurpose,aJSFunctionobjectiscreated(containinghundredsof

emptytry/catchblocksthatwilllaterbeoverwritten).TohelpensurethattheJavaScriptwillbe

Page42

compiledintonativecodebytheJIT,thefunctionissubsequentlycalledrepeatedly.Giventhe

natureoftheJavaScriptCorelibrary,thisJIT­compilednativecodewillresideinanareaof

memorythatismappedasread/write/execute.

var body = '' for (var k = 0; k < 0x600; k++) { body += 'try {} catch(e) {};';

}

var to_overwrite = new Function('a', body); for (var i = 0; i < 0x10000; i++) { to_overwrite();

}

TheaddressofthisJSFunctionobjectcanthenbeleakedandthevariousmemberscanberead

toacquiretheaddressoftheRWXmapping.TheJITedtry/catchblocksarethenoverwritten

withshellcode,andtheto_overwrite()functioncansimplybecalledtoachievearbitrarycode

execution.

Page43

Оценить