Hu3sky's blog

WordPress5.0 RCE 分析

Word count: 1,275 / Reading time: 5 min
2019/02/28 Share

WordPress5.0 RCE

近日,WP爆出了一个RCE的洞,感觉利用链很diao,看了大佬的分析文章,
利用链是
Post Meta变量覆盖->裁剪功能文件上传目录穿越->模板任意文件包含
自己再来分析分析..(不得不说 看大佬的分析都看的头要炸了 膜一波 太强了)

漏洞需求

漏洞分析

变量覆盖

当我们在媒体库上传图片时,图片会保存到wp-content\uploads,并且同时向wp_postmeta数据库中写入_wp_attached_file_wp_attachment_metadata,一个是路径,一个是文件的序列化内容

定位到

1
文件:wp-admin/post.php:207

1
跟进edit_post
1
$post_data变量来自POST的数据
一直往下,第383行,用wp_update_post$post_data进行了处理。跟进
1
又进入
1

跟进wp_insert_attachment

1
继续跟wp_insert_post
1
对post来的meta_input数组里的字段进行遍历赋值,这里就涉及到了变量覆盖

wp_postmeta库中
1

目录穿越

调用栈为
1

1
文件:wp-admin/admin-ajax.php

$core_actions_post数组中存在crop-image
1
1
根据裁剪的actioncrop-image 跟到函数
wp_ajax_crop_image
1
看到函数check_ajax_referer,对nonce值进行了处理
check_ajax_referer再跟到wp_verify_nonce
1
进行了hash计算
所以我们需要nonce参数和id参数 (原文中并没有说为什么要传入

定位到裁剪的函数

1
2
文件:wp-admin/includes/image.php:25
方法:wp_crop_image

这里的$src是上面传过来的$attachment_id也就是POSTid

1

当我们通过覆盖_wp_attached_file传入文件名改为
2019/02/1.jpg#/../../../../themes/twentynineteen/hu3sky.jpg
并没有对其过滤等验证处理,于是找不到文件就进到_load_image_to_edit_path
此函数用来将文件路径请求拼接为
{wordpress_path}/wp-content/uploads/xxx.jpg
于是根据这个我们请求的url为
http://127.0.0.1/CMS/WordPress5.0/wp-content/uploads/2019/02/1.jpg#/../../../../themes/twentynineteen/hu3sky.jpg
# 后的当作锚,于是图片可以被请求到

1

接着生成文件的时候没有对../进行过滤 在wp_mkdir_p时对应的filename和dirname为
1
1
于是可以最终调用wp_get_image_editor获取到的图片处理库

1
跟进load方法

1
文件:wp-includes/class-wp-image-editor-imagick.php:129
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public function load() {
if ( $this->image instanceof Imagick ) {
return true;
}

...

try {
$this->image = new Imagick();
...
$filename = $this->file;

...
$this->image->readImage( $filename );

save方法,进行文件的保存

1
2
3
$editor = wp_get_image_editor( $src );
...
$result = $editor->save( $dst_file );

图片马

这里就要考虑到裁剪后的图片马是否依然存在恶意代码
wordpress后端使用的图片处理库有两个,imagickgd
优先默认用imagick
Imagick处理图片时不处理EXIF信息,因此可以把恶意代码设置在EXIF部分,经过裁剪后会保留EXIF信息,此时再进行包含就能造成代码执行。

模板任意文件包含

通过设置_wp_page_template,用之前的变量覆盖来设置
但是这里对其作了一些判断
1
如果传入的page_template的值不等于default,并且不存在该模板,就会把_wp_page_template设置为default
所以我们需要再上传一个文件去设置_wp_page_template
找到加载模板的位置

1
2
文件:wp-includes/template.php:634
方法:locate_template

1
找到调用locate_template的位置

1
2
文件:wp-includes/template.php
方法:get_query_template

1

接着找到调用get_query_template
继续回溯

1
文件:wp-includes/template-loader.php:47

1
发现会根据不同的属性 调用不同的加载模板函数
发现在get_page_templateget_single_template
template调用了函数get_page_template_slug
1
这里直接从库里取了_wp_page_template,并返回给了$template
1
再回到

1
文件:wp-includes/template-loader.php:76

1
包含了template

总结利用链

  1. 通过POST META将_wp_attached_file设置为目录穿越的名字
  2. 构造裁剪图片,将裁剪后的图片存到theme目录下
  3. 只要我们之前裁剪后的图片依旧存在恶意代码,再到模板调用get_page_template/get_single_template
    之后就包含theme下存在恶意代码的图片,造成RCE

漏洞复现(未成功)

制作好图片马
1
选择图片上传,编辑图片
1
添加POST

1
&meta_input[_wp_attached_file]=2019/02/1.jpg#/../../../../themes/twentynineteen/hu3sky.jpg

1

裁剪
1
需要将POST改为

1
action=crop-image&_ajax_nonce=13f318d006&id=7&cropDetails[x1]=10&cropDetails[y1]=10&cropDetails[width]=10&cropDetails[height]=10&cropDetails[dst_width]=100&cropDetails[dst_height]=100

1
nonceid不变,然后用这个替换掉原本的post请求

已经将裁剪的图片保存到了我们构造的目录
themes\twentynineteen
1
接着再上传一个txt文件

编辑并修改POST,添加

1
&meta_input[_wp_page_template]=cropped-hu3sky.jpg

1
查看附件
1
由于Imagick版本一直对不上。没找到Imagick6.9.7的版本。只有6.9.3的 这里没有成功复现 - -

踩的坑

  • Imagick版本,说的是高版本不能请求远程图片,但是我当时用的低版本也不能请求,询问了一下畅师傅,暂时固定到版本6.9.7,但是能找到的都是6.9.3的
    测试脚本
1
2
$a= new Imagick();
$a->readImage("http://127.0.0.1/CMS/WordPress5.0/wp-content/uploads/2019/02/1.jpg");

1

emm 暂时先这样吧

CATALOG
  1. 1. WordPress5.0 RCE
    1. 1.1. 漏洞需求
    2. 1.2. 漏洞分析
      1. 1.2.1. 变量覆盖
      2. 1.2.2. 目录穿越
        1. 1.2.2.1. 图片马
      3. 1.2.3. 模板任意文件包含
      4. 1.2.4. 总结利用链
    3. 1.3. 漏洞复现(未成功)
    4. 1.4. 踩的坑