代码审计-ThinkPHP3.2.3框架漏洞
1.阅读目录
web目录:thinkphp架构目录:
熟悉web目录结构,ThinkPHP框架目录结构
www WEB部署目录(或者子目录)
├─index.php 入口文件
├─README.md README文件
├─Application 应用目录
├─Public 资源文件目录
└─ThinkPHP 框架目录
├─ThinkPHP 框架系统目录(可以部署在非web目录下面)
│ ├─Common 核心公共函数目录
│ ├─Conf 核心配置目录
│ ├─Lang 核心语言包目录
│ ├─Library 框架类库目录
│ │ ├─Think 核心Think类库包目录
│ │ ├─Behavior 行为类库目录
│ │ ├─Org Org类库包目录
│ │ ├─Vendor 第三方类库目录
│ │ ├─ ... 更多类库目录
│ ├─Mode 框架应用模式目录
│ ├─Tpl 系统模板目录
│ ├─LICENSE.txt 框架授权协议文件
│ ├─logo.png 框架LOGO文件
│ ├─README.txt 框架README文件
│ └─ThinkPHP.php 框架入口文件
2.搭建项目环境
环境要求:
(1)PHP版本:PHP5.3以上版本(注意:PHP5.3dev版本和PHP6均不支持)
(2)支持的服务器和数据库环境
- 支持Windows/Unix服务器环境
- 可运行于包括Apache、IIS和nginx在内的多种WEB服务器和模式
- 支持Mysql、MsSQL、PgSQL、Sqlite、Oracle、Ibase、Mongo等多种数据库和连接
(3)Model.class.php:位于ThinkPHP\Library\Think\Model.class.php
作用:对数据的连贯操作,如where、order、select、limit等
(4)Driver.class.php:ThinkPHP\Library\Think\DB\Driver.class.php
作用:数据库条件分析、各种操作数据库,和安全相关的有excute,join,table;limit函数有强转
项目搭建:
1.新建数据库,添加表和字段
2.访问index.php,
此时Thinkphp目录会生成一个Application的目录
目录结构:
Application
├─Common 应用公共模块
│ ├─Common 应用公共函数目录
│ └─Conf 应用公共配置文件目录
├─Home 默认生成的Home模块
│ ├─Conf 模块配置文件目录
│ ├─Common 模块函数公共目录
│ ├─Controller 模块控制器目录
│ ├─Model 模块模型目录
│ └─View 模块视图文件目录
├─Runtime 运行时目录
│ ├─Cache 模版缓存目录
│ ├─Data 数据目录
│ ├─Logs 日志目录
│ └─Temp 缓存目录
3.修改 ApplicationHome/Controller/IndexController.class.php 文件代码,内容如下:
<?php namespace HomeController;use ThinkController;class IndexController extends Controller {
public function index(){
$condition["name"] = I("name");
$data["pass"] = "2000";
$result = M("users")->where($condition)->save($data);
}
}
4.配置连接数据库的文件 Application/Common/Conf/config.php ,内容如下:
<?php return array(
'DB_TYPE' => 'mysql', // 数据库类型
'DB_HOST' => 'localhost', // 服务器地址
'DB_NAME' => 'thinkphp', // 数据库名
'DB_USER' => 'root', // 用户名
'DB_PWD' => 'root', // 密码
'DB_PORT' => 3306, // 端口
'DB_PREFIX' => '', // 数据库表前缀
'DB_CHARSET'=> 'utf8', // 字符集
'DB_DEBUG' => TRUE, // 数据库调试模式 开启后可以记录SQL日志 3.2.3新增);
);
二,0x01 注入成因:
ThinkPHP/Application/Home/Controller/IndexController.class.php,添加如下代码
public function index2(){
// $data = M('user')-> where('username = "admin"')->select();
// dump($data);
$id = i('id');
$res = M('user')->find($id);
}
断点测试,没问题
thinkphp/ThinkPHP/Common/functions.php,判断提交方式
重点观察参数过滤处
think_filter函数,过滤了一些特殊字符,但是还是又部分字符没被过滤掉,如:updatexml,extractvalue报错函数
protected function parseSet($data) {
foreach ($data as $key=>$val){
if(is_array($val) && 'exp' == $val[0]){
$set[] = $this->parseKey($key).'='.$val[1];
}elseif(is_null($val)){
$set[] = $this->parseKey($key).'=NULL';
}elseif(is_scalar($val)) {// 过滤非标量数据
if(0===strpos($val,':') && in_array($val,array_keys($this->bind)) ){
$set[] = $this->parseKey($key).'='.$this->escapeString($val);
}else{
$name = count($this->bind);
$set[] = $this->parseKey($key).'=:'.$name;
$this->bindParam($name,$val);
}
}
}
return ' SET '.implode(',',$set);
}
parseSet函数,可以看到提交的字符被当做占位符
_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['where']也同样,看到parseWhere函数
\(whereStr = '';
if (is_string(\)where)) {
// 直接使用字符串条件
$whereStr = $where; //直接返回了,没有任何过滤
} else {
// 使用数组表达式
......
}
0x02 漏洞利用
index.php?m=Home&c=Index&a=index2&id[where]=1 and updatexml(1,concat(0x7e,user(),0x7e),1)--
发现无法加载数据库驱动,查阅资料,大致有两个原因。一个是目录的问题,一个是配置项缺少或者增加时有问题。
应该在Common目录下的conf目录下的config.php进行数据库的配置,不是在Home目录下的conf目录下的config.php