PHP審計之POP鏈挖掘
前言
續上文中的php反序列化,繼續來看,這個POP的挖掘思路。在其中一直構思基於AST去自動化挖掘POP鏈,迫於開發能力有限。沒有進展,隨後找到了一個別的師傅已經實現好的專案。
魔術方法
__wakeup() //使用unserialize時觸發
__sleep() //使用serialize時觸發
__destruct() //物件被銷燬時觸發
__call() //在物件上下文中呼叫不可訪問的方法時觸發
__callStatic() //在靜態上下文中呼叫不可訪問的方法時觸發
__get() //用於從不可訪問的屬性讀取資料
__set() //用於將資料寫入不可訪問的屬性
__isset() //在不可訪問的屬性上呼叫isset()或empty()觸發
__unset() //在不可訪問的屬性上使用unset()時觸發
__toString() //把類當作字串使用時觸發,file_exists()判斷也會觸發
__invoke() //當指令碼嘗試將物件呼叫為函式時觸發
__call
與__callstatic
現實情況下__call
的利用居多,該魔術方法觸發的條件是在物件上下文中呼叫不可訪問的方法時觸發。
呼叫流程如下:
$this->a() ==> 當前類a方法 ==> 父類a方法 ==> 當前類__call方法 ==> 父類__call方法
如果觸發__call
方法,那麼a,即方法名,會作為__call
的方法的第一個引數,而引數列表會作為__call
的方法第二個引數。
來看到程式碼
function __destruct(){
$this->a->b();
}
這裡有2個利用路徑,一個是$this->a
中構造一個存在方法的例項化類,另一種方式是找一個不存在b方法並且存在__call
方法的類,當b不存在時,即自動呼叫__call
。
__callstatic
方法只有在呼叫到靜態方法的時候才能觸發
__get
與__set
不存在該類變數或者不可訪問時,則會呼叫對應的__get
方法
$this->a ==> 當前類a變數 ==> 父類a變數 ==> 當前類__get方法 ==> 父類__get方法
__get
程式碼案例
function __destruct(){
echo $this->a;
}
呼叫不存在變數a,即會自動觸發__get
方法,
資料寫入不可訪問的變數或不存在的變數即呼叫__set
function __destruct(){
$this->a = 1;
}
__toString
把類當作字串使用時觸發
$this->_adapterName = $adapterName;
$adapterName = 'xxx' . $adapterName;
POP鏈挖掘
此前構思的自動化挖掘POP鏈的功能已經被其他師傅實現了,在此就不班門弄斧了,直接拿現成的來用。
思路其實就是尋找__destruct
方法,作為入口點,然後尋找一個回撥函式作為末端。而中間需要尋找各種中間鏈,將其串聯起來。串聯的方法基本上就是一些魔術方法和一些自定義的方法。
專案地址:https://github.com/LoRexxar/Kunlun-M
cp Kunlun_M/settings.py.bak Kunlun_M/settings.py
python kunlun.py init initialize
python kunlun.py config load
python kunlun.py plugin php_unserialize_chain_tools -t C:\kyxscms-1.2.7
結果:
[20:28:51] [PhpUnSerChain] New Source __destruct() in thinkphp#library#think#Process_php.Class-Process
[20:28:51] thinkphp#library#think#Process_php.Class-Process
newMethod Method-__destruct()
[20:28:51] thinkphp#library#think#Process_php.Class-Process.Method-__destruct
MethodCall Variable-$this->stop()
[20:28:51] thinkphp#library#think#Process_php.Class-Process.Method-stop
MethodCall Variable-$this->updateStatus('Constant-false',)
[20:28:51] thinkphp#library#think#Process_php.Class-Process.Method-updateStatus
MethodCall Variable-$this->readPipes('Variable-$blocking', '\ === Constant-DIRECTORY_SEPARATOR ? 627')
[20:28:51] thinkphp#library#think#Process_php.Class-Process.Method-readPipes
MethodCall Variable-$this->processPipes->readAndWrite('Variable-$blocking', 'Variable-$close')
[20:28:51] thinkphp#library#think#console#Output_php.Class-Output
newMethod Method-__call('$method', '$args')
[20:28:51] thinkphp#library#think#console#Output_php.Class-Output.Method-__call.If
FunctionCall call_user_func_array("Array-['Variable-$this', 'block']", 'Variable-$args')
[20:28:51] [PhpUnSerChain] UnSerChain is available.
這其實利用鏈就清晰了
Process->__destruct ==>Process->stop ==>Process->updateStatus ==> Process->readPipes ==> Output->readAndWrite ==> Output->__call==> call_user_func_array()
參考
如何自動化挖掘php反序列化鏈 - phpunserializechain誕生記
結尾
但該工具並沒有達到我個人的預期,因為該工具中只是使用__destruct
這單個方法作為反序列化的入口點。