分析由来
这个洞费了我很长时间,因为踩坑比较多,基本上能踩的都踩了。刚开始跟着别人的分析文章走,发现走不通。可能忽略了很多东西,加上我第一次认识 WordPress,不是很熟悉这个框架,并且前端比较复杂,很难找到对应的调用点。其实完全分析到复习成功看来其实确实是一个很精妙的攻击链,不得不赞叹一下!
整个攻击链的流程
- 上传我们构造exif头带shell的图片
- 进入编辑图片第一次进入
edit_post
修改图片postmeta
中_wp_attachment_file
字段为辅助目录,再进入crop-image
创建辅助目录 - 进入编辑图片第二次进入
edit_post
修改图片postmeta
中_wp_attachment_file
字段为目标目录,再进入crop-image
创建目标文件 - 创建一个新的文章页面
- 进去新创建的文章页面,获取
wponce
和post_id
- 自己构造编辑内容即手动添加
wponce
和post_id
同上修改postmeta
中_wp_page_template
字段为上传文件名字前加上cropped-
7.访问修改过的文章页面Getshell
填坑
第一步构造exif没什么难度,随便找个可写的tag 比如
User Comment
第二步即整个链的关键之处,可以修改和添加任意post的
postmeta
,但是为什么这里有两步修改_wp_attachment_file
,这是其中第一个坑,我的看的分析文章中都没有提过这个问题。列如我们需要将图片移动至相应的主题文件下充当模板。 上传m.jpg
路径为/wp-content/upload/2019/3/05/m.jpg
则需要构造m.jpg?/../../../../themes/twentynineteen/m2.jpg
如果第一步就直接构造这个是有一点问题,会导致再crop-image
中写了修改过的图片。在
/wp-includes/class-wp-image-editor.php
402行中 会调用对应图片编辑扩展的writeImage
函数来写图片,会报错文件地址非法,这里原因就是不存在m.jpg?
这个目录,前面虽然有mkdir(dirname($filename),777,true)
,以递归的形式写,而且会返回true
,但是并没有创m.jpg
这个目录。而一般涉及到读写的函数都会逐级去判断目录,imagick::writeImage
在判断路径时就发生了错误,但是为什么mkdir
会执行成功呢,可能原因在于mkdir
第三个参数,之前我在看php的内核源码的看见过关于路径的一个expand过程,过程中会将./
../
这两个目录展开,我猜测可能是进行了展开,将m.jpg?
和../
抵消了。但是问题来了,同样我寻思这样的情况也是在window下成立,确实如此,但是在window情况下出现了两种情况第一种如果文件里面包含的是?,window不允许文件名包含?,道理上在
true
情况下会忽略,但实际上在true
和false
的情况下都写不了,当文件名包含#
时在true
的情况可以写这时候出现了第二种情况,我看见一篇分析中在文件名包含?,却在
false
的情况下执行成功了,true
的情况却不行。作者的解释时true
时递归判断了m.jpg?
目录名的合法性,在false
下没有判断出现了这个问题。这不明显矛盾了吗?事后我联系了文章作者询问其php版本为7.0,window我的为5.5。三种不同的情况(包括全程复现下的环境kali/debian php 7.3),这
mkdir
真的有趣,非常有必要去看一下。以下均为文件名包含?的情况
- window+php 5.5 mkdir(filename,777,true),mkdir(filename,777,false) 均为false
- window+php 7.0 mkdir(filename,777,true)=>false,mkdir(filename,777,false) =>true
- linux(kali/debian)+php7.3 mkdir(filename,777,true)=>true,mkdir(filename,777,false) =>false
写完这篇文章后,
mkdir
内部的实现必须要看了。结合前面说的,必须先创建辅助目录m.jpg?
才能在都后面写图片成功。这一步需要写两次!!接下来的模板包含情况,一开始我认为在
edit_post
里面只能编辑附件,无奈wordpress
前端太复杂,我找不到接口。只能看wordpress
解析请求的过程,看如何构造才能走到singer
或者page
模式上去,因为只有这两个模式加载的时候才会从postmeta
中取wp_page_template
来加载模板,可惜无论我怎样构造都会加载到attachment
的模式上去。仔细思考了一下,文章可以走到
singer
上去,但是能不能修改文章类型的postmeta
,因为在请求的时候wordpress有自带的防csrf机制,如果想要改必须要相对应的wpnonce
才行。比较wpnonce
过程为1
2check_admin_referer('update-post_'.$post_id);
wp_verify_nonce($_REQUEST['wpnonce'],$action); //$action='update-post_'.$post_id显然需要动作和
post_id
结合而成的wpnonce
,有了这个我们就能使用edit_post
,找了很长时间都没有找到带这个参数的接口,突然我觉得可以在文件编辑页面的html源码搜索一下,尽然有!!,在meta-form
中,到现在我都不知道如何操作才会使用到这个表单,修改文章过程中都是传的json。拿到wpnoce和post_id拼接请求,且通过meta_input[]修改wp_page_template,包含图片拿到shell。
总结
确实在看别人分析文章的时候,会出现一些偏差,导致自己的思维有些被限制住了,整个链从任意修改数据库中postmeta,到写文件的路径穿越,到模板文件的加载一气呵成,这样的链也不多了。总以为在分析完一个东西以后,会万事大吉,却发现问题只会源源不断的来 :)。文件最后也遗留了一个关于php中mkdir的处理过程。这也是我接下来的下一篇文章。 第一次用markdown写东西,写的不好大家别介意:)
附上做的一点验证性实验,很有可能我的猜测是正确的!第一个是为false的情况,第二是为true递归执行的情况,我用的是php7.4-dev
图丢失lol