前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >CVE-2022-21661:通过 WORDPRESS SQL 注入暴露数据库信息

CVE-2022-21661:通过 WORDPRESS SQL 注入暴露数据库信息

作者头像
Khan安全团队
发布2022-01-21 14:03:53
3.8K0
发布2022-01-21 14:03:53
举报
文章被收录于专栏:Khan安全团队Khan安全团队

今年 10 月,我们收到了来自 GiaoHangTietKiem JSC 的 ngocnb 和 khuyenn 的报告,涉及 WordPress 中的 SQL 注入漏洞。该漏洞可能允许攻击者暴露存储在连接数据库中的数据。此漏洞最近被解决为 CVE-2022-21661 ( ZDI-22-220 )。该博客涵盖了该错误的根本原因,并着眼于 WordPress 团队如何选择解决它。首先,这是一个演示该漏洞的快速视频:

漏洞

该漏洞发生在 WordPress Query ( WP_Query ) 类中。WP_Query对象用于对 WordPress 数据库执行自定义查询插件和主题使用此对象来创建他们的自定义帖子显示。 

当插件使用易受攻击的类时,就会出现该漏洞。一个这样的插件是Elementor Custom Skin 对于这篇文章,我们针对 WordPress 5.8.1 版和Elementor Custom Skin插件 3.1.3 版测试了该漏洞。

在这个插件中,易受攻击的WP_Query类在ajax-pagination.php的get_document_data方法中被利用:

public function get_document_data(){

global $wp_query;

...

$this->query['paged'] = $this->current_page; // we need current(next) page to be loaded

$this->query['post_status'] = 'publish';

$wp_query = new \WP_Query($this->query); // <------ [ZDI comment] $this->query contains user-supplied data

wp_reset_postdata();//this fixes some issues with some get_the_ID users.

if (is_archive()){

$post_id = $theme_id;

}

图1--wordpress/wp-content/plugins/ele-custom-skin/includes/ajax-pagination.php

当请求发送到wp-admin/admin-ajax.php并且操作参数是ecsload时,调用get_document_data方法。  

if ( is_user_logged_in() ) {

...

do_action( "wp_ajax_{$action}" );

} else {

...

/**

* Fires non-authenticated Ajax actions for logged-out users.

*

* The dynamic portion of the hook name, `$action`, refers

* to the name of the Ajax action callback being fired.

*

* @since 2.8.0

*/

do_action( "wp_ajax_nopriv_{$action}" ); //<------ [ZDI comment] this method is called

}

图 2 - wordpress/wp-admin/admin-ajax.php

admin-ajax.php页面检查请求是否由经过身份验证的用户发出。如果请求来自未经身份验证的用户,admin-ajax.php将调用未经身份验证的 Ajax 操作。在这里,请求是在没有身份验证的情况下发送的,因此会调用未经身份验证的 Ajax 操作,即wp_ajax_nopriv_ecsload

搜索字符串“wp_ajax_nopriv_ecsload”表明它是一个存在于ajax-pagination.php页面中的钩子名称:

public function init_ajax(){

//add_action( 'wp_footer',[$this,'get_document_data'],99);// debug line comment it

add_action( 'wp_ajax_ecsload', [$this,'get_document_data']);

add_action( 'wp_ajax_nopriv_ecsload', [$this,'get_document_data']); // <--- [ZDI comment] hook name mapping

}

图 3 - wordpress/wp-content/plugins/ele-custom-skin/includes/ajax-pagination.php

wp_ajax_nopriv_ecsload挂钩名称get_document_data回调函数。这意味着do_action方法调用get_document_data方法。

get_document_data方法创建一个WP_Query对象WP_Query对象的初始化调用以下get_posts方法:

public function get_posts() {

global $wpdb;

$this->parse_query();

...

// Taxonomies.

if ( ! $this->is_singular ) {

$this->parse_tax_query( $q );

// [ZDI comment] get_sql() returns the clauses of the SQL statement prepared using the user-input

$clauses = $this->tax_query->get_sql( $wpdb->posts, 'ID' ); // <----- calls

$join .= $clauses['join'];

$where .= $clauses['where'];

}

...

if ( $split_the_query ) {

// First get the IDs and then fill in the objects.

$this->request = "SELECT $found_rows $distinct {$wpdb->posts}.ID FROM {$wpdb->posts} $join WHERE 1=1 $where $groupby $orderby $limits";

/**

* Filters the Post IDs SQL request before sending.

*

* @since 3.4.0

*

* @param string $request The post ID request.

* @param WP_Query $query The WP_Query instance.

*/

$this->request = apply_filters( 'posts_request_ids', $this->request, $this );

$ids = $wpdb->get_col( $this->request ); //<---- [ZDI comment] SQL injection occurs here

if ( $ids ) {

$this->posts = $ids;

$this->set_found_posts( $q, $limits );

_prime_post_caches( $ids, $q['update_post_term_cache'], $q['update_post_meta_cache'] );

} else {

$this->posts = array();

}

图 4 - wordpress/wp-includes/class-wp-query.php

get_posts方法首先解析用户提供的参数。接下来,它调用get_sql方法,该方法最终调用get_sql_for_clause从用户提供的数据创建 SQL 语句的子句。get_sql_for_clause调用clean_query来验证用户提供的字符串。但是,如果分类参数为空且字段参数的值为字符串“term_taxonomy_id” ,则该方法无法验证术语参数。稍后在 SQL 语句中使用terms参数的值。

public function get_sql_for_clause( &$clause, $parent_query ) {

global $wpdb;

$sql = array(

'where' => array(),

'join' => array(),

);

$join = '';

$where = '';

$this->clean_query( $clause ); // [ZDI comment] calls the method to validate parameters

if ( is_wp_error( $clause ) ) {

return self::$no_results;

}

$terms = $clause['terms'];

$operator = strtoupper( $clause['operator'] );

...

}

$sql['join'][] = $join;

$sql['where'][] = $where;

return $sql;

}

...

private function clean_query( &$query ) {

if ( empty( $query['taxonomy'] ) ) {

if ( 'term_taxonomy_id' !== $query['field'] ) {

$query = new WP_Error( 'invalid_taxonomy', __( 'Invalid taxonomy.' ) );

return;

}

...

图 5 - wordpress/wp-includes/class-wp-tax-query.php

请注意,get_sql()返回的sql变量附加到 SQL SELECT 语句并使用从该方法返回的字符串进行组装。后来在get_posts方法中,这个查询是通过$wpdb->get_col()方法执行的,这里出现了SQL注入条件。WP_Tax_Query->get_sql()

此漏洞可被利用来读取 WordPress 数据库:

查看完整尺寸

图 6 - PoC 输出

补丁

解决 CVE-2022-21661 的补丁向terms参数添加了一些额外的检查,以帮助防止进一步的 SQL 注入发生。

查看完整尺寸

图 7 - wordpress/wp-includes/class-wp-tax-query.php 的 clean_query 方法

结论

对 WordPress 网站的主动攻击通常集中在可选插件上,而不是 WordPress 本身的核心。今年早些时候的情况就是这样,当时 Fancy Product Designer 插件中的一个错误被报告为受到主动攻击。同样, Contact Form 7中的文件上传漏洞插件也被检测为被趋势科技传感器利用。在这种情况下,错误通过插件暴露,但存在于 WordPress 本身中。虽然这是信息泄露而不是代码执行的问题,但暴露的数据可能对攻击者很有价值。在不久的将来,在主动攻击中看到这个错误并不会让我们感到惊讶。我们建议尽快应用补丁或采取其他补救措施。特别感谢 GiaoHangTietKiem JSC 的 ngocnb 和 khuyenn 向 ZDI 报告此事。

本文系转载,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文系转载前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
多因子身份认证
多因子身份认证(Multi-factor Authentication Service,MFAS)的目的是建立一个多层次的防御体系,通过结合两种或三种认证因子(基于记忆的/基于持有物的/基于生物特征的认证因子)验证访问者的身份,使系统或资源更加安全。攻击者即使破解单一因子(如口令、人脸),应用的安全依然可以得到保障。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档