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