潛藏在PHP安全的邊緣——淺談PHP反序列化漏洞

Gcow安全團隊發表於2020-07-30

潛藏在PHP安全的邊緣——淺談PHP反序列化漏洞

注意事項:
1.本篇文章由複眼小組的瞳話原創,未經允許禁止轉載
2.本文一共1376字,8張圖,預計閱讀時間6分鐘
3.本文比較基礎,請大佬酌情觀看,如果有其他的騷思路,歡迎和我們交流

0x00.前言

最近在研究程式碼審計漏洞的時候,特別注意了一下PHP反序列化漏洞,該漏洞又被成為PHP物件注入,個人感覺比較有意思,所以透過通俗易懂的語言來簡單說一下PHP反序列的安全問題。

0x01.理解序列化與反序列化

百度說明:

 

圖片1-百度百科序列化

 

聽起來似乎似懂非懂,那麼用通俗的語言講,就是將物件裡面的資訊改變成可以傳輸的格式,說到這裡是不是突然想起來json傳輸格式了呢,其實道理是一樣的,接下來請看演示的demo。

 

這裡我定義了一個物件,並填寫了兩個類屬性和一個類方法,並在程式碼第9行例項化成了一個物件,第10行使用serialize函式進行序列化操作,並輸出

 

圖片2-序列化演示demo

 

檢視執行結果之後,可以發現,這就是序列化之後的格式

 

解讀一下分別是什麼意思:O是物件的意思中間用:進行分割,6的意思是物件的name值為6個位元組,往下看,2的意思是有兩個類屬性花括號類屬性的內容S類屬性9類屬性name的長度,依次往下類推

 

值得一提的是,類方法並不會參與到例項化裡面

 

圖片3-序列化顯示例項

 

明白了序列化操作,那麼來理解一下什麼是反序列化,顧名思義,就是將序列化之後的資訊再反回去

 

這裡接收一個ser的引數,然後使用反序列化函式unserialize進行反序列化處理,最後使用var_dump進行輸出。

 

圖片4-反序列化演示demo

 

可以看到將我們輸入的序列化後的資訊又變回了一個物件,這就是反序列化操作

 

圖片5-反序列化顯示例項

0x02.漏洞產生

在程式碼審計中只要記住這一點,程式碼審計就已經入門了,任何輸入都是有害的。
試想一下,既然被稱為PHP物件注入,那麼是怎麼注入進去的呢?必須要有一個輸入點,比如SQL隱碼攻擊,是因為在資料庫互動的時候前端可控,導致的漏洞,那麼物件注入,也要有一個輸入點,所以我在下面寫了一個demo,再次之前我們需要了解一下PHP中常見的魔法函式(又稱魔術方法).

•    __construct()當一個物件建立被時被呼叫
•    __destruct()當一個物件銷燬時被呼叫
•    __toString()當一個物件被當作一個字串使用
•    __sleep()在物件在被序列化之前執行
•   __wakeup在將在反序列化之後立即被呼叫

為什麼被稱為魔法函式呢,因為是在觸發了某個事件之前或之後,魔法函式會自動呼叫執行,而其他的普通函式必須手動呼叫才可以執行,PHP中魔法函式的開頭都是以__(雙下劃線)為字首,所以在定義函式的時候儘量不要使用雙下劃線為字首

 

接下來看demo,這裡定義了一個類,並在類裡面新增了一個屬性和魔術方法,該模式方法的觸發條件為物件被反序列化之後立即呼叫,也正是這個方法導致了PHP反序列化漏洞的產生

 

圖片6-反序列化漏洞demo

 

先看到第5行的程式碼,有一個危險函式system,system裡面是可控的,但是如果我們直接去輸入一個系統命令是不能呼叫的,那麼我們可以看到第9行有一個反序列的函式,並且函式內部也是可控的,那麼我們就可以搞一些事情了。

 

php生成序列化之後的資訊

 

圖片7-php生成序列化資訊

 

序列化之後的資訊為:

O:4:"Test":1:{s:3:"cmd";s:6:"whoami";}

然後,後面輸入要執行的命令,前面的數值也要因命令的長度進行改變,這樣就可以執行任意命令。如圖後面的指令為ls,前面的數值變為"ls"的長度為2

 

圖片8-利用反序列化漏洞

0x03 原理解讀

首先要說的是,危險函式被帶入到魔法函式里面,這是一種非常危險的行為,然後system函式里面執行內容可控,引用了cmd這個類屬性,從下面進行了例項化之後,然後進行反序列操作,因為反序列化函式里面的內容是可控的,那麼我們可以輸入序列化之後的資料,並改變資訊中cmd的值(也就是whoami),原有的$cmd的值被我們新輸入的值覆蓋掉,並且執行完這些操作之後,魔法函式就像一顆定時炸彈,直接引爆,導致了命令執行,這就是為什麼會被成為物件注入的原因。

0x04 漏洞修復

  1. 在使用者可輸入的地方進行過濾,或新增白名單驗證,
  2. 應當避免危險函式帶入到魔法函式里面
  3. 在情況允許的情況下不要讓反序列函式可以被使用者輸入控制,儘量寫死

相關文章