背景
在逛exploit-db的时候,发现这个漏洞有点意思,就拿过来自己分析了下。Redaxo CMS是一套开源的Web门户内容管理系统(CMS),该系统支持自定义模块、插件扩展、项目备份等。
审计
根据报错信息可以定位到该文件,/redaxo-cms-5.0.0/redaxo/src/addons/mediapool/pages/media.php
,第582行-608行
|
|
也就是
|
|
这条sql语句存在问题了,那么关键看下 $where
变量是否可控,继续向上找,$where
中一共拼接了两个变量,media_name
和rex_file_category
,那么先看media_name
是从哪里来的,定位到/redaxo-cms-5.0.0/redaxo/src/addons/mediapool/pages/media.php
,第8行
|
|
接下来继续找到rex_request
函数的实现位置,查看对请求做了哪些封装,定位到/redaxo-cms-5.0.0/redaxo/src/core/functions/function_rex_globals.php
,第29行-37行
|
|
可以看到这个函数实际上是去调用了rex_request
类的request
方法,于是去找rex_request
类的实现代码去,定位到/redaxo-cms-5.0.0/redaxo/src/core/lib/request.php
,第38行-50行
|
|
这里又调用了rex_request
类自身的arrayKeyCast
方法,找到该方法的实现,第168行-194行
|
|
注意到传给arrayKeyCast
的$haystack
参数值是$_REQUEST
来的,$needle
参数值是media_name
,$vartype
参数值是string
,所以进入了
|
|
后直接调用了rex_type
类的cast
方法校验变量类型,找到cast
方法的实现代码,/redaxo-cms-5.0.0/redaxo/src/core/lib/util/type.php
|
|
在$vartype
为string
的时候,直接强制转换了下就break跳出,return了,全部过程没看到安全过滤函数的干预,所以是存在严重SQL注入问题的。
验证
访问
http://localhost:8888/redaxo-cms-5.0.0/redaxo/index.php?page=mediapool/media&rex_file_category=0
在该页面右侧搜索框搜索任意字符串,post数据中参数media_name
即为注入点,结合之前的分析,后端接收数据的方式为$_REQUEST
,那么将media_name
参数直接带到url后面以get方式请求也是可以被后端获取的。
http://localhost:8888/redaxo-cms-5.0.0/redaxo/index.php?page=mediapool/media&rex_file_category=0&media_name=abc%27)%20ORDER%20BY%2018+--+
order by 18
报错,那么列的数目就是17
,使用union select
语句把页面回显位置显示出来
可以看到回显位置有10,14,16
总结
这个漏洞比较有意思的地方来了,我留意到该cms选择的pdo
方式连接数据库,那么怎么会出这样的sql注入问题呢?说完就抽了自己一大嘴瓜子,为什么不会出这样的sql注入问题呢?毕竟代码是人写的,下面就分析下为什么造成了sql注入。
首先这个锅是程序员的,明明选择了pdo
方式连接数据库,但是写sql语句的时候却直接拼接了参数,而不是采用pdo的参数绑定或者prepare
方式,从而造成了注入问题。
定位到/redaxo-cms-5.0.0/redaxo/src/addons/mediapool/pages/media.php
,第600行-608行
|
|
看到$qry
变量拼接了$where
变量,而$where
里面又拼接了$media_name
变量,第586行
$where = "(f.filename LIKE '%" . $media_name . "%' OR f.title LIKE '%" . $media_name . "%')";
最后sql语句$qry变量传入了setQuery函数,找到setQuery函数的实现,定位到/redaxo-cms-5.0.0/redaxo/src/core/lib/sql/sql.php
|
|
由于没有传递$params
参数,所以if语句直接进入else逻辑,使用query
直接执行了sql语句中我们拼接的恶意代码。
到这里其实还没完,又惊奇的发现sqlmap
提供的payload
里面使用了多语句,这让我想到了php+mysql多语句执行,mysql本身从4.1
版本就已经开始支持多语句执行了,只是php自身限制了这种用法,虽然connect
的时候可以设置CLIENT_MULTI_STATEMENTS
,但是php在mysql_query/mysqli_query
函数实现的源码级别仍然会去除掉该设置。但pdo
方式连数据后默认是允许多语句执行的,但并不是完全没限制,因为pdo
方式操作数据库都是采用参数绑定预编译sql语句的,而这种绑定是在客户端执行的,这种情况是不需要多语句执行的。以前在zone里面有一篇帖子
“真正可以利用的多语句注射,只能是存在于利用PDO连接数据库,并直接使用exec或者query函数进行执行sql语句的地方”
由于恰巧该cms在执行sql语句的时候未严格执行pdo
标准使用参数绑定方式,而采用了直接拼接参数到sql语句,执行的时候直接使用了query
函数,因此造成了多语句执行。