WordPress5.0 RCE
近日,WP爆出了一个RCE的洞,感觉利用链很diao,看了大佬的分析文章,
利用链是Post Meta变量覆盖->裁剪功能文件上传目录穿越->模板任意文件包含
自己再来分析分析..(不得不说 看大佬的分析都看的头要炸了 膜一波 太强了)
漏洞需求
版本号 <= 43bdb0e193955145a5ab1137890bb798bce5f0d2
https://github.com/WordPress/WordPress/tree/43bdb0e193955145a5ab1137890bb798bce5f0d2需要一个author权限的账号
漏洞分析
变量覆盖
当我们在媒体库上传图片时,图片会保存到wp-content\uploads
,并且同时向wp_postmeta
数据库中写入_wp_attached_file
和 _wp_attachment_metadata
,一个是路径,一个是文件的序列化内容
定位到
1 | 文件:wp-admin/post.php:207 |
跟进edit_post
$post_data变量来自POST的数据
一直往下,第383行,用wp_update_post
对$post_data
进行了处理。跟进
又进入
跟进wp_insert_attachment
继续跟wp_insert_post
对post来的meta_input数组里的字段进行遍历赋值,这里就涉及到了变量覆盖
在wp_postmeta
库中
目录穿越
调用栈为
在
1 | 文件:wp-admin/admin-ajax.php |
$core_actions_post
数组中存在crop-image
根据裁剪的action
为crop-image
跟到函数wp_ajax_crop_image
看到函数check_ajax_referer
,对nonce
值进行了处理
跟check_ajax_referer
再跟到wp_verify_nonce
进行了hash计算
所以我们需要nonce参数和id参数 (原文中并没有说为什么要传入)
定位到裁剪的函数
1 | 文件:wp-admin/includes/image.php:25 |
这里的$src
是上面传过来的$attachment_id
也就是POST
的id
当我们通过覆盖_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
#
后的当作锚,于是图片可以被请求到
接着生成文件的时候没有对../
进行过滤 在wp_mkdir_p
时对应的filename和dirname为
于是可以最终调用wp_get_image_editor
获取到的图片处理库
跟进load方法
在
1 | 文件:wp-includes/class-wp-image-editor-imagick.php:129 |
1 | public function load() { |
save
方法,进行文件的保存
1 | $editor = wp_get_image_editor( $src ); |
图片马
这里就要考虑到裁剪后的图片马是否依然存在恶意代码
wordpress后端使用的图片处理库有两个,imagick
和gd
优先默认用imagick
而Imagick
处理图片时不处理EXIF信息,因此可以把恶意代码设置在EXIF部分,经过裁剪后会保留EXIF信息,此时再进行包含就能造成代码执行。
模板任意文件包含
通过设置_wp_page_template
,用之前的变量覆盖来设置
但是这里对其作了一些判断
如果传入的page_template
的值不等于default,并且不存在该模板,就会把_wp_page_template
设置为default
所以我们需要再上传一个文件去设置_wp_page_template
找到加载模板的位置
1 | 文件:wp-includes/template.php:634 |
找到调用locate_template
的位置
在
1 | 文件:wp-includes/template.php |
接着找到调用get_query_template
的
继续回溯
1 | 文件:wp-includes/template-loader.php:47 |
发现会根据不同的属性 调用不同的加载模板函数
发现在get_page_template
和get_single_template
对template
调用了函数get_page_template_slug
这里直接从库里取了_wp_page_template
,并返回给了$template
再回到
1 | 文件:wp-includes/template-loader.php:76 |
包含了template
总结利用链
- 通过POST META将
_wp_attached_file
设置为目录穿越的名字 - 构造裁剪图片,将裁剪后的图片存到theme目录下
- 只要我们之前裁剪后的图片依旧存在恶意代码,再到模板调用
get_page_template
/get_single_template
之后就包含theme下存在恶意代码的图片,造成RCE
漏洞复现(未成功)
制作好图片马
选择图片上传,编辑图片
添加POST
1 | &meta_input[_wp_attached_file]=2019/02/1.jpg#/../../../../themes/twentynineteen/hu3sky.jpg |
裁剪
需要将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 |
nonce
和id
不变,然后用这个替换掉原本的post请求
已经将裁剪的图片保存到了我们构造的目录themes\twentynineteen
下
接着再上传一个txt文件
编辑并修改POST,添加
1 | &meta_input[_wp_page_template]=cropped-hu3sky.jpg |
查看附件
由于Imagick版本一直对不上。没找到Imagick6.9.7的版本。只有6.9.3的 这里没有成功复现 - -
踩的坑
- Imagick版本,说的是高版本不能请求远程图片,但是我当时用的低版本也不能请求,询问了一下畅师傅,暂时固定到版本6.9.7,但是能找到的都是6.9.3的
测试脚本
1 | $a= new Imagick(); |
emm 暂时先这样吧