摘要:漏洞信息简介简称是北京康盛新创科技有限责任公司推出的一套通用的社区论坛软件系统。此次漏洞位于中的行处的可控导致。总结这个漏洞的成功利用离不开对解析特性的了解,成功从变量中解析出,才能调用方法,在该方法中使用请求拼接了前缀的地址。
Crossday Discuz! Board(简称 Discuz!)是北京康盛新创科技有限责任公司推出的一套通用的社区论坛软件系统。自2001年6月面世以来,Discuz!已拥有15年以上的应用历史和200多万网站用户案例,是全球成熟度最高、覆盖率最大的论坛软件系统之一。目前最新版本Discuz! X3.4正式版于2017年8月2日发布,去除了云平台的相关代码,是 X3.2 的稳定版本。此次漏洞位于/source/module/misc/misc_imgcropper.php
中的54行处的$prefix
可控导致SSRF。
2021最新整理网络安全/渗透测试/安全学习/100份src技术文档(全套视频、CTF、大厂面经、精品手册、必备工具包、路线)一>获取<一
Discuz开源地址为Gitee,使用git clone 克隆到本地
git clone https://gitee.com/Discuz/DiscuzX
根据补丁提交记录来切换到漏洞修复前的前一个commit版本
git checkout a5c1b95dc4464ee3da0ebd4655d30867f85d6ae9
本地搭建好运行环境之后首先访问页面http://www.a.com/dz/DiscuzX/upload/misc.php?mod=imgcropper
,然后点击裁切按钮并抓包
拦截之后重放数据包在提交内容位置添加参数cutimg
和picflag
,红框处填写需要请求的IP地址并发送数据包
&cutimg=/dz/DiscuzX/upload/member.php%3fmod%3dlogging%26action%3dlogout%26referer%3d//c%2523%2540192.168.163.131%26quickforward%3d1&picflag=2
这时服务器将成功收到请求
下面来看看后端是怎么处理的,断点地址为source/module/misc/misc_imgcropper.php
line 54。当传递的picflag
为2
时取$_G["setting"]["ftp"]["attachurl"]
变量的值"/"
。接下来55行接收拼接可控变量cutimg
。
既然可控,那么就要看看它后面是怎么处理的,来到Thumb方法
进入init
方法,到达parse_url
方法后在source
中解析出了host
。此时就会进入dfsockopen
//dz/DiscuzX/upload/member.php?mod=logging&action=logout&referer=//c%23%40192.168.163.131&quickforward=1
parse_url支持//baidu.com/s这种形式的url解析
继续跟进dfsockopen
方法,在该方法中又进行了解析,处理同上,由于不存在协议所以scheme
为null
,这样在最后拼接出来的URL就是://xx.com/
,这样的链接会自动补上协议,所以最后为http://://xx.com/
。
在windows中使用curl
请求该地址最终解析到了192.168.163.1,也就是某个网卡的本地地址。请求路径为http://192.168.163.1/xx.com
此时我们能够进行内网请求,但是地址并不可控,所以需要找到一个discuz可以进行任意url跳转的漏洞,再请求该路径跳转出去。discuz在退出的时候会取get
参数referer
中的值来进行跳转,下面来分析跳转处的代码。
function dreferer($default = "") { global $_G; $default = empty($default) && $_ENV["curapp"] ? $_ENV["curapp"].".php" : ""; //获取 $_GET["referer"]参数 $_G["referer"] = !empty($_GET["referer"]) ? $_GET["referer"] : $_SERVER["HTTP_REFERER"]; $_G["referer"] = substr($_G["referer"], -1) == "?" ? substr($_G["referer"], 0, -1) : $_G["referer"]; if(strpos($_G["referer"], "member.php?mod=logging")) { $_G["referer"] = $default; } $reurl = parse_url($_G["referer"]); //如果存在协议则判断是否为http和https,不存在则不判断 if(!$reurl || (isset($reurl["scheme"]) && !in_array(strtolower($reurl["scheme"]), array("http", "https")))) { $_G["referer"] = ""; } if( !empty($reurl["host"]) && //判断解析的host是否为 $_SERVER["HTTP_HOST"] !in_array($reurl["host"], array($_SERVER["HTTP_HOST"], "www.".$_SERVER["HTTP_HOST"])) && //判断 $_SERVER["HTTP_HOST"]是否存在于解析出的host中 !in_array($_SERVER["HTTP_HOST"], array($reurl["host"], "www.".$reurl["host"]))) { if(!in_array($reurl["host"], $_G["setting"]["domain"]["app"]) && !isset($_G["setting"]["domain"]["list"][$reurl["host"]])) { //截取解析的host第一个.后面的所有内容,没有.则当长度为1时则返回为空 $domainroot = substr($reurl["host"], strpos($reurl["host"], ".")+1); //$_G["setting"]["domain"]["root"]为array且为空 if(empty($_G["setting"]["domain"]["root"]) || (is_array($_G["setting"]["domain"]["root"]) && //想要不进入这个判断需要保证 $domainroot为空,这样referer才不会被覆盖,才能实现任意地址跳转 !in_array($domainroot, $_G["setting"]["domain"]["root"]))) { $_G["referer"] = $_G["setting"]["domain"]["defaultindex"] ? $_G["setting"]["domain"]["defaultindex"] : "index.php"; } } } elseif(empty($reurl["host"])) { $_G["referer"] = $_G["siteurl"]."./".$_G["referer"]; } $_G["referer"] = durlencode($_G["referer"]); return $_G["referer"];}
在上面的代码中只要我们做到$_G["referer"]
不被覆盖即可,首先解析的host
中存在协议则判断是否为http
和https
,不存在则不判断,所以我们可以不传入协议。第二处判断解析的host
是否为$_SERVER["HTTP_HOST"]
,如果是则不进入if覆盖$_G["referer"]
。但是这样的话在实际的ssrf跳转场景中$_SERVER["HTTP_HOST"]
为空。
所以这个条件无法生效。后面的一个关键判断$domainroot = substr($reurl["host"], strpos($reurl["host"], ".")+1)
;会截取解析的host
第一个.
后面的所有内容,没有.
并且当长度为1
时则返回为空。返回为空时后面的!in_array($domainroot, $_G["setting"]["domain"]["root"]))
这个条件就为false。也就不会进入if判断覆盖$_G["referer"]
了。但是这儿存在一个问题,如果我们host为a
那么最后通过curl
跳转的时候就往a跳转了。不能指定任意地址。此时可以利用parse_url
和curl
的解析差异来绕过这个限制。构造//a#[@1](https://github.com/1 "@1").1.1.1
,那么parse_url
将解析host
为a
,而curl
解析host为1.1.1.1
。所以就得到了构造的完整url。
最后的利用流程为:ssrf访问本地接口进行URL跳转
301>
跳转到目标服务器,服务器上使用跳转脚本进行协议转换或者任意路径访问
=302=>
通过指定协议如gopher
,访问指定路径/_test…等
首先通过服务器搭建跳转脚本index.php
本地进行nc监听
ssrf跳转服务器地址
成功将请求转发到本地发送test消息
官方在[补丁提交记录[2]的版本提交中对漏洞进行了修复,修复方式为重写了dfsockopen
中调用的parse_url
为_parse_url
,在该方法中判断了parse_url
是否能够解析出协议,无法解析则退出。
这个漏洞的成功利用离不开对parse_url
解析特性的了解,parse_url
成功从cutimg
变量中解析出host,才能调用dfsockopen
方法,在该方法中使用curl
请求拼接了前缀的地址://xx.com
。这将请求本地地址,通过寻找discuz的url跳转来将本地请求转发出去。而在url跳转的利用中使用到了parse_url
和curl
对//a#[@1](https://github.com/1 "@1").1.1.1
的解析差异来完成任意地址访问。最后在访问地址使用302跳转来达到使用指定协议请求指定路径或发送数据的目的。
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/122461.html
摘要:本人非安全专业相关人士,了解不多,实在惭愧。工作原因,在所负责的内部服务中遭遇了的困扰,在此记录一下学习过程及解决方案。,即服务端请求伪造,是一种由攻击者构造形成由服务端发起请求的一个安全漏洞。通用的解决方案有过滤返回信息。 Update: 掘金评论区有同学提出通过域名获取 IP 地址时可能遭遇攻击,感谢提醒。本人非安全专业相关人士,了解不多,实在惭愧。 说到 Web 安全,我们前端...
摘要:服务端请求伪造漏洞,服务端请求伪造,是一种由攻击者构造形成由服务器端发起请求的一种漏洞。举例攻击者传入一个未经验证的,后端代码直接请求这个,就会造成漏洞。禁止不常用的协议仅仅允许和请求。 ...
摘要:攻击者可能会窃取或修改此类保护不力的数据,以实施信用卡欺诈身份盗用或其他犯罪活动。跨站脚本攻击漏洞的防御策略前端过滤字符,后端白名单例如,只允许固定的标签,设置,防止被读取。 目录 WEB安全 OWASP Top 10(2017) Injection - 注入攻击 Broken Authen...
摘要:是跨站请求伪造攻击,是由于没有在关键操作执行时进行是否由用户自愿发起的确认,模仿合法用户对服务器发起请求。是跨站请求伪造攻击,由客户端发起是服务器端请求伪造,由服务器发起重放攻击是将截获的数据包进行重放,达到身份认证等目的 ...
阅读 1721·2021-11-22 15:25
阅读 1050·2021-11-19 09:40
阅读 2934·2021-10-14 09:42
阅读 1660·2021-09-27 13:57
阅读 828·2021-09-22 15:10
阅读 834·2021-08-16 11:01
阅读 2745·2021-07-23 17:51
阅读 595·2019-08-30 15:55