
[Confluence 7.12.5之前的版本存在Ognl表达式注入漏洞,未授权用户可以通过该漏洞在Confluence服务器上执行任意代码。漏洞成因在于程序未对前端输入的参数值做严格校验。]
背景介绍
Confluence是一个专业的企业知识管理与协同软件,也可以用于构建企业wiki。它使用Java语言编写, SpringMVC作为中间业务层, webwork2框架作为web层,数据层使用Hibernate。其中,webwork2框架使用Ognl作为表达式语言。
Ognl的全称是Object Graphic Navigation Language(对象图导航语言),它能够通过简单一致的表达式语法实现:存取java对象的任意属性,调用java对象的方法,遍历整个对象的结构图,进行字段类型转化等功能。Struts2系列漏洞就是因为程序未对Ognl表达式做严格过滤,导致的命令执行。
本文主要从代码层面对Confluence CVE-2021-26084 Ognl表达式命令执行漏洞产生的原因进行分析。
测试环境准备
靶场环境选择:atlassian-confluence-7.12.3版本

漏洞复现



上图中可以看到,Ognl表达式被执行了,证明漏洞存在。
漏洞分析
因为是Ognl表达式注入漏洞,所以选择在Ognl.getValue()函数处进行断点。此时,queryString参数的程序调用链,如下图所示:

queryString参数value值的调用链,如下图所示:

由此,我们可以看出大概的程序处理逻辑 :
下表中是pages/doenterpagevariables.action页面的所有参数:
#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") |

2)title传入参数时,后端依然不会接收前端输入的值,也可以说明不存在漏洞。


在这一步,object对象已经获取到了传过来的value值。
2)接下来,我们将逐步演示object获取到value值的方式。


运行到下一步的时候,object获取到了value。



对比
"value='$!queryString'" "value='$pageTemplate.id'" "value='$linkCreation'" "value=title" "value=parentPageId" "value=fromPageId" "value=spaceKey"

这就是为什么两个漏洞所在点的值都有$符号。
但为什么templateId参数值也包含了$,而程序并没有将前端值传入value中?
下面做详细分析:
#tag ("Hidden" "name='templateId'" "value='$pageTemplate.id'")
测试使用queryString传入恶意value时的场景,发现获取前端数据绑定到后端value参数前的程序调用链,如下所示:

查看当使用templateId传入恶意value时,程序从context上下文中取pageTemplate的值。此处的调用链与queryString一致,如下图所示:

但是当render运行完毕后,发现并没有将前端value值写入到writer中,

下图可以看出此时的context上下文中确实已经获取到了前端传入的templateId值,但是render函数调用链并没有赋值给result参数,导致返回了$pageTemplate.id,



SafeExpressionUtil.isSafeExpression()函数内部逻辑如下图所示:

可以看到:
第一个set禁用了Ognl表示式静态方法、静态参数、构造函数等的调用;
第二和第三个set禁用了常见的获取执行java反射的方式;
第四个set禁用了常用的获取模板context上下文的方式。
因为黑名单的存在,所以常用的获取Runtime类如"".getClass()等代码利用方式已经不可以使用了,但是可以使用
""["class"].forName["java.lang.Runtime"]
进行绕过。
漏洞修复
声明:
本篇文章仅用于技术研究,不恰当使用会造成危害,严禁违法使用 ,否则后果自负。
参考内容:
1、https://confluence.atlassian.com/doc/confluence-security-advisory-2021-08-25-1077906215.html
https://github.com/httpvoid/writeups/blob/main/Confluence-RCE.md
2、https://www.anquanke.com/post/id/253398


