某开源商城是基于thinkphp3.2.3,此商城中的sql注入漏洞很多,这篇文章主要举几个比较典型的例子,讲讲对漏洞的分析和自己审计代码时的一些思路。
ThinkPHP的控制器是一个类,而操作则是控制器类的一个公共方法。
控制器类的命名方式:控制器名(驼峰命名法)+Controller
控制器文件的命名方式:类名+.class.php(类文件后缀)
首先通过getController
和getAction
两个方法定义CONTROLLER_NAME
和ACTION_NAME
的值
然后通过CONTROLLER_NAME
获取当前操作名
然后通过ACTION_NAME
获取类所在的路径并创建控制器实例
然后通过反射方法ReflectionMethod
获取这个对象下对应的方法, 并且后续没有进行权限判断,导致所有请求都可以通过未授权访问
最后执行对应的应用程序
在Modules/Home/Controller/ApiuserController.class.php
文件中的group_info
方法
$order_id通过get请求传入,接着直接拼接进sql语句中,由于没有对参数进行过滤,并且没有对传入的token
参数进行校验,直接拼接sql语句进行未授权执行。
payload:
在Modules/Home/Controller/VipcardController.class.php
文件中的get_vipgoods_list
方法
order_id通过get请求传入,然后拼接到where语句中,由于是字符串,$this->options['where']
的键就是_string
所以导致不会进入到下面的if判断语句中,_parseType
方法用来检测传入的类型,由于传入的字符串类型,所以也就不会去判断是否符合字段数据类型
最终拼接成sql语句进行执行
还有注意一点的是,非array传出的参数会被添加(),所以有时候无法闭合的时候可以尝试添加括号看看
payload:
http://127.0.0.1/index.php?s=Vipcard/get\_vipgoods\_list&gid=1 and updatexml(1,concat(0x7e,database(),0x7e),1)
在Modules/Home/Controller/ApiuserController.class.php
中的set_default_address方法
然后跟进where
和save
方法,然后在save方法中跟进_parseOptions
方法
在_parseOptions
方法中,由于这里$val
是一个数组,所以is_scalar
判断不是标量,也就不会进入_parseType
方法用来检测传入的类型
在where子单元分析函数parseWhereItem
中,$exp
去了array中的第一个值bind,然后在下面的判断语句中拼接$whereStr
最后形成下面的sql语句,但此时的sql语句中任存在:0
在execute
中,是将:0
替换为外部传进来的字符串,所以我们让我们的参数也等于0,这样就拼接了一个:0
,然后会通过strtr()
被替换为1
payload:
[http://127.0.0.1/index.php?s=Apiuser/set\_default\_address&id\[0\]=bind&id\[1\]=0%20and%20(updatexml(1,concat(0x7e,user(),0x7e),1](http://127.0.0.1/index.php?s=Apiuser/set%5C_default%5C_address&id%5C%5B0%5C%5D=bind&id%5C%5B1%5C%5D=0%20and%20(updatexml(1,concat(0x7e,user(),0x7e),1)))
tp框架采用MVC架构,即模型
(Model),视图
(View),控制器
(Controller)。他们大致的运行流程是用户与应用程序交互,向控制器
发送请求,然后控制器
收到请求分析用户请求,并调用响应的模型
操作,模型
操作访问数据库或其他数据源,对数据进行处理并返回给控制器
,控制器
接受模型返回的数据,并传递给视图
,最后视图
将数据呈现给用户。
在MVC架构下对于挖掘sql注入漏洞应该首先关注其模型,因为他是与数据库交互的地方,如果我们能发现其过滤不严导致直接将用户数据带入数据库中执行从而造成sql注入。
QUERY
方法对原生的SQL查询和执行操作示例代码:
$gid \= $_GPC['gid'];
$sql \= "select xxx from xxx where gid='$id';"
$result \= M()->query($sql);
这种写法如果不对传入的gid变量加以过滤,很容易造成sql注入。
我们可以通过正则M\(.*?\)->query\(.+?\)
搜索代码中使用query方法的代码,然后查看传入的参数是否可控.
示例代码:
$gid \= $_GPC['gid'];
$resultt \= M('xxx')->where( "pid = {$gid}" )->select();
我们可以通过正则M\(.*?\)->where\(((?!array).)*\)
搜索代码中使用where方法且不是通过array传参的代码,然后查看传入的参数是否可控.
示例代码:
$gid \= $_GPC['gid'];
$resultt \= M('xxx')->where( array('gid' \=> $gid) )->save();
当使用数据进行传参情况就不一样了
如果传参是通过array的话,会进入到下面的if判断语句中,会对传入值的类型进行判断,若判断为int类型,参数将直接传唤为int类型将丢失后面的payload,
就算传入的类型可以为字符型也还会对传入的参数关键字符进行转义
所以这里我们只能另辟蹊径,只能通过tp3.2.3框架的几个sql注入漏洞来利用
例如这里我们可以使用tp3.2.3框架的bind注入,漏洞所在的地方一般都需要update的操作
我们可以通过正则M\(.*?\)->where\(.+?\).>save\(.+?\)
搜索代码中使用where方法且不是通过array传参的代码,然后查看传入的参数是否可控.
另外tp3.2.3中还可以通过exp注入,原理和上面的bind注入差不多,但其条件参数必须是通过全局数组传参,而不是I()
函数,因为I()
函数会调用think_filter()
函数,其中过滤了exp
。在这一套cms中所有参数都是通过I()
函数传参的,所以无法使用exp注入。
79 篇文章
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!