FromOutofMemorytoRemoteCodeExecu3on �YukiChen(@guhe120)Qihoo360VulcanTeam
�
CVE-2014-0290,CVE-2014-0321,CVE-2014-1753,CVE-2014-1769,CVE-2014-1782,CVE-2014-1804,CVE-2014-2768,CVE-2014-2802,CVE-2014-2803,CVE-2014-2824,CVE-2014-4057,CVE-2014-4092,CVE-2014-4091,CVE-2014-4095,CVE-2014-4096,CVE-2014-4097,CVE-2014-4082,CVE-2014-4105,CVE-2014-4129,CVE-2014-6369,CVE-2015-0029,CVE-2015-1745,CVE-2015-1743,CVE-2015-3134,CVE-2015-3135,CVE-2015-4431,CVE-2015-5552,CVE-2015-5553,CVE-2015-5559,CVE-2015-6682,CVE-2015-7635,CVE-2015-7636,CVE-2015-7637,CVE-2015-7638,CVE-2015-7639,CVE-2015-7640,CVE-2015-7641,CVE-2015-7642,CVE-2015-7643,CVE-2015-8454,CVE-2015-8059,CVE-2015-8058,CVE-2015-8055,CVE-2015-8057,CVE-2015-8056,CVE-2015-8061,CVE-2015-8067,CVE-2015-8066,CVE-2015-8062,CVE-2015-8068,CVE-2015-8064,CVE-2015-8065,CVE-2015-8063,CVE-2015-8405,CVE-2015-8404,CVE-2015-8402,CVE-2015-8403,CVE-2015-8071,CVE-2015-8401,CVE-2015-8406,CVE-2015-8069,CVE-2015-8070,CVE-2015-8440,CVE-2015-8409,CVE-2015-8047,CVE-2015-8455,CVE-2015-8045,CVE-2015-8441,CVE-2016-0980,CVE-2016-1015,CVE-2016-1016,CVE-2016-1017,CVE-2016-4120,CVE-2016-4160,CVE-2016-4161,CVE-2016-4162,CVE-2016-4163,CVE-2016-4185CVE-2016-4249,CVE-2016-4180,CVE-2016-4181,CVE-2016-4183,CVE-2016-4184,CVE-2016-4185,CVE-2016-4186,CVE-2016-4187,CVE-2016-4233,CVE-2016-4234,CVE-2016-4235,CVE-2016-4236,CVE-2016-4237,CVE-2016-4238,CVE-2016-4239,CVE-2016-4240,CVE-2016-4241,CVE-2016-4242,CVE-2016-4243,CVE-2016-4244,CVE-2016-4245,CVE-2016-4246,CVE-2016-4182,CVE-2016-3375,CVE-2017-3001,CVE-2017-3002,CVE-2017-3003,CVE-2017-0238,CVE-2017-0236,CVE-2017-8549,CVE-2017-8619�
WhoamI �BugHunter@360VulcanTeam�
WhoamI �HardcoreACGOtaku
About360VulcanTeam�
ü SecurityResearchesfromQihu360
ü Pwn2OwnWinners:ü Pwn2Own2015IE11ü Pwn2Own2016Google
Chrome,AdobeFlashü Pwn2Own2017Edge,Safari,
AdobeFlash,Win10,MacOSXü “MasterofPwn”Pwn2Own
2017
Agenda�
• Out-of-MemoryExcep3oninBrowser• FromOut-of-MemorytoRCE• FromOut-of-MemorytoASLRbypass• Conclusion
OutofMemoryExcep3oninWebBrower
�
OutofMemoryExcep3on �“Run%meexcep%onwhenthereisnosufficientmemory.” �
BrowserOOMExample1�• IE8CA_rArrayUseAaerFree(cve-2014-1753)• Foundbyfuzzing
function f() { var size = 0x8000000; var s = "AAAA"; while (s.length < (size - 2)/2) s += s;
var x = document.createElement("frame"); x.setAttribute("prop2", s.substring(0, (size - 2)/2)); var node = document.body.appendChild(x);
for ( var y = 0; y < 20; ++ y ) { var z = node.cloneNode(); document.body.appendChild(z); }
}
CloneNode �
Clonea_ributesOnebyone �
CreateanewA_ributesArray�
CopyA9ributeFail?�
FreeA_ributesArray�
AccessA_ributesArray(UAF) �
Ifthea9ributetocopyisaverylargestring,itcancauseanOut-of-MemorywhencloneThea9ribute.FinallytrigtheuseaHerfree.�
BrowserOOMExample2�• IEJscirptJSON.parseOOMMemoryCorrup3on �
var chunksize = 0x2000000; var json = '[' for ( var i = 0; i < chunksize/0x10; ++ i ) json += '1,' json += '1]'; var arr = new Array(); try {
for ( var i = 0; i < 0x1000; ++ i ) arr.push(json.substr(0, (chunksize-2)/2));
} catch (e) {} // Force IE into low-memory state by allocating large amount of memory
while (true) { JSON.parse(json); } �
jscript!JSONParser::ParseObject+0x3fb: 65b4e9da mov eax,dword ptr [esi+14h] // length of the json array 65b4e9dd mov dword ptr [esp+14h],eax 65b4e9e1 shl eax,4 // alloc size = arr_length * 0x10 65b4e9e4 push eax 65b4e9e5 mov dword ptr [esp+20h],eax 65b4e9e9 call dword ptr [jscript!_imp__malloc (65b740fc)] // malloc can fail if there is no sufficient memory // malloc fail is not checked and will directly copy content to address [NULL + arr_size] 65b4ea2fa5movsdwordptres:[edi],dwordptr[esi]es:002b:00777fc0=????????ds:002b:03eb8398=00000003 � �
OOMBugs–DifferentTypes �
• Handled/NotHandled• Controllable/Uncontrollable• 32-bits/64-bits• Con3nuable/NotCon3nuable
Handled/NotHandled �• HandledOOM– Developerisawareofpoten3alOOMinthecode– Butfailedtohandleitcorrectly(e.g.theIECA_rArrayUAFcase)
• NothandledOOM– Developerhasnoideaaboutthepoten3alOOMinthecode(e.g.theJSONcase)
– Cancauseunexpectedexecu3onpathchange(earlyreturn,excep3on),whichcancauseexploitablecondi3on
Controllable/Uncontrollable
• Controllable– WecantrigtheOOMexcep3onreliably,atany3mewewant• Controllablelargealloca3on• Controllablelow-memorystate
• Uncontrollable– Occursrandomly,notcontrolledbyus
• Smallalloca3ons• Uncontrollablelow-memorystate
32-bits/64-bits �
• Usually,it’seasiertofindreliableOOMin32-bitstargetsthan64-bits
• Becauseit’seasiertoforcetheprocessintoalow-memorystatein32-bitstarget– Bybruteforcealloca3ons
Con3nuable/NotCon3nuable�• Con3nuable– Programcancon3nuetoexecuteaaertheOOM– Exploitpossible
• NotCon3nuable– Programcannotcon3nuetoexecuteaaerOOM
• Crashimmediatelyduetonon-exploitablememorycorrup3on(e.g.nullpointerdeference)
• Crashac3velyforalloca3onsthatcannotfail• Browserhasamemorylimita3on,andwillcrashifmemoryexceedsthelimita3on
– Notexploitable,onlyDDOSL �
Find/Focusoncontrollableandcon,nuableOOMexcep3onsonly�
Forbughunters:�
FindOOMBugs �
• NormalFuzz– Fuzzwithrandomvaluessome3mestrigsOOM
• Fuzzinlowmemorystate– ToolssuchasApplica3onVerifier– Hookalloca3onAPIs– Somebrowsershastestinterfaceforout-of-mmemorysimula3on(e.g.FireFox)
• Codeaudi3ng�
FromOut-of-MemorytoRemoteCodeExecu3on
�
AJourneyWithOOMBugsinMicrosoaEdge�
• FindcontrollableOOMinEdge• Breakthetransac3onopera3onstomakeinconsistentarraystate
• Achievememorycorrup3on• Win �
FindcontrollableOOMinEdge�
• WeneedtofindOOMexcep3onswhichcouldbetriggedreliably
• JavaScriptarraysegmentalloca3onisanicevector
ArraySegment�• JavaScriptarrayinIE/Edge– Alinklistofarraysegments�
length �
head �
Array�HeadSegment�
lea�
length �
size�
next�
element[0]�
element[N]�
… �
Segment1�
lea�
length �
size�
next�
element[0]�
element[N]�
… �
SegmentN�… �
vararr=newArray();arr[0]=1;arr[1]=2;arr[2000]=3;arr[5000]=4;
�Headsegment �arr�
le.=0length=2element[0]=1element[1]=2��
Segment1�
Segment2�
le.=2000length=1element[0]=3�
le.=5000length=1element[0]=4�
ArraySegment(.cont) �
• JavaScriptarrayelementsarestoredinsegments– Whenalloca3nganarraysegment,italsoallocatesthememoryspaceforelementsinit
• Whenaddanewelementintoarray,ifthereisnoexis3ngspaceforit,iteitherallocatesnewsegmentorenlargesexis3ngsegment�
ArraySegmentAlloca3onOOMin32-bitsEdge�
• Whenalloca3ngarraysegment,ifthere’snosufficientmemory,anOOMexcep3onwillbethrown
try{for(vari=0x10000000;i<0x18000000;++i)
a[i]=0x0d0d0d0d;}catch(e){}try{
while(true){arr_ab.push(newArrayBuffer(0x02c9dbec*4));//Step1:Make
browserintoalow-memorystatebyalloca,nglargememoy}
}catch(e){}try{
a.reverse();//Step2:Trigarraysegmentalloca,on tothrowOOMexcep,on
}catch(e){alert(e);} �
TrigOOMin32-bitsEdge-Example�
TrigOOMin32-bitsEdge–Example(.cont) �
ArraySegmentAlloca3onOOMin64-bitsEdge�
• Wearenotabletoforce64-bitsEdgeintoinsufficientmemorystate– Because64-bitsprocess’smemoryspaceislarge
• Thereiss,llachancetotrigOOMexcep3onin64-bitsEdge– Ifthereisanoverflowinalloca,onsize
OOMWhenAllocateSizeOverflow�
varaaa=newArray();for(vari=0;i<arrSize.length;++i){
aaa.unshia.apply(aaa,args);//Step1:Growarraysegmentsizeun,lnearlyoverflow
aaa[arrSize[i]-1]=1;} try{
aaa.unshia.apply(aaa,args);//Step2:Triganothersegmentsizegrowwherethesizewilloverflow}catch(e){//Error:OutofMemory}
TrigOOMin64-bitsEdge-Example�
TrigOOMin64-bitsEdge–Example(.cont) �
NowwehavecontrollableOOMinboth32/64-bitsEdge,what’sNext? �
ArrayTransac3onOpera3on �
• Arraycontainssomeimportfields– Array:arraytype,length,head,cachedlast-usedsegment
– Segment:lea,length,size,elements
• Whenyouchangeoneofthefields,youmustalsochangesomeotherstokeepthearrayvalid– E.g.WhenyouconvertanIntarraytoFloatarray,theintelementsinthesegmentsneedalsobechangedtofloats
ArrayTransac3onOpera3on(.cont) �
• ManyJavaScriptarrayAPIswillmakechangetothearray– shia,unshia,splice,…– Thecorepartofsuchcoderequiresatomicityandconsistency
– Justliketransac3onindatabase,sowecallthemarraytransac3onopera3ons
– Breakarraytransac3onopera3onscancausetrouble
ArrayTransac3onOpera3on-Example�
• ConvertaNa3veIntarraytoNa3veFloatarray �
Foreachsegmentinarray:segment->ChangeElementsToFloat()
SetArrayType(NaDveFloatArray)�
Ifthecodereturnsunexpectedlyinthemiddleoftheitera%on,
youwillgetaNa%veIntArraywithsomeFloatsegments
BreakArrayTransac3onOpera3on �
• Callbackinthetransac3onopera3on– Commonpa_ernofEdgebugs
• Excep3onthatbreakscodeflow– OutofMemoryExcep3onsJ
Let’sParty!�
RCECase:Array.unshia�
nElemntsToUnshiG=NumberofElementstoUnshiGForeachsegmentinarray:
segment->leG+=nElemntsToUnshiG (1)SetUnshiGElementsToHeadSegment(); (2)Array->length+=nElemntsToUnshiG; (3)�
Transac3onOpera3onofArray.unshia�
nElemntsToUnshiG=NumberofElementstoUnshiGForeachsegmentinarray:
segment->leG+=nElemntsToUnshiG (1)SetUnshiGElementsToHeadSegment(); (2)Array->length+=nElemntsToUnshiG; (3)�
Break�
Setelementstotheheadsegmentcancauserealloca%onoftheheadsegment,whichcanthrowOut-of-Memoryexcep%on�
RCECase:Array.unshia�
• BybreakingtheArray.unshiatransac3onopera3on– Wehaveaninconsistentarraythat:
array->lastSegment->lea+array->lastSegment->length
>array->length
– Whilethearraymanipula3ngcodeassumesthat:array->lastSegment->lea+array->lastSegment->length
<=array->length
RCECase:Array.unshia�
• CallArray.unshiaagainontheinconsistentarray,wecangetanarraythat:arr->lastSegment->length>arr->lastSegment->size
• Anarraysegmentwhoselengthislargerthansizecancauseheapoverflowinmanyplaces– SuchasJavaScriptArray::DirectSetElementAt
RCECase:Array.unshia�
• Aheapoverflowinarraysegmentisquitesimpletoexploit
• Justallocateanotherarraysegmentaaertheoverflowedsegment,thenwecanoverwritethelengthandsizefiledofnextsegment
lea� length � size�
Overflow �
0xffffffff� 0xffffffff�
OverflowedSegment �
NextSegment �
RCECase:Array.unshia�
• UsedtoexploitEdgeatPwn2Own2017• FixedasCVE-2017-0238�
Demo �
RCECase:JavascriptNa3veIntArray::ToNa3veFloatArray�
• Thisfunc3onconvertsanintarraytofloatarray�
Foreachsegmentinarray:seg->size>>=1 (1)if(seg->length>(seg->size>>=1)) seg=AllocateNewSegment (2) Seg->ChangeElementToFloat() (3)Adjust(seg->length) (4)
�
Transac3onOpera3onofJavascriptNa3veIntArray::ToNa3veFloatArray�
Foreachsegmentinarray:seg->size>>=1 (1)if(seg->length>(seg->size>>=1)) seg=AllocateNewSegment (2) Seg->ChangeElementToFloat() (3)Adjust(seg->length) (4)
�
Break�
AllocateanewarraysegmentcanthrowOut-of-Memoryexcep%on.Atthispoint,seg->sizehasbeendivededby2,whileseg->lengthremainsunchanged.�
• BybreakingtheToNa3veFloatArraytransac3onopera3on– WecanhaveanarraysegmentthatSeg->length>Seg->size
• GetRCEexactlythesamewayasArray.unshia– OneofthebackupbugsforPwn2Own2017– Wepreparedseveralsimilarbugsasbackups(e.g.Array.splice)
RCECase:JavascriptNa3veIntArray::ToNa3veFloatArray�
PatchTime–AprilFix�
• MicrosoafixedourPwn2OwnbuginApril• Thefixisali_lesurprise– ItdoesnotfixtheOOMexcep3ons– Insteadittriestobreaktheexploittechweused
• Addedanewfunc3on“CheckLengthVsSize”• Toavoidheapoverflowscausedby“segment->length>segment->size” �
CheckLengthVsSize�Ifdetected“segment->length>segment->size”,crashtheprocessimmediately�
ProblemofTheAprilFix�
• ItbreakssomeOOMexploitsinourhand• Buttherootcauses3llnotfixed– Rootcause:OOMexcep3onbreaksarraytransac3onopera3on
• AndthereareotherOOMvulnerabili3esthatdonotrequire“seg->length>seg->size”toexploit�
Let’sCon3nueParty�
OOMbugscans3llfightinthenext10months�
OOMバグはあと10ヶ月は戦える! �
RCECase:Array.reverseSegmentUseAaerFree�
Reversethewholesegmentlist (1)Ifheadisleafsegment:
head=ReallocateNonLeafSegment (2)�
Transac3onOpera3onofArray.reverse�
LeafSegment�
• Leafsegment– Puredatasegment– NextSegmentwillNOTbescannedwhenGC
• Nonleafsegment– NextSegmentwillbescannedwhenGC
NonLeaf � NonLeaf � Leaf �
Leaf � NonLeaf � NonLeaf �
ü�
X �Thelast2segmentswillnotbescannedbyGCandwillbefreedunexpectedly,causinguseaaerfree��
RCECase:Array.reverseSegmentUseAaerFree�
NonLeaf �
Step1:Reversethesegmentlist�
Step2:Reallocateheadifit’sleafsegment�
NonLeaf � Leaf �
Leaf � NonLeaf � NonLeaf �
NonLeaf � NonLeaf � NonLeaf �
Break�
Reallocatenonleafsegmentmaycauseout-of-memoryexcep%on �
RCECase:Array.reverseSegmentUseAaerFree�
• BybreakingtheArray.reversetransac3onopera3on,wecanaccessanarraysegmentthathasalreadybefreed
• Easytogetfullremotecodeexecu3on– Reusethefreedmemoryofthearraysegment– Getafakearraysegment(achieveOOMaccess,typeconfusion,…) �
RCECase:Array.reverseSegmentUseAaerFree�
• FixedasCVE-2017-8549inJuneCPU
• Got$15,000fromedgebugbounty– ManythankstoMicrosoaJ �
RCECase:ConvertToVarArrayBufferOverflow�
• JavascriptNa3veFloatArray::ConvertToVarArraybufferoverflow �
Foreachsegmentinarray:ifsegisleafsegment: seg=ReallocateNonLeafSegment()(1)
seg->size*=2 (2)seg>ChangeIntElementsToVar() (3)
Array->ChangeTypeToVarArray() (4)�
Seg->size*=2? �
• In32-bitsedge,sizeof(Var)=4,sizeof(Float)=8• Sowhenconver3ngfloatsegmenttovarsegment,wecandoublethecapacity(size)ofthesegment �
Float� Float�
var� var� var� var�
Foreachsegmentinarray:ifsegisleafsegment: seg=ReallocateNonLeafSegment()(1)
seg->size*=2 (2)seg>ChangeIntElementsToVar() (3)
Array->ChangeTypeToVarArray() (4)�
Break�
Reallocatenonleafsegmentmaycauseout-of-memoryExcep%on.Ifwebreakat(1)inthemiddleoftheitera%on,somesegments’sizewillalreadybedoubled,andthedoubledsizewillnotberestored. �
RCECase:ConvertToVarArrayTypeConfusion �
• Bybreakingthetransac3onopera3oninJavascriptNa3veFloatArray::ConvertToVarArray– Wegetafloatarray,withsomedouble-sizedsegments
• Wecandirectlyread/writeoutoftheboundsofthesegments– Findamonkeytofinishtheexploit�
PatchTime(Again) �
• FinallyMicrosoastartstofixtherootcause– Probablybecausewecon3nuestoreportOOMbugsaaerPwn2Own
• Thefix– CrashtheprocesswhendetectedOOMexcep3onincertainfunc3ons
AutoDisableInterrupt�• Aclassforprotec3ngaregionofcode• Crashestheprocessiftheprotectedcoderegionthrowsanyexcep3on
• Solvedtherootcause– Addedtomanyimportfunc3onssuchasunshia,splice,arrayconversions,…
– Maybeforgetsomefunc3on?
RCECase:Array.reverse(Again) �
• AaertheAutoDisableInterruptpatch– Array.reverseisnotprotectedbyit
• CVE-2017-8619fixedtheOOMsegmentUAFissuewereported
• ThenwefoundanotherOOMissueinthesamefunc3on– CVE-2017-8753– TypeconfusioncausedbyinvalidlastUsedSegment
lastUsedSegment�
• JavaScriptarraywillcachethelastusedarraysegment,tospeeduparrayaccess
• Sowhenasegmentisremovedfromthearray,thelastUsedSegmentmustalsogetupdated,otherwiseitwillcausetrouble�
RCECase:Array.reverse(Again) �lastUsedSegment=head; (1)head=AllocateNewHead(); (2)Ifthelastsegmentisleaf:
ReallocateLastSegmentToNonLeaf();(3)lastUsedSegment=head; (4)�
Break�
Reallocatenonleafsegmentmaycauseout-of-memoryExcep%on.Ifwebreakat(3),thearraywillhavealastUsedSegmentpointstoaninvalidatedsegment �
ExploitanInvalidatedlastUsedSegment�
• Specialthankstoourteammember@LiuLongforthemethodtoexploitsuchbugs
• Exploitmethod– Changethetypeofthearray(e.g.IntArray->FloatArray)
– ThelastUsedSegmentwillnotgetupdatedwhenchangingarraytype
– ThenweaccesselmentinlastUsedSegment,wegettypeconfusion
AndItCon3nuous… �
• CVE-2017-8753fixedinSeptember• EndofOOMexploitinedge?• Let’scheckitoutJ• Demo3me,maybe
FromOut-of-MemorytoASLRBypass �
ExhaustMemoryin64-bitsEdgeBrowser? �
• Usuallyyouarenotabletodothis– Thebrowser(andthewholesystem)willgetsloworfreezebeforeyouusingupthememory
– Becauseyoucommi_edtoomuchmemory
• Un3lwefindaninteres3ngfeaturein64-bitsbrowser �
TheFastArrayBuffer��
• In64-bitsedge,whenyouallocateanarraybufferwhosesizeislargerthan0x10000(64KB),itwillbea“fastarraybuffer”– ab=newArrayBuffer(0x10000);//createavirtualarraybuffer
• Edgereserves0x100000000(4GB)bytesforeachfastarraybuffer �
4GBforeachbuffer? �
WhatDoesThatMean �
• Whenweallocatea64KBfastarraybuffer,therealcommi_edmemoryis64KB
• Butedgewillreserve4GBvirtualmemoryspaceforit
• Sowecanoccupy4GBmemorybyjustcommi�ng64KBmemory
Commit64KB,reserve4GB�
UserModeMemorySpacein64-bitsEdge�
• Windows10uses48-bitsforusermodememory
• Userheapaddresswillbealwayslessthan0x800000000000
• Soweonlyneedtospraylessthan0x8000arraybufferstoexhaustusermodememoryspace�
5LinestoExhaustMemoryin64-bitsEdge�
vararr=newArray(0x10000/2);try{ for(i=0;i<arr.length;i++) arr[i]=newArrayBuffer(0x10000);}catch(e){//outofmemoryexcep3on} �
�
AttheEndofSpray�
rcx=0000000800000000 �rcx=0000000700000000 �rcx=0000000600000000 �rcx=0000000500000000 �rcx=0000000400000000 �rcx=0000000300000000 �rcx=0000000200000000 �rcx=0000000100000000 ��
Tooursurprise,whenwefinishedsprayingmostofthearraybuffers,itbeginstoallocatememoryatverypredictableaddresses�
GetFixedContentatFixedAddress�
• Aaerwefinishedthespray,weknowoneofthefastarraybufferswillbeallocatedatafixedaddress(e.g.0x200000000)
• Ifwefreethatarraybuffer,andspraysomeinteres3ngobjects(e.g.JavaScriptarray),weknowtheseobjectswillbeallocatedatthatfixedaddress,thuswebypassedASLR
0x10000000 �
0x20000000 �
0x30000000 �
… �
FastArrayBuffer�
FastArrayBuffer�
FastArrayBuffer�
FastArrayBuffer�
FastArrayBuffer�
0�
sprayobjects �
Demo �
Effect�
• Wecanputcontrolledcontentatcontrolledaddressin64-bitsedge
• Makesexploita3onofcertainbugseasier– Write-to-anyonce– Useaaerfree– Typeconfusion
Limita3on �
• Ittakestoolongtofinishthespray– ~300secondsonmylaptop
• Notsuitableforreala_ack• Niceop3ontobeusedincontestssuchasPwn2OwnJ
BeyondASLRBypass �
• Aaerweexhaustedthe64-bitsmemoryspace,wecanmakecontrollableOOMjustlikein32-bitsprocess
• OOMvulnerabili3esthatareonlyexploitablein32-bitsprocesscanbeexploitedin64-bitsnowifcombinedwiththisissue
Conclusion �
• Out-of-Memoryexcep3onsinbrowsersareoaenignoredbydevelopers/bughunters
• Itiss3llpossibletofindexploitableifwefocusoncontrollableonesinmodernbrowser
• Wes3llneedtotakeOOMissuesseriously�
Thankyou!�