CVE-2022–21661 WordPress核心框架WP_Query SQL注入漏洞原理分析与复现
漏洞信息
近日,WordPress官方发布了多个漏洞信息:
其中一处关于`WP_Query`的SQL注入漏洞值得关注,编号为CVE-2022–21661。
`WP_Query`是WordPress提供的一个用于处理复杂SQL查询的类,在WordPress核心框架和插件中使用范围非常广泛,用户可以通过`WP_Query`类完成数据库查询操作,方便构建WordPress的输出内容。
补丁对比
首先进行补丁对比,发现修改的文件不多,最关键的地方位于`class-wp-tax-query.php`文件的函数`clean_query`:
补丁中,对`$query['terms']`的赋值取决于`$query['field']`的取值。
触发点分析
下面分析一下`clean_query`函数:
第559行通过`array_unique`函数移除`$query['terms']`数组中的重复项后,首先进行了一个`if`判断:
if (is_taxonomy_hierarchical($query['taxonomy'])&&$query['include_children'])
可以通过控制`$query['taxonomy']`或者`$query['include_children']`的取值使得`if`判断不成立,这样就直接将`$query['terms']`带入了函数`transform_query`:
可以设置`$query['field']`的值为`term_taxonomy_id`,这样函数会直接返回,也就是说可以通过控制`$query`的取值来绕过`clean_query`函数的处理。下面查找一下`clean_query`函数被调用的情况:
只存在函数`get_sql_for_clause`这唯一的一个调用:
通过`clean_query`处理参数`$clause`的引用对象,然后赋值给`$terms`,当存在`IN`操作时,调用函数`implode`以`,`将数组`$terms`转换为字符串,继续往下走:
将`$terms`字符串拼接进入`$where`,并最终带入SQL查询语句。回顾`get_sql_for_clause`函数的处理过程,可以通过构造特殊`$query`,导致输入参数无过滤处理就直接带入SQL语句,导致出现SQL注入漏洞。
调用链分析
查找函数`get_sql_for_clause`的调用链:
可以构建一条从`WP_Query`构造函数出发,到达`get_sql_for_clause`函数的完整调用链条:
WP_Query#__construct WP_Query#query WP_Query#get_posts WP_Tax_Query#get_sql WP_Tax_Query#get_sql_clauses WP_Tax_Query#get_sql_for_query WP_Tax_Query#get_sql_for_clause
漏洞复现
通过前面分析,我们知道参数`$query`只需要满足以下2个条件,就可以触发SQL注入漏洞:
- `$query['include_children']`取值为`false`(或者`is_taxonomy_hierarchical($query['taxonomy'])`取值为`false`);
- `$query['field']`取值为`term_taxonomy_id`
分析WordPress核心框架本身的漏洞潜在触发点:
一共找到205处调用,但是比较遗憾的是未能在WordPress核心框架中直接找到满足漏洞触发前提条件的场景。WordPress插件数量众多,应该可以找到一些满足条件的漏洞触发点,有兴趣的小伙伴可以自行去跟踪研究。
为了方面验证漏洞,这里构建一个自定义URL页面模板。修改默认主题样式的`applications.php`,手动增加一个名为`wp_ajax_nopriv_test`的AJAX请求定义:
为了方便测试,开启DEBUG模式,然后构造请求进行发送:
修复方式
回顾前面的补丁对比,新版本`class-wp-tax-query.php`中的函数`clean_query`修改代码如下:
if ( 'slug' === $query['field'] || 'name' === $query['field'] ) { $query['terms'] = array_unique( (array) $query['terms'] );} else { $query['terms'] = wp_parse_id_list($query['terms']);}
通过函数`wp_parse_id_list`限制了`$query['terms']`数组元素类型必须是整数。
