Wordpress <= 4.6.1 使用主题文件触发存储型XSS 漏洞分析
Author: p0wd3r (知道创宇404安全实验室)
0x00 漏洞概述
1.漏洞简介
WordPress是一个以PHP和MySQL为平台的自由开源的博客软件和内容管理系统,近日研究者发现在其<=4.6.1版本中,通过上传恶意构造的主题文件可以触发一个后台存储型XSS漏洞。通过该漏洞,攻击者可以在能够上传主题文件的前提下执行获取管理员Cookie等敏感操作。
2.漏洞影响
在能够上传主题文件的前提下执行获取管理员Cookie等XSS可以进行的攻击,实际的攻击场景有以下两种:
- 攻击者诱导管理员上传恶意构造的主题文件,且管理员并没有对文件进行检查
- 攻击者拥有管理员权限可以直接上传主题文件,但既然已经有管理员权限再进行这样的攻击也就多此一举了
3.影响版本
<= 4.6.1
0x01 漏洞复现
1. 环境搭建
docker pull wordpress:4.6.1 docker pull mysql
docker run --name wp-mysql -e MYSQL_ROOT_PASSWORD=hellowp -e MYSQL_DATABASE=wp -d mysql
docker run --name wp --link wp-mysql:mysql -d wordpress
2.漏洞分析
我们先随便下载一个主题:
wget https://downloads.wordpress.org/theme/illdy.1.0.29.zip
unzip -x illdy.1.0.29.zip
然后对illdy/style.css
进行如下更改:
/*
Theme Name: <svg onload=alert(1234)>
... DO NOT CHANGES HERE ...
*/
接着更改文件夹名字再打包:
mv illdy "<svg onload=alert(5678)>"
zip -r theme.zip "<svg onload=alert(5678)>"
构造好之后我们登录后台上传该主题文件,同时开始动态调试。
首先进入wp-admin/includes/class-theme-installer-skin.php
中第55-82行:
$name = $theme_info->display('Name');
...
if ( current_user_can( 'edit_theme_options' ) && current_user_can( 'customize' ) ) {
$install_actions['preview'] = '<a href="' . wp_customize_url( $stylesheet ). '" class="hide-if-no-customize load-customize"><span aria-hidden="true">' . __( 'Live Preview' ) . '</span><span class="screen-reader-text">' . sprintf( __( 'Live Preview “%s”' ), $name ) . '</span></a>';
}
$install_actions['activate'] = '<a href="' . esc_url( $activate_link ) . '" class="activatelink"><span aria-hidden="true">' . __( 'Activate' ) . '</span><span class="screen-reader-text">' . sprintf( __( 'Activate “%s”' ), $name ) . '</span></a>';
其中$theme_info
的值如下:
其中stylesheet
和template
的值为我们更改的文件夹名,headers.Name
为更改的style.css
中的Name
。$theme_info
中有我们可控的payload,其调用display
函数后赋值给$name
,$name
直接与html拼接,所以关键点在display
函数上,动态调试跟进到wp-includes/class-wp-theme.php
中第630-646行:
public function display( $header, $markup = true, $translate = true ) {
$value = $this->get( $header );
if ( false === $value ) {
return false;
}
if ( $translate && ( empty( $value ) || ! $this->load_textdomain() ) )
$translate = false;
if ( $translate )
$value = $this->translate_header( $header, $value );
if ( $markup )
$value = $this->markup_header( $header, $value, $translate );
return $value;
}
由之前的调用可知,这里的$header
的值为Name
。首先看$this-get($header)
,在wp-includes/class-wp-theme.php
中第594-617行:
public function get( $header ) {
...
$this->headers_sanitized[ $header ] = $this->sanitize_header( $header, $this->headers[ $header ] );
...
return $this->headers_sanitized[ $header ];
}
这里省略了与漏洞无关的部分,程序进入了$this->sanitize_header
,在wp-includes/class-wp-theme.php
第661-705行:
private function sanitize_header( $header, $value ) {
switch ( $header ) {
...
case 'Name' :
static $header_tags = array(
'abbr' => array( 'title' => true ),
'acronym' => array( 'title' => true ),
'code' => true,
'em' => true,
'strong' => true,
);
$value = wp_kses( $value, $header_tags );
break;
...
}
这里执行了Name
这个分支,可以看到程序使用wp_kses
对$value
的值进行了过滤,仅允许$header_tags
中的html符号,所以我们headers.Name
的值<svg onload=alert(1234)>
是不合法的,$value
值被赋为空。
然后程序回到了display
函数,根据动态调试可以知道程序执行了$value = $this->markup_header( $header, $value, $translate );
这个条件分支,再跟进,在wp-includes/class-wp-theme.php
中第720-748行:
private function markup_header( $header, $value, $translate ) {
switch ( $header ) {
case 'Name' :
if ( empty( $value ) )
$value = $this->get_stylesheet();
break;
...
return $value;
}
这里我们看到由于$value
在之前被赋为空,导致此处$value
被重新赋值为了$this->get_stylesheet()
,也就是值为<svg onload=alert(5678)>
的stylesheet
变量。最后返回的$value
赋给了$name
,$name
再与html拼接返回给客户端,从而触发了漏洞:
这个漏洞有趣的地方在于style.css
中的payload其实起到的是一个障眼法的作用,正是因为<svg onload=alert(1234)>
被过滤了才使$value
被赋值成了我们真正的payload<svg onload=alert(5678)>
。所以在构造主题文件的时候style.css
和文件夹名这两个地方都要更改。
3.补丁分析
可能是由于利用条件十分苛刻,目前Wordpress官方还没有发布补丁,最新版Wordpress仍存在该漏洞。
0x02 修复方案
在官方发布补丁前,管理员应提高安全意识,不要轻易使用来路不明的主题。
对于开发者来说建议对$name
进行合法性检查,例如这样:
$allowed_html = array(
'em' => true,
'strong' => true,
);
$name = wp_kses($name, $allowed_html);
0x03 参考
- https://www.seebug.org/vuldb/ssvid-92458
- https://www.mehmetince.net/low-severity-wordpress-461-stored-xss-via-theme-file
- https://codex.wordpress.org/FunctionReference/wpkses
- 信用卡“坏账”客户分析(一)
- 一道简单的sql语句题
- python爬虫反爬取---设置User Agent自动变换header文件
- 一文读懂Python多线程
- 深入理解Python变量作用域与函数闭包
- TensorFlow从1到2 - 5 - 非专家莫入!TensorFlow实现CNN
- JetBrains Rider 破解 (ideaIU等等开发工具都通用)
- python中的小魔法(一)
- 由问题入手,步步爬出Python中赋值与拷贝的坑
- python爬取链家租房之获得每一页的房屋信息地址(持续更新)
- python使用正则表达式
- python在租房过程中的应用
- python爬虫反爬取---设置IP代理自动变换requests.get()中proxy的IP
- 【译】TensorFlow实现Batch Normalization
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 用Gaussian 16计算振动分辨的紫外-可见吸收光谱
- graylog日志分析系统上手教程
- 使用Seq搭建免费的日志服务
- 拜托!这才是分布式系统CAP的正确打开方式!
- 接口管理这下总会了吧?
- 交子杯 - 2020 - AI赛道 - TOP1
- Valine 一款快速、简洁且高效的无后端评论系统
- 两段有趣的C代码
- 算法数据结构 | 三个步骤完成强连通分量分解的Kosaraju算法
- 并查集算法 详解
- SQL 中 EXISTS 用法详解
- Blazor带我重玩前端(六)
- PB 级大规模 Elasticsearch 集群运维与调优实践
- MySQL实时在线备份恢复方案
- Android通过原生请求直接获取网页内容