看到官方发布了预警,于是开始了漏洞应急。漏洞描述中指出Confluence Server与Confluence Data Center中的Widget Connector存在服务端模板注入漏洞,攻击者能利用此漏洞能够实现目录穿越与远程代码执行。

确认漏洞点是Widget Connector,下载最新版的比对补丁,发现在com\atlassian\confluence\extra\widgetconnector\WidgetMacro.java里面多了一个过滤,这个应该就是这个漏洞最关键的地方。

可以看到

this.sanitizeFields = Collections.unmodifiableList(Arrays.asList(VelocityRenderService.TEMPLATE_PARAM));

TEMPLATE_PARAM的值就是_template,所以这个补丁就是过滤了外部传入的_template参数。

public interface VelocityRenderService {public static final String WIDTH_PARAM = "width";public static final String HEIGHT_PARAM = "height";public static final String TEMPLATE_PARAM = "_template";

翻了一下Widget Connector里面的文件,发现TEMPLATE_PARAM就是模板文件的路径。

public class FriendFeedRenderer implements WidgetRenderer {private static final String MATCH_URL = "friendfeed.com";private static final String PATTERN = "friendfeed.com/(\\w+)/?";private static final String VELOCITY_TEMPLATE = "com/atlassian/confluence/extra/widgetconnector/templates/simplejscript.vm";private VelocityRenderService velocityRenderService;
......public String getEmbeddedHtml(String url, Map<String, String> params) {params.put(VelocityRenderService.TEMPLATE_PARAM, VELOCITY_TEMPLATE);return velocityRenderService.render(getEmbedUrl(url), params);}

加载外部的链接时,会调用相对的模板去渲染,如上,模板的路径一般是写死的,但是也有例外,补丁的作用也说明有人突破了限制,调用了意料之外的模板,从而造成了模板注入。

在了解了补丁和有了一些大概的猜测之后,开始尝试。

首先先找到这个功能,翻了一下官方的文档,找到了这个功能,可以在文档中嵌入一些视频,文档之类的。

看到这个,有点激动了,因为在翻补丁的过程中,发现了几个参数,urlwidthheight正好对应着这里,那_template是不是也从这里传递进去的?

随便找个Youtube视频插入试试,点击预览,抓包。

params中尝试插入_template参数,好吧,没啥反应。。

开始debug模式,因为测试插入的是Youtube视频,所以调用的是com/atlassian/confluence/extra/widgetconnector/video/YoutubeRenderer.class

public class YoutubeRenderer implements WidgetRenderer, WidgetImagePlaceholder {private static final Pattern YOUTUBE_URL_PATTERN = Pattern.compile("https?://(.+\\.)?youtube.com.*(\\?v=([^&]+)).*$");private final PlaceholderService placeholderService;private final String DEFAULT_YOUTUBE_TEMPLATE = "com/atlassian/confluence/extra/widgetconnector/templates/youtube.vm";
......public String getEmbedUrl(String url) {Matcher youtubeUrlMatcher = YOUTUBE_URL_PATTERN.matcher(this.verifyEmbeddedPlayerString(url));return youtubeUrlMatcher.matches() ? String.format("//www.youtube.com/embed/%s?wmode=opaque", youtubeUrlMatcher.group(3)) : null;}public boolean matches(String url) {return YOUTUBE_URL_PATTERN.matcher(this.verifyEmbeddedPlayerString(url)).matches();}private String verifyEmbeddedPlayerString(String url) {return !url.contains("feature=player_embedded&") ? url : url.replace("feature=player_embedded&", "");}public String getEmbeddedHtml(String url, Map<String, String> params) {return this.velocityRenderService.render(this.getEmbedUrl(url), this.setDefaultParam(params));}

getEmbeddedHtml下断点,先会调用getEmbedUrl对用户传入的url进行正则匹配,因为我们传入的是个正常的Youtube视频,所以这里是没有问题的,然后调用setDefaultParam函数对传入的其他参数进行处理。

    private Map<String, String> setDefaultParam(Map<String, String> params) {String width = (String)params.get("width");String height = (String)params.get("height");if (!params.containsKey("_template")) {params.put("_template", "com/atlassian/confluence/extra/widgetconnector/templates/youtube.vm");}if (StringUtils.isEmpty(width)) {params.put("width", "400px");} else if (StringUtils.isNumeric(width)) {params.put("width", width.concat("px"));}if (StringUtils.isEmpty(height)) {params.put("height", "300px");} else if (StringUtils.isNumeric(height)) {params.put("height", height.concat("px"));}return params;}

取出widthheight来判断是否为空,为空则设置默认值。关键的_template参数来了,如果外部传入的参数没有_template,则设置默认的Youtube模板。如果传入了,就使用传入的,也就是说,aaaa是成功的传进来了。

大概翻了一下Widget Connector里面的Renderer,大部分是不能设置_template的,是直接写死了,也有一些例外,如Youtube,Viddler,DailyMotion等,是可以从外部传入_template的。

能传递_template了,接下来看下是如何取模板和渲染模板的。

跟进this.velocityRenderService.render,也就是com/atlassian/confluence/extra/widgetconnector/services/DefaultVelocityRenderService.class里面的render方法。

    public String render(String url, Map<String, String> params) {String width = (String)params.get("width");String height = (String)params.get("height");String template = (String)params.get("_template");if (StringUtils.isEmpty(template)) {template = "com/atlassian/confluence/extra/widgetconnector/templates/embed.vm";}if (StringUtils.isEmpty(url)) {return null;} else {Map<String, Object> contextMap = this.getDefaultVelocityContext();Iterator var7 = params.entrySet().iterator();while(var7.hasNext()) {Entry<String, String> entry = (Entry)var7.next();if (((String)entry.getKey()).contentEquals("tweetHtml")) {contextMap.put(entry.getKey(), entry.getValue());} else {contextMap.put(entry.getKey(), GeneralUtil.htmlEncode((String)entry.getValue()));}}contextMap.put("urlHtml", GeneralUtil.htmlEncode(url));if (StringUtils.isNotEmpty(width)) {contextMap.put("width", GeneralUtil.htmlEncode(width));} else {contextMap.put("width", "400");}if (StringUtils.isNotEmpty(height)) {contextMap.put("height", GeneralUtil.htmlEncode(height));} else {contextMap.put("height", "300");}return this.getRenderedTemplate(template, contextMap);}}

_template取出来赋值给template,其他传递进来的参数取出来经过判断之后放入到contextMap,调用getRenderedTemplate函数,也就是调用VelocityUtils.getRenderedTemplate

   protected String getRenderedTemplate(String template, Map<String, Object> contextMap){return VelocityUtils.getRenderedTemplate(template, contextMap);}

一路调用,调用链如下图,最后来到/com/atlassian/confluence/util/velocity/ConfigurableResourceManager.classloadResource函数,来获取模板。

这里调用了4个ResourceLoader去取模板。

com.atlassian.confluence.setup.velocity.HibernateResourceLoader
org.apache.velocity.runtime.resource.loader.FileResourceLoader
org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader
com.atlassian.confluence.setup.velocity.DynamicPluginResourceLoader

这里主要看下Velocity自带的FileResourceLoaderClasspathResourceLoader

FileResourceLoader会对用户传入的模板路径使用normalizePath函数进行校验

可以看到,过滤了/../,这样就导致没有办法跳目录了。

路径过滤后调用findTemplate查找模板,可看到,会拼接一个固定的path,这是Confluence的安装路径。

也就是说现在可以利用FileResourceLoader来读取Confluence目录下面的文件了。

尝试读取/WEB-INF/web.xml文件,可以看到,是成功的加载到了该文件。

但是这个无法跳出Confluence的目录,因为不能用/../

再来看下ClasspathResourceLoader

    public InputStream getResourceStream(String name) throws ResourceNotFoundException {InputStream result = null;if (StringUtils.isEmpty(name)) {throw new ResourceNotFoundException("No template name provided");} else {try {result = ClassUtils.getResourceAsStream(this.getClass(), name);
......}

跟进ClassUtils.getResourceAsStream

    public static InputStream getResourceAsStream(Class claz, String name) {while(name.startsWith("/")) {name = name.substring(1);}ClassLoader classLoader = Thread.currentThread().getContextClassLoader();InputStream result;if (classLoader == null) {classLoader = claz.getClassLoader();result = classLoader.getResourceAsStream(name);} else {result = classLoader.getResourceAsStream(name);if (result == null) {classLoader = claz.getClassLoader();if (classLoader != null) {result = classLoader.getResourceAsStream(name);}}}return result;}

会跳到/org/apache/catalina/loader/WebappClassLoaderBase.class

跟进,发现会拼接/WEB-INF/classes,而且其中也是调用了normalize对传入的路径进行过滤。。

这里还是可以用../跳一级目录。

尝试读取一下../web.xml,可以看到,也是可以读取成功的,但是仍然无法跳出目录。

我这里测试用的版本是6.14.1,而后尝试了file://,http://https://都没有成功。后来我尝试把Cookie删掉,发现在Linux环境下面还是可以读取文件,Windows的6.14.1版本是需要登陆的,但是跳不出目录。应急在这里卡住了。

而后的几天,有大佬用file://协议可以跳出目录限制,我惊了,我确定当时是已经试过了,没有成功的。看了大佬的截图,发现用的是6.9.0的版本,我下载了,尝试了一下,发现真的可以。而且在6.9.0版本中,Windows和Linux环境都不需要登陆。

问题还是在ClasspathResourceLoader上面,步骤和之前的是一样的,断到/org/apache/catalina/loader/WebappClassLoaderBase.classgetResourceAsStream方法

前面拼接/WEB-INF/classes获取失败后,继续往下进行。

跟进findResource,函数前面仍然获取失败

关键的地方就在这里,会调用super.findResource(name),这里返回了URL,也就是能获取到对象。

不仅如此,这里还可以使用其他协议(https,ftp等)获取远程的对象,意味着可以加载远程的对象。

获取到URL对象之后,继续回到之前的getResourceAsStream,可以看到,当返回的url不为null时,

会调用url.openStream()获取数据。

最终获取到数据给Velocity渲染。

尝试一下

至于6.14.1为啥不行,赶着应急,后续会跟,如果有新的发现,会同步上来,目前只看到ClassLoader不一样。

6.14.1

6.9.0

这两个loader的关系如下

现在可以加载本地和远程模板了,可以尝试进行RCE。

关于Velocity的RCE,基本上payload都来源于15年blackhat的服务端模板注入的议题,但是在Confluence上用不了,因为在调用方法的时候会经过velocity-htmlsafe-1.5.1.jar,里面多了一些过滤和限制。但是仍然可以利用反射来执行命令。

python -m pyftpdlib -p 2121开启一个简单的ftp服务器,将payload保存成rce.vm,保存在当前目录。

_template设置成ftp://localhost:2121/rce.vm,发送,成功执行命令。

对于命令回显,同样可以使用反射构造出payload,执行ipconfig的结果。

漏洞影响

根据 ZoomEye 网络空间搜索引擎对关键字 "X-Confluence" 进行搜索,共得到 61,856 条结果,主要分布美国、德国、中国等国家。

全球分布(非漏洞影响范围)

中国分布(非漏洞影响范围)

漏洞检测

2019年4月4日,404实验室公布了该漏洞的检测PoC,可以利用这个PoC检测Confluence是否受该漏洞影响。

参考链接

  • 漏洞检测PoC
  • Remote code execution via Widget Connector macro - CVE-2019-3396
  • 漏洞预警 | Confluence Server 远程代码执行漏洞

原文地址 :https://paper.seebug.org/884/

(转)Confluence 未授权 RCE (CVE-2019-3396) 漏洞分析相关推荐

  1. 蓝海卓越计费管理系统 debug.php_未授权RCE

    文章目录   Fofa搜索:title=="蓝海卓越计费管理系统".   点开第一个搜索结果:   /debug.php未授权访问,文件提供一个命令执行的接口:   输入Poc验证 ...

  2. 用友NC BeanShell_未授权RCE

    文章目录 漏洞信息 Fofa Zoomeye 漏洞信息   漏洞信息:CNVD-2021-30167,2021年6月通报.存在一个未授权的执行命令的接口,访问即可执行任意命令.   影响版本:NC 6 ...

  3. cve-2019-11581 Atlassian Jira未授权服务端模板注入漏洞

    漏洞描述 Atlassian Jira是澳大利亚Atlassian公司的一套缺陷跟踪管理系统.该系统主要用于对工作中各类问题.缺陷进行跟踪管理. Atlassian Jira Server和Jira ...

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

    参考: https://jira.atlassian.com/browse/CONFSERVER-57974 https://github.com/knownsec/pocsuite3/blob/ma ...

  5. cve-2019-11076 Cribl UI 1.5.0 未授权命令执行漏洞分析

    Cribl是一款日志分析工具.Cribl UI是其中的一个用户界面. Cribl UI 1.5.0版本中存在安全漏洞.远程攻击者可借助未认证的Web请求利用该漏洞运行任意命令. 前言 前几天瞎逛 Gi ...

  6. 常用的30+种未授权访问漏洞汇总

    未授权访问漏洞汇总预览 1 .FTP 未授权访问(21) 2 .LDAP 未授权访问(389) 3 .Rsync 未授权访问(873) 4 .ZooKeeper 未授权访问(2181) 5 .Dock ...

  7. Jenkins未授权访问

    Jenkins Jenkins简介 Jenkins是一个开源软件项目,是基于Java开发的一种持续集成工具,它能把软件开发过程形成工作流.默认情况下Jenkins面板中用户可以选择执行脚本界面来操作一 ...

  8. 通达+oa+php+文件+乱,通达OA任意文件删除/OA未授权访问+任意文件上传RCE漏洞复现...

    0x00 简介 通达OA采用基于WEB的企业计算,主HTTP服务器采用了世界上最先进的Apache服务器,性能稳定可靠.数据存取集中控制,避免了数据泄漏的可能.提供数据备份工具,保护系统数据安全.多级 ...

  9. 安全扫描失败无法上传_Apache Solr 未授权上传(RCE)漏洞的原理分析与验证

    漏洞简介 Apache Solr 发布公告,旧版本的ConfigSet API 中存在未授权上传漏洞风险,被利用可能导致 RCE (远程代码执行). 受影响的版本: Apache Solr6.6.0 ...

最新文章

  1. do{ ...}while(0)应用技巧
  2. JVM 发生内存溢出的 8 种原因、及解决办法
  3. 700 页的机器学习笔记火了!完整版开放下载
  4. 绘画 某种字体 以某种折行规则 最后画出的text有多大
  5. 网工小课堂(part1)--计算机网络概论
  6. c语言链表写贪吃蛇思路,C语言构建的链表贪吃蛇
  7. 比java好的_Java Spring真的比直接进行Java编程好吗
  8. 行云管家堡垒机 导入腾讯云主机
  9. 即席和即兴_即兴说话小课堂
  10. Android 自动换行流式布局的RadioGroup
  11. iphone iPhone开发中为UINavigationBar设置背景图片方法
  12. iOS项目开发实战——通过Http Get方式与server通信
  13. 17、【易混淆概念集】第十一章1 项目风险 风险临界值 VS 风险承受力 风险管理流程 风险管理及变更流程 规划风险管理 识别风险
  14. MAX262程控滤波器
  15. PPT转图片(Java)
  16. flash游戏开发02_引入flixel框架的helloworld
  17. 《蜗居》的经典台词(转载)一
  18. 苹果LIVE PHOTOS实况照片转普通静态照片jpg
  19. 关于海外博士和启明计划
  20. buuctf————findkey

热门文章

  1. Cisco IP Phone XML Object Definitions
  2. 交大BBS上一个失恋的女生无意下写的却感动了万人的真实的帖...
  3. PC-Cillin被黑(嘿嘿)
  4. 【2019-总结】初中毕业暑假集训No.6
  5. 元宇宙真正建构了属于自己的生态闭环
  6. 服务器出现宕机该如何解决
  7. Bootstrap (Web框架)
  8. php获取sql字符编码格式,PHP和MYSQL的编码问题
  9. python中.find函数的使用方法及实例_python 如何使用find和find_all爬虫、找文本的实现...
  10. java毕业设计MVC土特产交易平台系统源码+系统+数据库+lw文档+调试运行