ThinkPHP6.0.13反序列化漏洞分析

最近有點閑下來了,不找點事干比較難受,打算找點漏洞分析一下,于是就打算看看tp的一些漏洞,thinkphp6.0.13是tp的最新版,八月份有師傅提交了一個issue指出tp存在反序列化問題,網上也有些師傅分析了一波,不過斷點下的比較多,而且部分方法沒有闡明其用途,所以我也嘗試詳細的分析一波。下面先給出poc

ThinkPHP6.0.13反序列化漏洞分析

分析

首先看看POC的起始點

ThinkPHP6.0.13反序列化漏洞分析

立即學習PHP免費學習筆記(深入)”;

ThinkPHP6.0.13反序列化漏洞分析

發現起始點在Psr6Cache這個類,我們進入這個類,不過沒有發現__destruct或者__wakeup等常見的反序列化起始魔術方法,推測應該在其父類AbstractCache這個抽象類中。跟入AbstractCache類

ThinkPHP6.0.13反序列化漏洞分析

如圖,成功發現本次反序列化鏈子的起始類。這里我們可以控制autosave這個屬性為false,從而進入save方法。

回到Psr6Cache類查看這個方法

ThinkPHP6.0.13反序列化漏洞分析

可以發現,pool屬性和key屬性我們都可控。因此可能存在兩種路線,調用不同類的同名方法(getItem)。或者是直接嘗試觸發__call方法。我們來看看POC作者是怎么讓反序列化進行下去的。

ThinkPHP6.0.13反序列化漏洞分析

作者用構造方法傳入了exp,exp其實就是在實例化Channel類。我們進入Channel類查看

ThinkPHP6.0.13反序列化漏洞分析

Channel類中有一個__call方法,那么作者是選擇觸發__call來讓鏈子繼續下去。這個call方法接受了兩個參數,method是寫死的(getItem),parameters是可控的(即前面可控的key屬性)

ThinkPHP6.0.13反序列化漏洞分析

跟入log方法查看,其接受三個傳參(但是其實對后續的鏈子沒啥用),傳入record方法

ThinkPHP6.0.13反序列化漏洞分析

跟入record方法

ThinkPHP6.0.13反序列化漏洞分析

再返回查看作者的POC,發現其控制lazy屬性為false,讓函數進入最后一個if分支執行save方法

ThinkPHP6.0.13反序列化漏洞分析

那么save方法應該是比較關鍵的方法了,跟入save方法,這里面有三個可能被利用的點,作者選擇了哪一個呢?

ThinkPHP6.0.13反序列化漏洞分析

根據POC不難發現作者選擇了控制logger屬性,利用構造函數對其賦值,令其為Socket類的對象

ThinkPHP6.0.13反序列化漏洞分析

ThinkPHP6.0.13反序列化漏洞分析

在這個類中,我們找到了一個復雜的同名方法,其中有大量的操作。

ThinkPHP6.0.13反序列化漏洞分析

我們繼續來看作者是怎么構造的,作者控制config屬性,給其賦值為數組。數組有如下內容

ThinkPHP6.0.13反序列化漏洞分析

關鍵在于這兩個鍵值,作者控制config,讓程序運行到調用invoke方法的分支

ThinkPHP6.0.13反序列化漏洞分析

同時,app屬性可控,作者令app屬性為App類的對象,我們進入App類

ThinkPHP6.0.13反序列化漏洞分析

這里先看看App類的的exists方法的情況,在其父類中找到了這個方法

ThinkPHP6.0.13反序列化漏洞分析

繼續往后,這里對App類進行了唯一一個操作,控制了instances屬性的值。這里控制其值是為了進入Request類,并且執行url方法

ThinkPHP6.0.13反序列化漏洞分析

ThinkPHP6.0.13反序列化漏洞分析

作者在這里對Request類做出唯一的操縱,就是控制url屬性的值。可以看出,如果url屬性存在,那么就會進入第一個分支,其值等于本身。

ThinkPHP6.0.13反序列化漏洞分析

同時又注意到,complete我們之前傳入的是true。因此最終返回的結果就是$this->domain().$url,url我們已經控制了,那么domain方法返回什么呢?

ThinkPHP6.0.13反序列化漏洞分析

OK,這點我們就不用看了。分析了這么多,我們得到了$currentUri最后的值,就是:

http://localhost/

ThinkPHP6.0.13反序列化漏洞分析

currentUri作為一個數組被傳入invoke了,根據鏈子的長度,達到invoke,我們的反序列化之旅就快結束了

ThinkPHP6.0.13反序列化漏洞分析

查看invoke,App類找不到這個方法,在他的父類里找到了這個方法

ThinkPHP6.0.13反序列化漏洞分析

這里可以看到。這個函數內有三分支走向,那么最終會走向哪里呢?根據我們之前$config[‘format_head’]的傳入, 首先我們傳入的這個對象不是Closure的實例或者子類,并且也不滿足第二個分支的條件

ThinkPHP6.0.13反序列化漏洞分析

因此進入到到第三個分支。我們跟進invokeMethod()方法。這里傳入的$callabel就是[new thinkviewdriverPhp,’display’]、而$vars就是[‘http://localhost/’]

ThinkPHP6.0.13反序列化漏洞分析

注意,我們傳入的$method是數組,因此進入第一個分支。把new thinkviewdriverPhp (即對象)賦值給$class,’display’(即方法名)賦值給新的$method。

然后下面進行了一個判斷,如果$class是對象,那么其值就為它本身,因為我們傳入的是對象,所以這里沒什么變化。然后進入最關鍵的代碼

可以看到,把對象new thinkviewdriverPhp 以及方法 display傳入了ReflectionMethod。

ThinkPHP6.0.13反序列化漏洞分析

在最后,調用invokeArgs方法,傳入了new thinkviewdriverPhp對象,同時傳入了$args

ThinkPHP6.0.13反序列化漏洞分析

那么args是什么呢?

ThinkPHP6.0.13反序列化漏洞分析

我們跟入之后發現是一個處理函數,因為本人比較懶,而且到這都快分析完了,就不去硬讀了,直接給結論,總之我們傳入的 $vars ,也即 [‘http://localhost/’] 其中的關鍵部分保留了下來,并且進入到了后續的傳參中

ThinkPHP6.0.13反序列化漏洞分析

繼續往后看,對于這個函數(invokeArgs),可以簡單的類比call_user_func(),因此最后的關鍵代碼其實只有這兩行

ThinkPHP6.0.13反序列化漏洞分析

也即

$reflect?=?new?ReflectionMethod(new?thinkviewdriverPhp,’display’); return?$reflect->invokeArgs(new?thinkviewdriverPhp,’?’)

常看tp反序列化的朋友就知道,已經結束咧!畢竟調用display方法了。但是上述這個調用ReflectionMethod類的操作到底是什么呢?我們可以借助如下實例來演示。所以說這玩意和call_user_func很像

ThinkPHP6.0.13反序列化漏洞分析

最后是display方法,沒什么好說的了,content傳入display方法中,eval執行命令了

ThinkPHP6.0.13反序列化漏洞分析

結語

TP的鏈子一如既往的有意思(以及復雜),特別是最后的ReflectionMethod類的用法上,如果不了解這個類以及類中的方法組合可以實現類似call_user_func函數的作用的話,那么就很容易錯過這樣一個精彩的漏洞。

【相關教程推薦:thinkphp框架

? 版權聲明
THE END
喜歡就支持一下吧
點贊13 分享