潛藏在PHP安全的邊緣——淺談PHP反序列化漏洞
注意事項:
1.本篇文章由複眼小組的瞳話原創,未經允許禁止轉載
2.本文一共1376字,8張圖,預計閱讀時間6分鐘
3.本文比較基礎,請大佬酌情觀看,如果有其他的騷思路,歡迎和我們交流
0x00.前言
最近在研究程式碼審計漏洞的時候,特別注意了一下PHP反序列化漏洞,該漏洞又被成為PHP物件注入,個人感覺比較有意思,所以透過通俗易懂的語言來簡單說一下PHP反序列的安全問題。
0x01.理解序列化與反序列化
百度說明:
聽起來似乎似懂非懂,那麼用通俗的語言講,就是將物件裡面的資訊改變成可以傳輸的格式,說到這裡是不是突然想起來json
傳輸格式了呢,其實道理是一樣的,接下來請看演示的demo。
這裡我定義了一個物件,並填寫了兩個類屬性和一個類方法,並在程式碼第9行例項化成了一個物件,第10行使用serialize
函式進行序列化操作,並輸出
檢視執行結果之後,可以發現,這就是序列化之後的格式
解讀一下分別是什麼意思:O是物件的意思中間用:進行分割,6的意思是物件的name
值為6
個位元組,往下看,2
的意思是有兩個類屬性,花括號
是類屬性的內容,S
是類屬性,9
是類屬性name的長度,依次往下類推
值得一提的是,類方法並不會參與到例項化裡面
明白了序列化操作,那麼來理解一下什麼是反序列化,顧名思義,就是將序列化之後的資訊再反回去。
這裡接收一個ser
的引數,然後使用反序列化函式unserialize
進行反序列化處理,最後使用var_dump
進行輸出。
可以看到將我們輸入的序列化後的資訊又變回了一個物件,這就是反序列化操作
0x02.漏洞產生
在程式碼審計中只要記住這一點,程式碼審計就已經入門了,任何輸入都是有害的。
試想一下,既然被稱為PHP物件注入,那麼是怎麼注入進去的呢?必須要有一個輸入點,比如SQL隱碼攻擊,是因為在資料庫互動的時候前端可控,導致的漏洞,那麼物件注入,也要有一個輸入點,所以我在下面寫了一個demo,再次之前我們需要了解一下PHP中常見的魔法函式(又稱魔術方法).
• __construct()當一個物件建立被時被呼叫
• __destruct()當一個物件銷燬時被呼叫
• __toString()當一個物件被當作一個字串使用
• __sleep()在物件在被序列化之前執行
• __wakeup在將在反序列化之後立即被呼叫
為什麼被稱為魔法函式呢,因為是在觸發了某個事件之前或之後,魔法函式會自動呼叫執行,而其他的普通函式必須手動呼叫才可以執行,PHP
中魔法函式的開頭都是以__(雙下劃線)為字首,所以在定義函式的時候儘量不要使用雙下劃線為字首。
接下來看demo
,這裡定義了一個類,並在類裡面新增了一個屬性和魔術方法,該模式方法的觸發條件為物件被反序列化之後立即呼叫,也正是這個方法導致了PHP
反序列化漏洞的產生
先看到第5
行的程式碼,有一個危險函式system,system裡面是可控的,但是如果我們直接去輸入一個系統命令是不能呼叫的,那麼我們可以看到第9行有一個反序列的函式,並且函式內部也是可控的,那麼我們就可以搞一些事情了。
用php
生成序列化之後的資訊
序列化之後的資訊為:
O:4:"Test":1:{s:3:"cmd";s:6:"whoami";}
然後,後面輸入要執行的命令,前面的數值也要因命令的長度進行改變,這樣就可以執行任意命令。如圖後面的指令為ls,前面的數值變為"ls"的長度為2
0x03 原理解讀
首先要說的是,危險函式被帶入到魔法函式里面,這是一種非常危險的行為,然後system
函式里面執行內容可控,引用了cmd
這個類屬性,從下面進行了例項化之後,然後進行反序列操作,因為反序列化函式里面的內容是可控的,那麼我們可以輸入序列化之後的資料,並改變資訊中cmd
的值(也就是whoami
),原有的$cmd
的值被我們新輸入的值覆蓋掉,並且執行完這些操作之後,魔法函式就像一顆定時炸彈,直接引爆,導致了命令執行,這就是為什麼會被成為物件注入的原因。
0x04 漏洞修復
- 在使用者可輸入的地方進行過濾,或新增白名單驗證,
- 應當避免危險函式帶入到魔法函式里面
- 在情況允許的情況下不要讓反序列函式可以被使用者輸入控制,儘量寫死