CVE-2021-26084

发布于 2021-09-16  1103 次阅读


漏洞描述

  CVE-2021-26084这个漏洞名称为Confluence OGNL注入漏洞,具体归为远程任意命令执行漏洞,最早于2021年8月26日Atlassian官方发布公告,披露了 Atlassian Confluence 远程代码执行漏洞,攻击者在认证或无需认证的情况下,构造恶意请求,造成OGNL表达式注入,从而执行任意代码,控制服务器。
  Confluence是Atlassian公司的一个专业的企业知识管理与协同软件,也可以用于构建企业wiki,因此,Confluence的使用面很广,并且国内的使用情况主要都是较低版本且较多企业均是破解使用,没有订阅更新功能,所以具体的影响范围会比预估的更为广泛。

什么是OGNI

  OGNL(Object Graph Navigation Language)即对象图形导航语言,是一个开源的表达式引擎。使用OGNL,你可以通过某种表达式语法,存取Java对象树中的任意属性、调用Java对象树的方法、同时能够自动实现必要的类型转化。如果我们把表达式看做是一个带有语义的字符串,那么OGNL无疑成为了这个语义字符串与Java对象之间沟通的桥梁。
  OGNL进行对象存取操作的API在Ognl.java文件中,分别是getValue、setValue两个方法。getValue通过传入的OGNL表达式,在给定的上下文环境中,增加root对象里取值。
  OGNL的API无论何种复杂的功能,OGNL会将其最终映射到OGNL的三要素中通过调用底层引擎完成计算,OGNL的三要素即上述方法中的三个参数expression、root、context。

站点搭建

  该系统是多平台系统,因为手头比较成熟稳定的测试机还没有搞定,所以病急乱投医使用了自己的虚拟攻击机——Debian(kali)作为底层的系统进行搭建,图个方便快捷,什么东西都有,就不需要自己再去搭建,避免一些踩坑的操作,毕竟我这最简单的装个软件都要把所有坑都踩一遍的人啊!当然在系统搭建过程中的踩坑也会在这边记录下来。
  废话差不多了,进入正题
  首先是获取安装网站程序:

  这个安装程序可以直接通过官网进行获取,我这边版本选择的是atlassian-confluence-7.12.4,这个版本也是最早的出现payload的版本。

wget https://product-downloads.atlassian.com/software/confluence/downloads/atlassian-confluence-6.14.1-x64.bin

  加执行权限

chmod +x atlassian-confluence-6.14.1-x64.bin

  执行

./atlassian-confluence-6.14.1-x64.bin

  一路默认即可,当然,你想改端口自己留意即可,安装过程中有修改端口的设置,安装后修改端口我没有弄,不要问我,自己研究一下,毕竟不是搭建使用的网站,就复现个漏洞,要啥自行车啊。

image-20210915163251363

  安装完成后,文件路径如下

/opt/atlassian/confluence/

网站系统破解

  下载网上的破解器,破解软件一大堆,我这里使用的是confluence破解工具.zip
  首先先访问站点获取站点的Server ID,后面破解要用到,安装完成后会看到下面这个页面,在这里选择生产安装"Production Installation" 谷歌翻译大法好

image-20210915165532942

  记录下来这个Server ID

image-20210915170042700

  接下来进行正式的破解工作,首先要将Confluence先关闭

/opt/atlassian/confluence/bin/stop-confluence.sh

  然后将atlassian-extras-decoder-v2-3.4.1.jar文件复制到本地,该文件路径如下,具体你装服务器的时候怎么弄到本地,稍稍自己考虑一下

/opt/atlassian/confluence/confluence/WEB-INF/lib/atlassian-extras-decoder-v2-3.4.1.jar

  记得文件名字要改一下,改成atlassian-extras-2.4.jar
  接下来进行本地操作:

  1. 打开下载的破解工具,运行confluence_keygen.jar ,我不确定其他几个处理文件能不能打开,反正我是直接运行的confluence_keygen.jar

  1. 将记录的server ID填入,Name随便填。

  1. 点击gen, 生成key并记录。

  1. 点击patch,选择刚下传到本地的atlassian-extras-2.4.jar文件进行破解,显示Jar successfully patched.即为破解成功,这软件还贴心的生成了一份备份。

  1. 破解完成后,将文件改回原名并回传至服务器原路径下覆盖原文件。完事后重启Confluence,重启就不截图了哈,用下面的两个命令。关闭+开启=重启
/opt/atlassian/confluence/bin/stop-confluence.sh        #关闭Confluence
/opt/atlassian/confluence/bin/start-confluence.sh       #开启Confluence
  1. 把刚刚生成的key复制到页面中

image-20210915172936007

  1. 看到这个页面就说明你破解成功了

image-20210915173005098

继续站点搭建

  到这里,因为我并不是使用这个网站系统,所以我这边一些从简,选择内置的数据库进行自动的搭建。选择"Built in (for evaluation or demonstration)",等待它初始化完成。

image-20210915173325774

  选择内置数据库后等待初始化完成,会弹出以下页面,你可以根据自己的情况去选择,我这里选择示例站点,一切从简,能用、能复现漏洞就可以了。

image-20210915174021216

  用户管理随意,我这里选择"在Confluence中管理用户和组"

image-20210915174135936

  设置管理员用户

image-20210915174200111

  搭建结束!

image-20210915174314096

网站搭建的辛酸历程

  一切看起来都是那么的顺利,其实中间踩坑的挺多的,比如说使用了破解工具破解完了之后,刷新页面还是需要输入key,初见是以为是没有破解成功,又去找了临时邮箱去注册了官网获取试用权限,结果试用权限导入不成功。
  还有一个就是破解出来的key是没有mysql数据的连接模块的,需要自己去官网下载相关的模块,兴冲冲的跑去下载,但是因为底层是Debian(kali)所以并没有适用的,结果可想而知,下载的模块不能使用,然后没办法只能装内置数据库的演示站点,只要能跑起来怎么样都行。卑微

CVE-2021-26084漏洞复现

  终于到重点了。这算是我个人第一次正式的追洞,还是已知结果的追洞,重点就放在得到每个结果的过程中。一点一点来嘛,学习是一个漫长的过程。

分析补丁和版本更迭差异发现

  首先呢,漏洞存在版本为Confluence-7.12.4,在漏洞通报的同时,官方也发布了相应的修补补丁,当然也可以理解为官方更新了站点系统的版本。那么在这里有两种方式去进行下去:

  1. 分析补丁,这是原来参考文章中的做法,通过补丁一系列的动作去查看产生变化的文件,然后根据输出内容查看相应的文件并进行分析
  2. 快速对比两个版本之间文件的差异,可以使用git上传GitHub去快速比对,更新的时候记得备份带有漏洞的版本。

  这里说一点,GitHub去对比差异也是有些麻烦的,GitHub仓库网页端不允许上传大于25Mb的文件,客户端不允许上传大于100Mb的文件,所以这个方法不是很推荐用,当然,前提是你会分析补丁可以不用这种方法,不会的话还是老老实实借用GitHub吧。
  回到正题,原参考文章中通过分析补丁脚本,定位到补丁更新的文件有三个,文章中只展示了最为重要的一个文件,补丁脚本的内容我也有所浏览,得益于补丁脚本是shll脚本,能够进行简单的分析,重要文件如下:

<confluence_dir>/confluence/pages/createpage-entervariables.vm

  之后我也自己运行了一下脚本看了脚本回显,原文章中这处出错,补丁脚本实际更新了五个文件

confluence/users/user-dark-features.vm
confluence/login.vm
confluence/pages/createpage-entervariables.vm
confluence/template/custom/content-editor.vm
confluence/WEB-INF/atlassian-bundled-plugins/confluence-editor-loader*.jar

  具体回显如下:

./cve-2021-26084-update.sh 
chdir '/opt/atlassian/confluence'

File 1: 'confluence/users/user-dark-features.vm':
   a. backing up file.. done
   b. updating file.. done
   c. showing file changes..
   d. validating file changes.. ok
   e. file updated successfully!

File 2: 'confluence/login.vm':
   a. backing up file.. done
   b. updating file.. done
   c. showing file changes..
   d. validating file changes.. ok
   e. file updated successfully!

File 3: 'confluence/pages/createpage-entervariables.vm':
   a. backing up file.. done
   b. updating file.. done
   c. showing file changes..
24c24
<                 #tag ("Hidden" "name='queryString'" "value='$!queryString'")
---
>                 #tag ("Hidden" "name='queryString'" "value=queryString")
26c26
<                 #tag ("Hidden" "name='linkCreation'" "value='$linkCreation'")
---
>                 #tag ("Hidden" "name='linkCreation'" "value=linkCreation")
   d. validating file changes..ok
   e. file updated successfully!

File 4: 'confluence/template/custom/content-editor.vm':
   a. backing up file.. done
   b. updating file.. done
   c. showing file changes..
64c64
<         #tag ("Hidden" "name='queryString'" "value='$!queryString'")
---
>         #tag ("Hidden" "name='queryString'" "value=queryString")
85c85
<             #tag ("Hidden" "id=sourceTemplateId" "name='sourceTemplateId'" "value='${templateId}'")
---
>             #tag ("Hidden" "id=sourceTemplateId" "name='sourceTemplateId'" "value=templateId")
   d. file updated successfully!

File 5: 'confluence/WEB-INF/atlassian-bundled-plugins/confluence-editor-loader*.jar':
   a. extracting templates/editor-preload-container.vm from confluence/WEB-INF/atlassian-bundled-plugins/confluence-editor-loader-7.12.4.jar.. 
Archive:  confluence/WEB-INF/atlassian-bundled-plugins/confluence-editor-loader-7.12.4.jar
  inflating: ./templates/editor-preload-container.vm  
   b. updating file.. done
   c. showing file changes..
56c56
< #tag ("Hidden" "id=syncRev" "name='syncRev'" "value='$!{action.syncRev}'")
---
> #tag ("Hidden" "id=syncRev" "name='syncRev'" "value=syncRev")
   d. validating file changes.. ok
   e. updating confluence/WEB-INF/atlassian-bundled-plugins/confluence-editor-loader-7.12.4.jar with ./templates/editor-preload-container.vm..updating: templates/editor-preload-container.vm (deflated 59%)
-rw-r--r-- 1 root root 13370 9月  15 20:38 confluence/WEB-INF/atlassian-bundled-plugins/confluence-editor-loader-7.12.4.jar
   f. cleaning up temp files..ok
   g. extracting templates/editor-preload-container.vm from confluence/WEB-INF/atlassian-bundled-plugins/confluence-editor-loader-7.12.4.jar again to check changes within JAR.. 
Archive:  confluence/WEB-INF/atlassian-bundled-plugins/confluence-editor-loader-7.12.4.jar
  inflating: ./templates/editor-preload-container.vm  
   h. validating file changes for file within updated JAR.. ok
   i. cleaning up temp files..ok

Update completed!

  根据脚本回显我们可以很明显的看到,最为重要的文件可以定位到Flie 3和File 4中,接下来就是查看这些文件的路由文件。
  怎么说呢,根据已知结果确实是这样,后来仔细看了一下回显,觉得File 5中也应该存在相应的漏洞,这里的理由就是File 5与之前的两个文件所使用的的模板十分相似,不过,事实证明并非如此,File 5这个文件是我们后面所定位的路由文件的Jar包。

找寻路由文件

  这里查找路由文件消耗了非常长的时间,因为之前并没有做过相关的动作,这里询问了几位大佬,直接上命令。

grep -ari "createpage-entervariables"

  grep命令参考:Linux grep 命令
  这个命令没有设置过滤,最终的回显结果数量还算比较大的,需要仔细分析一下,主要是日志也会混在里面。
  然后当你辛辛苦苦分析完结果之后你会发现并没有你想要的,这里涉及到路由文件的找法,这一点也是我卡了很长时间的一个原因。
  一般情况下,路由文件都会存储在网站文件的WEB-INF文件夹下或分散在不同文件下,前者很好说,找就完了,后者这个需要你对这个网站的文件比较熟悉,会相对好弄一些,而这次遇到的网站他的路由文件,应该说我们所需要找到的路由文件在Jar包中,属于二进制文件,这是这次的一个难点,怎么在Jar包中找到我们所需要的文件?
  这里运用到另外一个grep的命令——zipgrep。参考连接:[How to search for a string in JAR files]
  首先,Jar包的本质是什么?没错,是压缩包,zipgrep是Linux自带的命令,可以递归查询zip包中内容进行匹配。但是zipgrep只能查询单个文件,所以我们要构造能够查询目标目录内所有Jar包的内容的命令,而find这个命令则是比较好的选择,这也是借鉴了grep管道符过滤的方法进行构造。

find /opt/atlassian/confluence/confluence/WEB-INF/ -name "*.jar" -exec zipgrep "createpage-entervariables" '{}' \;

# 回显
# com/atlassian/confluence/plugins/createcontent/js/blueprint-object.js:                var createFromTemplateUrl = Confluence.getContextPath() + "/pages/createpage-entervariables.action"
# com/atlassian/confluence/plugins/createcontent/js/blueprint-object-min.js:l.extend(d,a),c)};else throw Error("No way to process item: "+g);a.howToUseTemplate?Confluence.Blueprint.HowToUse.check(c,a,h):h()}}else if(a.templateId){AJS.trigger("analytics",{name:c.id+".submit.template",data:{spaceKey:k,templateId:a.templateId}});h=Confluence.getContextPath()+"/pages/createpage-entervariables.action?templateId\x3d"+encodeURIComponent(a.templateId)+"\x26spaceKey\x3d"+encodeURIComponent(k)+"\x26title\x3d"+encodeURIComponent(a.title||"")+"\x26newSpaceKey\x3d"+encodeURIComponent(k);
# com/atlassian/confluence/plugins/templates/common-listpagetemplatesbytype.vm:                                <a href="$req.contextPath/pages/createpage-entervariables.action?spaceKey=$htmlUtil.urlEncode($key)&templateId=$pageTemplate.id"> $action.getText("action.create.page.from.template") </a>
# xwork.xml:        <action name="createpage-entervariables" class="com.atlassian.confluence.pages.actions.PageVariablesAction">
# xwork.xml:            <result name="error" type="velocity">/pages/createpage-entervariables.vm</result>
# xwork.xml:            <result name="input" type="velocity">/pages/createpage-entervariables.vm</result>
# xwork.xml:            <result name="success" type="velocity">/pages/createpage-entervariables.vm</result>
# xwork.xml:            <result name="error" type="velocity">/pages/createpage-entervariables.vm</result>
# xwork.xml:            <result name="input" type="velocity">/pages/createpage-entervariables.vm</result>
# com/atlassian/confluence/util/UrlUtils.class:匹配到二进制文件 (标准输入)
# com/atlassian/confluence/core/ConfluenceActionSupport.properties:#createpage-entervariables.vm

  这样通过回显,我们可以很明确的定位到所要找寻的文件是xwork.xml,因为我构造的命令还是有一定的欠缺并没有回显出更准确的文件路径,但是找到一个文件难道不很简单嘛?

grep -ri xword.xml [flie path]

# 回显:/opt/atlassian/confluence/confluence/WEB-INF/lib/confluence-7.12.4.jar

分析路由文件

  找到路由文件了,接下来进行分析,通过相应的分析可以看到,createpage-entervariables.vm文件对应的动作为input、error、success、novariables四个动作,后面访问分析了createpage-entervariables.vm进一步可以确定,无论有没有登录或者拥有账户,只要是使用此模板的路由都会触发漏洞且在未授权的情况下。

image-20210915220959122

  这个分析涉及到网站路由文件的相关内容,我这里就不再详细展开,后续如果有需要会另外写篇笔记记录这个。
到这一步我们就可以真正的开始复现这个漏洞了!铺垫是不是有点长,但这是必要的

重要文件的分析及漏洞复现——/pages/createpage-entervariables.vm

<html>
    <head>
        #requireResource("confluence.web.resources:page-templates")
        <title>$action.getText("page.template.wizard")</title>
    </head>

    <body>
        #parse ( "/template/includes/actionerrors.vm" )
        #applyDecorator("root")
        #decoratorParam("helper" $action.helper)
        #decoratorParam("context" "space-pages")
        #decoratorParam("mode" "create-page")

        <div class="padded">
            <div class="steptitle" style="margin-top: 10px">$action.getText('pagevariables.step2')</div>
            <p>$action.getText('text.pagevariables.step2.instructions')</p>

            <div class="smallfont view-template">
                <div class="wiki-content">$action.renderedTemplateContent</div>
            </div>

            <form name="filltemplateform" method="POST" action="doenterpagevariables.action">
                #form_xsrfToken()
                #tag ("Hidden" "name='queryString'" "value='$!queryString'")
                #tag ("Hidden" "name='templateId'" "value='$pageTemplate.id'")
                #tag ("Hidden" "name='linkCreation'" "value='$linkCreation'")
                #tag ("Hidden" "name='title'" "value=title")
                #tag ("Hidden" "name='parentPageId'" "value=parentPageId")
                #tag ("Hidden" "name='fromPageId'" "value=fromPageId")
                #tag ("Hidden" "name='spaceKey'" "value=spaceKey")

                <div class="aui-toolbar2" role="toolbar">
                    <div class="aui-toolbar2-inner">
                            <input class="aui-button" type="button" value="$action.getText('back.witharrows.name')" onclick="javascript:history.go(-1)">
                            #tag( "Submit" "name='confirm'" "id=confirm" "value='next.name'" "theme='notable'" "cssClass='aui-button'")
                    </div>
                </div>
            </form>

            #parse ( "/pages/page-breadcrumbs.vm" )
        </div>

        #end
    </body>
</html>

 先GET访问一下,看看效果

 image-20210916160547297

  页面中并没有太多的信息,看一下数据包

image-20210916160830979

  黑盒分析中是尝试了在模板中输入标签名称作为参数,发现这些值实际上是从请求参数中获取的,并反映在响应中。所以对于这个文件可以很明显的看到"queryString"等几个参数都是可以通过POST进行提交的,对比补丁脚本的输出内容,可以看到修复方式也是将这个提交的参数变成了字符串,那么下面就是考虑一下什么东西可以提交?
  可以先测试一下关于这种模板的标准表达式:例如#{} %{} ${}这些,使用POST方法提交,你会发现并没有什么效果,然后你会想到去闭合原来存在的引号、括号等闭合方式,同样没有什么效果,再来会不会是有反斜杠转义符,然后输入"\"进行测试,然后还是没效果,为什么呢?这是一个很大的问题,为什么文章上的测试方法照搬过来也没有用呢?
  这样问一下,你有没有考虑过数据包存在一些错误?或者说访问的网站路径可能是错的?
  为什么会想到这个……因为直觉性的操作是将GET包直接更改HTTP请求方法去构造POST包而并非是让浏览器自己生成POST包进行提交,所以GET数据包中缺少一个HTTP头部字段"Content-Type:application/x-www-form-urlencoded"
  看了一下手头的POC脚本,可能存在漏洞的路径是比较多的,所以说换一换试一下。
  解决了数据包的问题,提交一些表达式,测试一下效果,可以发现能够插入字符串了,这里为什么说是字符串呢,因为这个表达式并没有执行,如果执行应该要回显表达式的结果"999",这样的话就要考虑闭合了。

image-20210916171204828

  关于闭合,常见也就是括号和引号,使用引号的时候并没有什么特殊的变化,那么考虑加一下反斜杠,在输入一个反斜杠的时候,queryString字段后的value直接不回显了,可以说有戏,双斜杠的时候会回显出value,值是一个斜杠,基本上可以确定是存在需要闭合的引号的。

image-20210916172433877

image-20210916172622391

  再说一点就是为什么要用反斜杠进行测试,反斜杠算是转义符,在多个语言中用来输出一些闭合的符号,比如说括号、引号等等,转义掉一个符号看是否有效果,也可以确定是否存在闭合的符号。这部分结束之后,就开始尝试闭合引号,在输入单引号提交后,value返回的是',可以看到单引号被实体编码了,然后对单引号再进行处理,后面发现将单引号Unicode编码后可以正常闭合。

\u002f——>\
\u0022——>"      提交上去之后value回显"
\u0027——>'      提交上去之后value没有回显,同反斜杠一样

  由上面的结果就可以看出来了,闭合方式是单引号——\u0027,你是不是在想直接构造一个payload:

queryString=aaaa\u0027#{3*333}\u0027bbb

  然后恭喜你,不回显,傻了吧唧的你,太着急了吧,通过另外的测试,这里发现使用queryString=aaaa\u0027%2b\u0027bbb该payload,返回值则会返回value="aaaabbb",意味着上下文被破坏并且我们的输入被连接起来。
  所以加个拼接吧,"%2b",再试一试,你问我在哪里加?自己试一试总不能都教给你吧。

image-20210916175600036

系统卸载

  有些人呢测试完了为了保证自己的测试机的纯净,想要卸载掉测试机上的这些安装的系统,所以呢,系统卸载也在这里写出来。我才不说是我

# 卸载命令:
/opt/atlassian/confluence/uninstall
# 执行完会提示我们有些日志或者什么文件没有删除,如果是第一次装可以直接rm -rf 删掉。
The following files/directories were not deleted since they contain files with potentially important logs or configurations you may wish to retain. If you do not need to keep these files, however, you can safely delete these directories.
/opt/atlassian/confluence/logs
/opt/atlassian/confluence/confluence/WEB-INF/atlassian-bundled-plugins
/opt/atlassian/confluence/confluence/WEB-INF
/opt/atlassian/confluence/confluence
/opt/atlassian/confluence
 
# 同时删掉 confluence 的数据目录则算卸载干净:
rm -rf /var/atlassian/application-data/confluence/

写在最后

  到这里,我个人这边就结束了,后面就是利用这个漏洞进行系统命令执行的payload的构造,期间是绕过网站的isisSafeExpression,我这里就不继续整理记录了,后续如果想要学习,你可以到原文章CVE-2021-26084 Remote Code Execution on Confluence Servers继续进行,或这边比较详细的分析教程通过详细的代码的角度去重新分析这个漏洞CVE-2021-26084:Confluence Webwork Ognl表达式注入漏洞分析

关于漏洞其他的内容

FOFA字段

app="ATLASSIAN-Confluence"

影响范围

  易受 CVE-2021-26084 攻击的 Atlassian 产品是使用以下版本的 Confluence Server 和 Data Center 的产品:

  • 所有 4.xx 版本。
  • 所有 5.xx 版本。
  • 所有 6.0.x 版本。
  • 所有 6.1.x 版本。
  • 所有 6.2.x 版本。
  • 所有 6.3.x 版本。
  • 所有 6.4.x 版本。
  • 所有 6.5.x 版本。
  • 所有 6.6.x 版本。
  • 所有 6.7.x 版本。
  • 所有 6.8.x 版本。
  • 所有 6.9.x 版本。
  • 所有 6.10.x 版本。
  • 所有 6.11.x 版本。
  • 所有 6.12.x 版本。
  • 6.13.23 之前的所有 6.13.x 版本。
  • 所有 6.14.x 版本。
  • 所有 6.15.x 版本。
  • 所有 7.0.x 版本。
  • 所有 7.1.x 版本。
  • 所有 7.2.x 版本。
  • 所有 7.3.x 版本。
  • 7.4.11 之前的所有 7.4.x 版本。
  • 所有 7.5.x 版本。
  • 所有 7.6.x 版本。
  • 所有 7.7.x 版本。
  • 所有 7.8.x 版本。
  • 所有 7.9.x 版本。
  • 所有 7.10.x 版本。
  • 7.11.6 之前的所有 7.11.x 版本。
  • 7.12.5 之前的所有 7.12.x 版本。

Confluence Cloud 客户不受此漏洞的影响。
升级到版本 6.13.23、7.11.6、7.12.5、7.13.0 或 7.4.11 的客户不受影响。

缓解措施

  建议将 Atlassian Confluence Server 和 Data Center 更新到最新版本7.13.0 (TLS)。你可以在Atlassian 的下载中心找到最新版本。剩下的就没我什么事了哈
  如果你无法安装最新升级,请参阅 Atlassian 安全公告中的缓解部分,了解有关如何通过为你的Confluence服务器托管的操作系统运行脚本来缓解此漏洞的信息。