【A9】CVE-2022-21661-WORDPRESS-SQL-注入

文摘   科技   2023-09-28 14:30   广东  

“A9 Team 甲方攻防团队,成员来自某证券、微步、青藤、长亭、安全狗等公司。成员能力涉及安全运营、威胁情报、攻防对抗、渗透测试、数据安全、安全产品开发等领域,持续分享安全运营和攻防的思考和实践。”



01

漏洞描述


由于 WP_Query 中的不当清理,可能存在通过以某种方式使用它的插件或主题进行 SQL 注入的情况。这已在 WordPress 版本 5.8.3 中进行了修补。受影响的旧版本也通过安全版本修复,可以追溯到 3.7.37。防止 这种攻击的最安全版本 是 WordPress 5.8.3。但是由于这种攻击的措辞——WordPress Core中的 SQL 注入 ——它很容易被误解。作者证实了这一点。这是一个粗略的翻译:


虽然 SQL 注入源自 WordPress Core 文件系统,但无法自行触发攻击。至少不在一个普通的 WordPress 网站上。因此,您需要查找主题和插件中的不一致之处。例如,如果主题或插件使用WP_Query调用数据库并将信息传递给它。


在官方提交的补丁中可以看到如下的最关键的地方位于class-wp-tax-query.php文件的函数clean_query:



补丁中,对$query['terms']的赋值取决于$query['field']的取值。


02

漏洞点分析

根据官方给的补丁可以知道问题出在clean_query()方法上,查看一下该方法

在get_sql_for_clause中调用了clean_query()方法来校验查询中的参数值,当满足547行的 $query['field'] == 'term_taxonomy_id' 时,会调用transform_query()方法,而在这个方法中若满足598行的 $query['field'] == $resulting_field 条件则直接返回,那么就没有做到任何校验。


调用链的WP_Query::get_sql_for_clause()调用了clean_query()方法。


clean_query()方法没有起到校验参数值的作用。返回到get_sql_for_clause()方法,可以看到409行$clause['terms']值在用逗号连接后,直接拼接到IN语句中,最终导致了SQL注入漏洞的产生。(这里可能会有疑惑那如果$operator不为IN呢?也没关系,因为其他的语句也是一样的,看代码就知道了)而后放入到下面的$sql数组中。


造成sql注入。

03

漏洞利用链

网上有很多关于这个利用链的分析了,在这里贴出来:

WP_Query::__construct()->

    WP_Query::query()-> //设置了类属性query_vars的值,并调用了get_posts()

        WP_Query::get_posts()-> //当查询的不是针对现有的某个帖子时,$this->is_singular为false,会调用到get_sql()方法。

            WP_Query::get_sql()->

                WP_Query::get_sql_clauses()->

                    WP_Query::get_sql_for_query()->

                        WP_Query::get_sql_for_clause()->

                            WP_Query::clean_query() //满足特定条件时,未对terms参数做过滤

起点位于WP_Query的构造函数中


可以看到我们需要带一个参数调用query)方法中,而这个方法是类实例化时传过来的参数,暂时先不理会这个。将这个称之为"假设可控参数"。跟进该函数。


将"假设可控参数赋"值给了$this->query和$this->query_vars。跟进到get_posts()中,该方法代码非常多。重点看到下面这个代码。


将刚刚"假设可控变量"的值赋值了$q以后,进入1790行q)进行其余的查询条件的填充后来到下面的q)带入进去后,根据查询条件将参数作为实例化WP_Tax_Query类的参数进行调用,而后2138行会去调用wp-tax-query类中的get_sql()方法。


跟进后发现这两个参数起到了初始化wp-tax-query.class的作用。继续跟进250行调用了get_sql_clauses()。



在274行经过我们"假设可控参数"填充后的$q传入实例化的WP_Tax_Query类传入的参数。带入到$this->get_sql_for_query()方法中。跟进该方法。



其实这里非常简单,首先要求我们$query是数组,而这个是参数,即依旧是我们"假设性可控变量"。并且在刚刚漏洞点分析时我们知道,我们需要控制terms来进行拼接注入,所以is_first_order_clause()是随便可以绕过去的。进行跟进324行的get_sql_for_clause()。



那么就来都我们漏洞点的位置了。最后回到get_posts()调用$wpdb->get_col进行查询




看到这里其实并没有一个直接的sql注入漏洞在我我们面前,更多的利用点而已。因为我们办法主动的触发该漏洞,所以这个算是个利用链。所以越南的大佬发表出的文章说过这样一句话:


由于这种攻击的措辞——WordPress Core中的 SQL 注入 ——它很容易被误解。虽然 SQL 注入源自 WordPress Core 文件系统,但无法自行触发攻击。至少不在一个普通的 WordPress 网站上。因此,您需要查找主题和插件中的不一致之处。例如,如果主题或插件使用WP_Query调用数据库并将信息传递给它。


04

插件漏洞利用复现分析


1、环境搭建

在这我们也看到了利用链的开端是WP-Query类的构造方法。并且要求我们对其参数具有控制能力


那么WordPress本身不具备自身触发该漏洞的能力,我们就去寻找插件的方法。首先先在wp-config.php文件中把报错信息给打开


(这实际上漏洞和这个并无太大关联,这只是因为数据库在报错时会有信息出来方便理解,漏洞本身实际上是使用无回显的盲注)

复现这个漏洞使用的环境是:

Wordpress 5.8.0

Elementor Custom Skin 3.1.4

插件的安装也比较简单,直接上传压缩包即可



2、漏洞复现分析

安装插件并启用后,我们来分析该插件的漏洞触发点/wp-content/plugins/ele-custom-skin/includes/ajax-pagination.php,在get_document_data()方法创建了WP_Query对象。


$this->query属性在构造函数__construct()中进行了初始化,$_POST['query']在json解码后赋值给了$this->query,数据是可控的。因此,假设可控变量就可以通过post传入构造进来了。get_document_data()满足了SQL注入触发的两个条件。

所以现在的问题转变为了如何实例化ECS_Ajax_Load这个类然后调用get_document_data()方法。


那么该如何调用get_document_data方法呢?通过搜索发现,在上面的__construct方法中25init_ajax()方法将get_document_data()注册为action分别为wp_ajax_ecsload、wp_ajax_nopriv_ecsload`。



这里就不得不提到wordpress的两个重要方法add_action()、do_action()。

•add_action 用法:add_action("Hook名","函数名")

add_action 可以将我们自定义的函数加到特定的 Hook 上去,等待执行。一般来说,我们只需要执行如下命令即可。

•do_action 用法:do_action("Hook名", "参数")

do_action 是 WordPress 插件机制非常重要的一环,当程序运行到这个函数时,就会将挂载在这个 Hook 上的所有函数执行一遍。这个函数有两个参数,第一个参数是 Hook 的名称,第二个参数则是具体的参数。

因此要触发,只需要找到一个入口文件,既可以加载插件,又可以调用特定的action。通过查询资料和代码搜索,我们发现了wp-admin/admin-ajax.php文件。在文件开始,加载了wp-load.php文件。

通过查询资料,我们发现插件加载的流程如下,在wp-settings.php中会加载active状态的插件。

index.php

    ->wp-blog-header.php

        ->wp-load.php

            ->wp-config.php

                ->wp-settings.php



该php会先包含wp-load.php,而根据上面刚刚分析的wp-load.php会去加载我们的插件,因此,admin-ajax.php满足了插件加载的条件。随后获取action参数后会检查当前用户有没有登录,当用户登录并且有action调用权限时,会调用wp_ajax_前缀的action;而当用户没有登录时,则会调用wp_ajax_nopriv_前缀的action。



刚好wp-admin\admin-ajax.php里包含/wp-load.php,刚刚分析了这个包含会一直包含是会去调用实例化我们的插件的。实例化经过最后的wp-content\plugins\ele-custom-skin\includes\ajax-pagination.php完成实例化。

所以利用过程就很明显了,由于wp-admin\admin-ajax.php的前几行代码会去包含我们的插件类,而这时候插件利用类是会被实例化的,在该插件类的构造方法__construct()中接受我们post的传值,所以在访问wp-admin\admin-ajax.php文件时传入query就可以完成对之前的限制处理,也就是假设可控变量的可控,而后继续走到下面,由于我们没有登录,所以走到else分支,调用wp_ajax_nopriv_{$action}。而在刚刚插件类的实例化时,__construct()的init_ajax()调用代码


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']);       }
添加到了action中,值调用do_action("wp_ajax_nopriv_{$action}"),而$action也是从post传入是可控的,这时只需要将$action=ecsload,即可调用get_document_data()并开启利用的调用。

POST /wordpress/wp-admin/admin-ajax.php HTTP/1.1Host: localhostCache-Control: max-age=0sec-ch-ua: " Not A;Brand";v="99", "Chromium";v="92"sec-ch-ua-mobile: ?0Upgrade-Insecure-Requests: 1User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.159 Safari/537.36Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9Sec-Fetch-Site: noneSec-Fetch-Mode: navigateSec-Fetch-User: ?1Sec-Fetch-Dest: documentAccept-Encoding: gzip, deflateAccept-Language: zh-CN,zh;q=0.9Connection: closeContent-Type: application/x-www-form-urlencodedContent-Length: 182

action=ecsload&ecs_ajax_settings={"max_num_pages":2,"theme_id":"1","current_page":"1","widget_id":"1"}&query={"tax_query":[{"field":"term_taxonomy_id","terms":["1)+and+sleep(5)#"]}]}

(ecs_ajax_settings参数是为了绕过__construct的一些限制,可控很好理解,看看代码就行)

05

补丁分析


wp_parse_id_list()方法会对数组的每个元素调用absint()方法转换成非负的int类型,杜绝了SQL注入漏洞的可能。

06

参考

https://mp.weixin.qq.com/s/-OYMQbsdYxXuvW7RY_W8xg

https://www.zerodayinitiative.com/blog/2022/1/18/cve-2021-21661-exposing-database-info-via-wordpress-sql-injection

https://www.exploit-db.com/exploits/50663

https://stackdiary.com/sql-injection-in-wordpress-core-cve-2022-21661/

https://www.cnblogs.com/forforever/p/15799392.html

https://github.com/TAPESH-TEAM/CVE-2022-21661-WordPress-Core-5.8.2-WP_Query-SQL-Injection









A9 Team
A9 Team 甲方攻防团队,成员来自某证券、微步、青藤、长亭、安全狗等公司。成员能力涉及安全运营、威胁情报、攻防对抗、渗透测试、数据安全、安全产品开发等领域,持续分享安全运营和攻防的思考和实践,期望和朋友们共同进步,守望相助,合作共赢。
 最新文章