湯青松 中公教育高級安全工程師
今天分享的PHP反序列化也是異樣安全,,每一年都會爆發(fā)反序列化的問題,,PHP反序列化的問題大家會很少聽到或者是沒有聽過,,有幾點(diǎn),,首先(英)影響的范圍比較廣泛,,PHP反序列化主要是在CMS或者是框架當(dāng)中去做,,影響范圍比較少,,它的門檻比較高,,關(guān)注的人也會比較少一些,,在去漏洞第一時間會挖一些(英),應(yīng)用掃描器掃描或者是常見的邏輯性的漏洞,,對于反序列化漏洞一般關(guān)注點(diǎn)相對來說比較少,,但反序列化漏洞有一個特點(diǎn),如果利用成功了,,它的危害往往是比較大的,,可以面面執(zhí)行,代碼注入等等,。
我今天分享相對來說比較偏一點(diǎn)的話題,,今天有點(diǎn),,首先認(rèn)識漏洞為什么會產(chǎn)生。第二個怎么利用它,。第三個拿一個實(shí)例講解一下,。
反序列化漏洞有一些同學(xué)不是太清楚,和反序列化有什么關(guān)系,,我相信很多人去淘寶買家具,,不會直接把家具給你運(yùn)過來,會把這些東西打包拆成一塊一塊,,序列化的過程也是一樣的,,會把一個“類”或者是“數(shù)組”變成這么一個串,反序列化會把這個串再反向操作一下,,再把它的變量復(fù)制過去,。
認(rèn)識漏洞,這個漏洞相對來說比較偏,,首先要知道PHP的對象,,模數(shù)方法,序列化,,反序列化,,序列化與魔術(shù)方法,以及數(shù)據(jù)結(jié)構(gòu),。
先看一個代碼,,這是一個非常簡單的小樣代碼,在代碼當(dāng)中能夠看到一個class,,這是對象,,各種語言基本上都差不多,運(yùn)行之后會執(zhí)行對象里面的某一個方法,,魔術(shù)方法,,它是在我們不去主動調(diào)用某一個方法自動去觸發(fā),在PHP反序列化漏洞當(dāng)中就利用了自動觸發(fā)的問題去造成問題,,面向?qū)ο缶幊陶Z言會執(zhí)行它的方法,,銷毀一個對象的時候,這些自動觸發(fā)的方法可以理解為魔術(shù)方法,。
這個代碼當(dāng)中就是加了下面幾個方法,,這三個方法,第一個方法這樣的對象會自動觸發(fā)它,。第二個就是結(jié)束會觸發(fā),。第三個把對象當(dāng)成錯的去觸發(fā),我們先看一下這個會自動觸發(fā)方法。
第二個是手動調(diào)用方法,,后面把對象作為字符串自動輸出,,最后程序要結(jié)束,這是運(yùn)行結(jié)果,,這是一個反序列化漏洞主要的關(guān)鍵點(diǎn),。
假設(shè)還是有一個類,會有兩個變量,,這里面會有兩個方法,,有了這樣一個對象,把對象兩個變量復(fù)制,,調(diào)用了一下這個方法,,這邊會輸出字符串,再把它進(jìn)行序列化操作,,輸出出來,,輸出的結(jié)果這個是程序當(dāng)中(英)的值,這是反序列化的數(shù)據(jù),,把反序列化的數(shù)據(jù)復(fù)制出來,再認(rèn)識一下反序列的操作,,這個代碼當(dāng)中同樣的代碼不變,,下面不去做這個方法,把它的序列化數(shù)據(jù)放在這里面,,反序列化操作一下,,得到的還是這樣的類,并且在序列化類的過程當(dāng)中會把變量給復(fù)制進(jìn)去,,這邊并沒有給進(jìn)行復(fù)制操作,,依然是零,這邊還是一個空的,,當(dāng)我們執(zhí)行這個方法我們輸出的結(jié)果這個是我們這邊的名字,,這是運(yùn)行結(jié)果。
反序列化操作會做兩個事情,,這邊有一個自定義的方法,,后邊都是魔術(shù)方法,在自動觸發(fā)的時候,,來認(rèn)識一下這幾個魔術(shù)方法,,序列化也會觸發(fā)這樣的方法,當(dāng)我們輸出序列化字符,,在這邊進(jìn)行反序列化操作的時候又會調(diào)用這樣的魔術(shù)方法,,在反序列化漏洞操作的時候,在迂回的不斷嘗試這幾個方法的過程,接下來看一下例子,,這是剛才代碼的運(yùn)行結(jié)果,,可以看到這邊的魔術(shù)方法都是自動執(zhí)行的,序列化的結(jié)構(gòu),,這里面定了一個數(shù)組,,數(shù)組當(dāng)中進(jìn)行了序列化操作,并且把序列化操作的數(shù)據(jù)給輸出出來了,,PHP序列化結(jié)構(gòu)是什么,,3是代表數(shù)組,有3個數(shù)據(jù),,ABC,,這邊有一個S1,就代表字符串,,1是它的長度,,內(nèi)容是A,大概是這樣的結(jié)構(gòu),,這邊還有一些特征,,我們一會兒用到對象的時候它可能是O,代表的是對象,。
接下來看一下怎么去利用的,,我們在這個代碼當(dāng)中有兩類,第一個類,,第二個類,,下面有一行代碼,這行代碼不在這兩個類當(dāng)中,,代碼是有漏洞的,,怎么利用,在執(zhí)行之后前端傳遞了一個(英),,通過(英)傳遞了一個數(shù)據(jù),,這里并沒有進(jìn)行安全過濾,就把它進(jìn)行到了反序列化操作,,這里就會觸發(fā)一個操作,,在這個類里會自動觸發(fā)這個魔術(shù)方法,又會調(diào)用自定義方法,,這個方法當(dāng)中又會調(diào)用這個對象,,這個對象當(dāng)前類并沒有這樣的對象,它是一個變量,,怎么做,?我們可以把變量復(fù)制為對象,可以把這樣的變量復(fù)制為這樣的對象,在這個對象里面又可以把PHP設(shè)置為我們想執(zhí)行的命令,,調(diào)用這樣的方法就執(zhí)行了相應(yīng)的命令,,看一下POC,第一步我們在這邊再寫一個POC的代碼,,讓程序識別,,在這個對象當(dāng)中把它進(jìn)行復(fù)制,這樣的變量在識別的時候會調(diào)用識別方法,,自動給變量復(fù)制,,復(fù)制的時候又把下一個對象放進(jìn)去,下一個對象在方法里面也放了一個(英),,這樣的值就是(英),,這個變量里面的數(shù)據(jù)就是一個對象,運(yùn)行出來之后就會輸出這樣的字符,,當(dāng)把這樣的字符放到前面代碼傳遞過去就會執(zhí)行相應(yīng)的操作,,把結(jié)果放進(jìn)去,因?yàn)闀?zhí)行(英),,再通過反序列化操作就會產(chǎn)生這樣的結(jié)果,。
我們看一個實(shí)例,這個實(shí)例在PHP里面比較流行的博客系統(tǒng),,之前爆發(fā)了一個代碼的漏洞,,這個漏洞通過反序列化操作的過程,分析一下漏洞的利用過程,,我們說到PHP反序列化操作,就是使用反序列化函數(shù),,把字符串放進(jìn)去,,當(dāng)這個函數(shù)去調(diào)用了,就可能產(chǎn)生反序列化的漏洞,,在這個博客當(dāng)中直接去搜索函數(shù),,在安裝的位置這個地方有這樣的一串代碼,這個代碼我們可以看到只做了三個事情,,首先從cookie取出數(shù)據(jù),,通過base64解碼后進(jìn)行反序列化操作,從前端傳遞出來數(shù)據(jù)沒有進(jìn)行安全過濾就放在反序列化操作,,就會產(chǎn)生安全問題,,有這樣的安全問題最主要是看它能不能利用,如果有一個漏洞,,但是這個漏洞并不能進(jìn)行利用,,那就說明沒有什么效果,反序列化操作的時候會調(diào)用特征方法,反序列化操作的時候會捋一個對象,,會調(diào)用反序列化操作方法,,會調(diào)用一個魔術(shù)方法,在進(jìn)行反序列化漏洞的時候有兩種思路,,第一個就是直接利用,,只要進(jìn)行反序列化操作必定會執(zhí)行這三個方法,這是反序列化的魔術(shù)方法以及對象結(jié)束的方法,,我們就可以搜索全系統(tǒng)當(dāng)中有哪些位置有這幾個方法,,看這三個方法有沒有對它當(dāng)前類里面的變量進(jìn)行處理,使用(英)處理當(dāng)前的變量或者是其他的方法,,如果有的話就會有問題,,反序列化操作是可以控制某一個類里面的變量數(shù)據(jù),在處理變量的時候就會產(chǎn)生相應(yīng)的問題,。
另外一個就是間接利用,,去了一個迷宮,去迷宮有兩種結(jié)果,,第一種我們直接找到出口,。另外一種我們并不能一下子找到出口我們會在里面繞,一直找到出口為止,,我們在直接利用的時候不能用,,就會對代碼進(jìn)行跟進(jìn),首先會排除這三個方法再去找其他可以間接利用的方法,,比如tostring,,(英)等方法。
回到這個代碼當(dāng)中,,這個代碼當(dāng)中按照直接利用思路會搜索魔術(shù)方法,,在全局系統(tǒng)里面去搜這幾個,在系統(tǒng)當(dāng)中就找到了這兩處位置有方法,,但是并沒有對變量進(jìn)行處理,,這個位置并不適用,直接利用法把它放棄了,,接下來就需要迂回的去看這個系統(tǒng)當(dāng)中還有沒有其他的安全問題,,間接利用我們除了直接利用的那三個魔術(shù)方法,跟進(jìn)代碼看它有沒有把一個對象進(jìn)行字符串操作,,比如他把一個對象進(jìn)行字符串操作的時候就會去觸發(fā)(英)方法,,也有可能在調(diào)用變量的時候調(diào)用到不可訪問的變量,就會觸發(fā)另外一個(英)的魔術(shù)方法,,我們接著對代碼進(jìn)行審計,,在剛才那串代碼的時候看到它下面又把反序列化數(shù)據(jù)了,,里面有兩個變量放在這里面,我們就根據(jù)這個方法里面的處理,,這里面?zhèn)鬟f了一個(英),,首先看這樣一個變量,它確實(shí)是把這樣的變量作為字符串處理,,假設(shè)我們把這樣的變量是作為對象,,并沒有進(jìn)行安全過濾,把它作為對象,,就會觸發(fā)(英)的魔術(shù)方法,,什么地方有(英)方法,只要在全局系統(tǒng)當(dāng)中搜索一下,,搜索的時候就搜索(英),,搜索有三處進(jìn)行(英)操作,雖然我們找到了這些方法,,但這些方法有沒有對變量進(jìn)行相應(yīng)的處理,,前面兩處是沒有進(jìn)行任何處理的,最后一處他是有對對象進(jìn)行處理,,我們繼續(xù)跟進(jìn),,處理的時候并沒有直接對某一個變量進(jìn)行安全的過濾或者是進(jìn)行相應(yīng)的轉(zhuǎn)換,它是又進(jìn)行了一次迂回,,就像我們?nèi)ッ詫m,,迷宮我們找到一個房間,這個房間發(fā)現(xiàn)并不是一個出口,,還有一條門就繼續(xù)跟進(jìn)這條門,,前面直接利用的三個方法就排除了,再加上(英)只找到當(dāng)前類可以利用的,,(英)也排除了,,還剩下哪幾個魔術(shù)方法可以利用,可能會有set,、isset這幾個魔術(shù)方法可以被利用,我們的(英)有沒有調(diào)用不可訪問的代碼,,繼續(xù)跟進(jìn),,這三行代碼需要注意,這三行代碼有什么樣的特點(diǎn),,首先調(diào)用了當(dāng)前變量類里面的變量,,反序列化操作是可以知道它的結(jié)構(gòu),把這樣的變量定義為數(shù)組,,里面某一個(英)值再給它一個對象,,并且這樣的對象是不存在這樣的值,。這樣的一個類還要滿足一個條件,這個類必須要有一個(英)魔術(shù)方法,,繼續(xù)接著去迂回,,搜索,在這些系統(tǒng)當(dāng)中有哪些get的魔術(shù)方法,,全局搜索之后又搜索出三個位置有g(shù)et的方法,,有兩處是沒有意義的,因?yàn)椴⒉荒苷{(diào)用當(dāng)前類里面的變量進(jìn)行處理,,因?yàn)椋ㄓⅲ┧?,我們可以看到它這邊又調(diào)用了一個get,把變量返回,,我們繼續(xù)跟進(jìn)get的方法,,跟進(jìn)之后,我們看看這個代碼,,在這個方法里面他有一個案子判斷一下當(dāng)前類里面的變量是否存在,,如果存在的時候就取里面的值,這個變量我們是可以控制的,,我們在反序列化操作的時候肯定會把變量設(shè)置成這樣的值,,我們是可以控制會走這樣的一行代碼,它上面還有一行代碼,,這行代碼會判斷當(dāng)前變量是數(shù)組還是字符串,,以及進(jìn)行相應(yīng)的過濾,這里做了一個安全過濾,,這里面就有一處問題了,,首先會判斷(英)有沒有(英),(英)是否存在,,我們是可以控制這樣類里面的方法值,,可以讓它找這里面的方法,使用哪些方法對某一個值進(jìn)行過濾,,既然(英)可以控制,,在(英)我們可以填入(英),繼續(xù)判斷這個值是數(shù)組還是對象,,使用不同的方法把(英)對值進(jìn)行過濾,,把這樣的(英)放進(jìn)去,(英)可以控制,,我們可以有一個任意代碼執(zhí)行的問題,,接著根據(jù)我們剛才的思路,我們構(gòu)造一個POC,,在構(gòu)造POC的時候,,主要有四點(diǎn),,首先我們可以看到取數(shù)據(jù)的位置取得(英)數(shù)據(jù),取得的是64的數(shù)據(jù),,POC就輸出一個64的數(shù)據(jù),,這是最外面的包裝,里面調(diào)用了兩個變量,,這里就給他兩個變量,,在前面有一個方法把某一個變量當(dāng)做字符串,為了讓它觸發(fā)(英)方法,,我們就給他放一個對象,,當(dāng)他把數(shù)據(jù)作為字符串處理就做成(英)方法,這個方法里面有什么,?這個是POC,,前面調(diào)用了一個不存在的方法,這個不存在的方法是有屬性的,,調(diào)用了這樣的方法我們又給他放入了相應(yīng)的對象,,這個對象就是(英)的對象,主要給它設(shè)置兩個值,,第一個設(shè)置了(英)值,,我們想執(zhí)行某一個函數(shù),是動態(tài)的,。第二個會獲取變量,,前面那個代碼當(dāng)中調(diào)用了變量,把相應(yīng)的值傳進(jìn)去,,結(jié)果就會產(chǎn)生這樣的效果,,就是(英)里面/1,就會被執(zhí)行出來,,也就是這樣的POC就構(gòu)造成功了,。
剛才復(fù)盤的時候,這邊取的是(英)64,,這邊輸出了64,,為了讓它觸發(fā)(英)方法,因?yàn)檫@個地方是把它作為字符串處理,,我們這個地方放入一個對象就可以調(diào)用這里面的方法,,在調(diào)用(英)方法這里面使用了構(gòu)造的魔術(shù)方法,這些本來應(yīng)該是字符串的數(shù)據(jù),,結(jié)果又把它放入了另外一個類,在調(diào)用這樣數(shù)據(jù)的時候,,結(jié)果發(fā)現(xiàn)這樣的類是找不到的,,這樣的類本來是可以找到的數(shù)據(jù),,結(jié)果忙入這樣的類,這樣的參數(shù)反而是私有方法,,找不到,,又會調(diào)用get方法,調(diào)用get方法又會觸發(fā)構(gòu)造方法,,環(huán)環(huán)相扣,,就執(zhí)行了(英),傳遞出了一個/1,。(速記)