“A9 Team 甲方攻防团队,成员来自某证券、微步、青藤、长亭、安全狗等公司。成员能力涉及安全运营、威胁情报、攻防对抗、渗透测试、数据安全、安全产品开发等领域,持续分享安全运营和攻防的思考和实践。”
01
—
漏洞描述
补丁中,对$query['terms']的赋值取决于$query['field']的取值。
漏洞利用链
网上有很多关于这个利用链的分析了,在这里贴出来:
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()方法中。跟进该方法。
那么就来都我们漏洞点的位置了。最后回到get_posts()调用$wpdb->get_col进行查询
看到这里其实并没有一个直接的sql注入漏洞在我我们面前,更多的利用点而已。因为我们办法主动的触发该漏洞,所以这个算是个利用链。所以越南的大佬发表出的文章说过这样一句话:
插件漏洞利用复现分析
(这实际上漏洞和这个并无太大关联,这只是因为数据库在报错时会有信息出来方便理解,漏洞本身实际上是使用无回显的盲注)
复现这个漏洞使用的环境是:
Wordpress 5.8.0
Elementor Custom Skin 3.1.4
插件的安装也比较简单,直接上传压缩包即可
$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
刚好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']);
}
POST /wordpress/wp-admin/admin-ajax.php HTTP/1.1
Host: localhost
Cache-Control: max-age=0
sec-ch-ua: " Not A;Brand";v="99", "Chromium";v="92"
sec-ch-ua-mobile: ?0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.159 Safari/537.36
Accept: 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.9
Sec-Fetch-Site: none
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Connection: close
Content-Type: application/x-www-form-urlencoded
Content-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)#"]}]}
补丁分析
参考
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