漏洞介绍
WordPress是一个用PHP编写的免费开源内容管理系统,由于clean_query函数的校验不当,导致了可能通过插件或主题以某种方式从而触发SQL注入的情况。这已经在WordPress5.8.3中进行了修复。影响版本可以追溯到3.7.37。
漏洞范围
WordPress <= 5.8.2
靶场配置
docker-compose.yml
1 | version: '3.8' |
其中需要替换functions.php,或者在原来的functions.php的第642行加上
1 | function wp_query_test(){ |
漏洞原理
在分析整个漏洞之前,首先可以看一下如果想要触发该漏洞,漏洞代码应该是什么样子的。
1 | new WP_Query($_POST['query_vars']) |
这也就是说,传入WP_Query的参数如果可控的话,就可以利用该漏洞。接下来我们看看整个漏洞的利用调用链:
1 | WP_Query::__construct |
根据官方的修复代码,最后的漏洞点位于WP_Tax_Query的clean_query方法:

函数clean_query是从 调用的get_sql_for_clause。阅读该函数的代码会发现,该函数的工作是为 SQL 查询中的条件创建子句,具体来说,它的工作是处理接收到的数据,将这些数据组合成 SQL 查询中的条件。将其返回给父函数。所以我们可以控制这个函数的返回数据,也就是说我们可以控制SQL查询,进行SQL注入。

wordpress/wp-includes/class-wp-tax-query.php#L383-L483
回到函数clean_query,当这个改动没有做的时候,默认情况下$query['terms']只会删除in的值,然后再调用到$this->transform_query( $query, 'term_taxonomy_id' );。
为了避免下降if,它$query['taxonomy']需要为空或is_taxonomy_hierarchical返回false的值。

wordpress/wp-includes/class-wp-tax-query.php#L559
该函数transform_query将检查$query['field'] == $resulting_field,如果为真,则返回并且不做进一步处理,因此如果变量$query['field']为term_taxonomy_id,我们可以退出函数而不更改变量值$query['terms']。

wordpress/wp-includes/class-wp-tax-query.php#L598-L600
函数逃逸后,返回原位的代码流会调用clean_query函数get_sql_for_clause,变量的值$query['terms']会直接作为SQL查询条件,导致SQL注入。
利用链
1 | WP_Query#__construct |
主要参数$query只需要满足以下2个条件,就可以触发SQL注入漏洞:
$query['include_children']取值为false(或者is_taxonomy_hierarchical($query['taxonomy'])取值为false);$query['field']取值为term_taxonomy_id
虽然这是wordpress核心的bug,但是wordpress核心的复用方式并不能触发错误,在插件和主题中会自动转向错误方向。WP_Query当你要查询数据库时,插件/主题会调用该类,从源代码中识别错误的方法是在使用时WP_Query($data)和 $data 是可控的。
漏洞复现
对http://your-ip:8080/wp-admin/admin-ajax.php发送数据包
1 | POST /wp-admin/admin-ajax.php HTTP/1.1 |

使用github上的poc
1 | poc.exe -h your-ip:8080 |

参考
WordPress SQL 注入漏洞(CVE-2022-21661 分析与复现) - FreeBuf网络安全行业门户
【POC】CVE-2022–21661 WordPress核心框架WP_Query SQL注入漏洞-POC - komomon - 博客园 (cnblogs.com)
【漏洞分析】CVE-2022–21661 WordPress核心框架WP_Query SQL注入漏洞原理分析与复现 - komomon - 博客园 (cnblogs.com)