漏洞介绍

CVE-2024-4439是一个存储型跨站脚本(XSS)缺陷,允许攻击者通过 Avatar 块注入恶意的 Web 脚本。这个缺陷产生的原因是因为用户显示名称的输出没有进行充分的转义,使得经过身份验证和未经身份验证的攻击者都能够执行任意的 PHP 命令。

漏洞范围

受影响的版本: 6.5 – 6.5.1、6.4 – 6.4.3、6.3 – 6.3.3、6.2 – 6.2.4、 6.1 – 6.1.5、6.0 – 6.0.7

漏洞靶场

docker-compose.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
version: '3.8'

services:
db:
image: mysql:5.7
container_name: wordpress_db
environment:
MYSQL_ROOT_PASSWORD: root_password
MYSQL_DATABASE: wordpress
MYSQL_USER: wordpress_user
MYSQL_PASSWORD: wordpress_password
volumes:
- db_data:/var/lib/mysql
networks:
- wordpress_network

wordpress:
image: wordpress:6.4.3
container_name: wordpress_app
depends_on:
- db
ports:
- "8080:80"
environment:
WORDPRESS_DB_HOST: db:3306
WORDPRESS_DB_NAME: wordpress
WORDPRESS_DB_USER: wordpress_user
WORDPRESS_DB_PASSWORD: wordpress_password
WORDPRESS_DEBUG: "true"
volumes:
- wordpress_data:/var/www/html
networks:
- wordpress_network

volumes:
db_data:
wordpress_data:

networks:
wordpress_network:

访问127.0.0.1:8080,完成配置后

在后台-外观-主题中,选中一个主题,自定义,进行设置

image-20241121211110379

进入设计-模版-单篇文章

image-20241121211213085

image-20241121211346727

右边选中头像,进入主题编辑器

image-20241121211641111

先点击头像,然后右上方点击设置,右方有两个属性,链接到用户个人资料和在新窗口打开,把两个按钮勾选上

漏洞原理

这次的漏洞产生点并不能在默认的wordpress中,还需要对wordpress进行一些配置,主要产生存储型XSS的点是在文章评论的作者名称中,因为对文章作者的名称代码不恰当,导致了输入的作者名称没有经过任何的过滤就嵌入到了HTML的属性当中,导致了存储型XSS

关于头像的渲染的处理在wp-includes/blocks/avatar.php文件中,它的render_block_core_avatar对评论处的头像和名称等进行了渲染处理。

img

wp-includes/blocks/avatar.php#L16-L133

这部分代码是对评论信息的一些处理,首先通过get_comment函数传入commentId获取评论的内容,这里是通过数据库中的索引字段commentId获取数据,获取到了内容之后,通过__函数将作者的名称进行翻译,以便于支持多语言。随后通过get_avatar()函数来获取评论者的头像,随后就进入到了if判断之中,当评论中的某些属性不为空,根据下面wordpress评论的一些属性信息,这里的条件检查应该是处理是否可以将评论者的头像作为链接显示,如果链接的目标是可以以_blank新窗口的方式打开,那么就会赋值$label属性。

1
2
3
4
5
6
if ( '_blank' === $attributes['linkTarget'] ) {
$label = 'aria-label="' . sprintf( esc_attr__( '(%s website link, opens in a new tab)' ), $comment->comment_author ) . '"';
}
$avatar_block = sprintf( '<a href="%1$s" target="%2$s" %3$s class="wp-block-avatar__link">%4$s</a>', esc_url( $comment->comment_author_url ), esc_attr( $attributes['linkTarget'] ), $label, $avatar_block );
}
return sprintf( '<div %1s>%2s</div>', $wrapper_attributes, $avatar_block );

其实这里一开始看并没有看出什么问题,细看会发现esc_attr__函数并没有包裹$comment->comment_author,也就是没有对$comment->comment_author进行处理,最终通过sprintf翻译输出了$comment->comment_author,在下面的代码中直接就将$label嵌入到了target="%2$s之后,这就导致了XSS的问题。

看回去esc_attr函数,这个函数的确保字符串在存储或输出前是有效的UTF-8格式,随后就调用了_wp_specialchars进行了处理。

img

_wp_specialchars函数是进行了一些安全处理的,如果$comment->comment_author能经过这些处理,那么就不会产生xss的问题,因为在这个函数中,对能够造成xss的字符问题进行了实体的编码,防止了xss问题的产生。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
function _wp_specialchars( $text, $quote_style = ENT_NOQUOTES, $charset = false, $double_encode = false ) {
$text = (string) $text;

if ( 0 === strlen( $text ) ) {
return '';
}
if ( ! preg_match( '/[&<>"\']/', $text ) ) {
return $text;
}
if ( empty( $quote_style ) ) {
$quote_style = ENT_NOQUOTES;
} elseif ( ENT_XML1 === $quote_style ) {
$quote_style = ENT_QUOTES | ENT_XML1;
} elseif ( ! in_array( $quote_style, array( ENT_NOQUOTES, ENT_COMPAT, ENT_QUOTES, 'single', 'double' ), true ) ) {
$quote_style = ENT_QUOTES;
}
if ( ! $charset ) {
static $_charset = null;
if ( ! isset( $_charset ) ) {
$alloptions = wp_load_alloptions();
$_charset = isset( $alloptions['blog_charset'] ) ? $alloptions['blog_charset'] : '';
}
$charset = $_charset;
}

if ( in_array( $charset, array( 'utf8', 'utf-8', 'UTF8' ), true ) ) {
$charset = 'UTF-8';
}

$_quote_style = $quote_style;

if ( 'double' === $quote_style ) {
$quote_style = ENT_COMPAT;
$_quote_style = ENT_COMPAT;
} elseif ( 'single' === $quote_style ) {
$quote_style = ENT_NOQUOTES;
}

if ( ! $double_encode ) {
$text = wp_kses_normalize_entities( $text, ( $quote_style & ENT_XML1 ) ? 'xml' : 'html' );
}

$text = htmlspecialchars( $text, $quote_style, $charset, $double_encode );
if ( 'single' === $_quote_style ) {
$text = str_replace( "'", '&#039;', $text );
}

return $text;
}

因此在修复的版本之中,可以看到wordpress官网只是把两个函数的顺序调换了一下,就完成了修复。感觉这是程序员在编码的时候疏忽大意造成的问题。

漏洞复现

发布任意文章后,就可以进行评论,在评论中把作者的名字换成xss恶意语句

" onmouseover="alert(1)"

image-20241121212104697

image-20241121212357761

image-20241121212522905

成功复现

参考

CVE-2024-4439 Wordpress XSS存储型漏洞复现 - 先知社区 (aliyun.com)