原文链接:Understanding the Recent Confluence Vulnerability (CVE-2023-22515) and Digging into Atlassian Bamboo
译者:知道创宇404实验室翻译组

概述

最近,有安全团队披露了 Confluence的一个漏洞SafeParameterFilter,该漏洞允许未经身份验证的远程攻击者绕过XWork功能来创建新的管理用户帐户。借此契机,我们准备研究另一个与之相关的Atlassian 产品 Atlassian Bamboo,以确定该应用程序中是否存在类似的漏洞。本文中,我们对Confluence中的漏洞进行了描述,同时还对 Atlassian Bamboo 为何不容易受到此漏洞影响原因进行了分析。

Confluence漏洞 (CVE-2023-22515)

近期, AttackerKB 上发布了 Confluence 漏洞分析,文章中指出该漏洞存在于 SafeParametersInterceptor 类中实施的错误过滤中。并提出到:“其虽然通过SafeParametersInterceptor.filterSafeParameters识别和过滤供应的参数,但这对基础类ParametersInterceptor没有任何影响”。

为了寻找其他Atlassian产品中潜在的变体,我们需要了解SafeParametersInterceptor中问题的根本原因。

遗憾的是,上述文章它实际上并没有深入探讨SafeParametersInterceptor存在的核心问题,这使我们很难确定Atlassian Bamboo是否在应用程序中遭受了相同或类似的问题。因此,我们的目标是通过提供 SafeParametersInterceptor 和ParametersInterceptor 类的一些附加分析来补充该文章中提供的有用信息。

SafeParametersInterceptor 分析

我们通过对 SafeParametersInterceptor 和ParametersInterceptor 类执行静态代码来分析SafeParametersInterceptor,在进行静态代码分析之后,我们使用调试器进行了动态分析以证实我们的假设。从中观察到处理HTTP参数会调用doIntercept函数作为过滤机制。

我们还注意到,SafeParametersInterceptor 中的 doIntercept 函数首先调用函数 SafeParametersInterceptor.before,该函数在第 117 行调用函数filterSafeParameters 并将其保存到名为parameters 的局部变量。如果我们使用参数名称为bootstrapStatusProvider.applicationConfig.setupComplete且值为"false"调用server-info.action端点,filterSafeParameters将过滤掉参数名称,因此在第117行实例化的parameters对象将不包含任何元素。

在正常情况下,如果参数名称没有被过滤,应用程序将在第 136 行的for循环中遍历参数数组,并在第146行调用setValue函数。通过使用调试工具分析应用程序,我们知道stack变量的类型是OgnlValueStack。OgnlValueStack.setValue函数文档说明了setValue函数尝试使用默认搜索顺序,在堆栈中的bean上设置给定表达式的属性。

从上图中我们观察到,在SafeParametersInterceptor类中,doIntercept函数首先调用了before函数,该函数调用了过滤参数过滤和验证功能,然后调用了父类ParametersInterceptor中doIntercept函数。

上图表明OgnlStack.setValue 函数的文档表明它尝试使用提供的表达式在Java bean对象上调用一个setter,它还将该setter的值传递给要设置的属性。

在这一点上,你可能认为系统是安全的,因为它正确识别到提供的输入bootstrapStatusProvider.applicationConfig.setupComplete是一个不安全的OGNL表达式。此外,SafeParametersInterceptor类正确地对其进行了过滤。然而,如上图所示,SafeParametersInterceptor中的doIntercept函数还调用了父类ParametersInterceptor中的super.doIntercept函数。

ParametersInterceptor分析

接下来,我们回顾一下ParametersInterceptor中doIntercept函数的代码是否存在潜在的漏洞。我们在这里观察到,在第118行获取与请求相关联的HTTP请求参数,然后使用先前读取的参数调用ParametersInterceptor.setParameters函数。有趣的是,这些参数与 SafeParametersInterceptor 之前处理的参数相同。读取参数后,第132行的代码调用ParametersInterceptor.setParameters

从上图中我们观察到,在ParametersInterceptor 类中,doIntercept 函数从传递给doIntercept函数的ActionContext对象中读取参数。

如果我们深入研究ParametersInterceptor.setParameters函数,可以观察到,在处理每个参数时,第181行调用了ParametersInterceptor.isAcceptableParameter。该函数不执行任何与安全相关的过滤检查,只是检查参数名称是否有效。接下来,每个参数在循环中进行处理,并调用newStack.setParameter。由此,我们可以得出结论,在 SafeParametersInterceptor.doIntercept 中调用 super.doIntercept 会导致完全规避在SafeParametersInterceptor.before 函数中执行的所有过滤。

上图对 setParameters 函数的分析表明:该函数仅利用用户提供的参数作为OGNL表达式进行处理,而没有执行任何与安全相关的过滤。

上图表明isAcceptableParameter 函数对处理的值不执行任何与安全相关的检查。

使用 Java 调试器探索 Confluence

首先,我们在SafeParametersInterceptor.doIntercept上放置一个断点,并运行以下curl 命令来利用该漏洞。下图显示了对 filterSafeParameters的调用结果:它返回了一个包含零个对象的映射,因为SafeParametersInterceptor过滤了我们提供的参数。

curl -vk http://localhost:8090/server-info.action\?bootstrapStatusProvider.applicationConfig.setupComplete\=false

从上图中我们观察到参数数组为 null,因为filterSafeParameters 函数过滤了 bootstrapStatusProvider.applicationConfig.setupComplete的输入参数,该参数的值为 false 。

结果,before 函数从未执行其 for 循环,因为parameters数组为空,而before函数直接返回。然而,第 52 行的 super.doIntercept 调用了父类ParametersInterceptor中的doIntercept方法。我们逐步执行该函数,并观察到原始的HTTP参数再次从请求中读取。

从上图中我们观察到ParametersInterceptor.doIntercept 函数调用了retrieveParameters,该函数从HTTP 请求中读取参数。这意味着SafeParametersInterceptor 不会对ParametersInterceptor 的行为产生任何影响。

在我们调用 setParameters 函数后,恶意参数就通过了isAcceptableParameter函数,该函数的目的不是执行任何安全检查以防止恶意操作。然后,第 140 行将我们的恶意参数添加到acceptedParameter数组中。

上图表明我们的恶意参数是通过调用ParameterInspector.setParameters函数中的isAcceptableParameter来获取的。

然后,该进程创建一个迭代器对象来迭代acceptableParameters数组,然后通过调用newStack.setParameter处理每个接受的参数。正如我们之前提到的,此逻辑本质上处理 OGNL 表达式以调用攻击者控制的 setter,而无需 SafeParameterInspector 通常应用的任何输入验证。

上图表明攻击者控制的参数名称和值充当 setParameter 的输入,这允许攻击者执行攻击者控制的 OGNL 表达式。

对 setParameter 的调用导致对ApplicationConfig.setSetupComplete的调用,这为theApplicationConfig.setupComplete变量设置了 false 值。这最终允许攻击者获得对受影响的 Confluence 实例的管理访问权限。

上图表明攻击者可以调用ApplicationConfig.setSetupComplete函数将 setupComplete 标记为 false,以获得对 Confluence 实例的管理访问权限。

从上图中我们观察到,在ParametersInspector.setParameters中对setParameter 的调用最终导致OGNL 表达式的编译和执行。这反过来导致 Java 反射调用 setSetupComplete 函数来修改应用程序配置。

调查潜在的Bamboo Chain

在深入了解了Confluence的漏洞后,我们查看了Atlassian Bamboo,并开始重新创建漏洞利用链。我们专门研究了docker 9.3.4映像。

与 Confluence 链中一样,拦截器会检查 Bamboo 部署的状态以确定它是否已设置。我们发现SetupCheckInterceptor(在atlassian-bamboo-web-9.3.4.jar中)类在继续设置操作之前执行了相关检查。SetupCheckInterceptor中的拦截方法调用 getCurrentStep 并确定返回值是否等于complete。如果返回的值不等于complete,则intercept方法调用所请求的操作,并继续执行设置步骤以创建新的管理帐户。因此,潜在的Bamboo Chain必须修改拦截器的currentStep值,从“complete”改为允许另一个设置操作。

图 1:Bamboo SetupCheckInterceptor 类

进入漏洞链的第一个强制入口点是公共“.action”端点,它公开具有相关方法的对象。about.action 扩展了 BambooActionSupport 类,并从其父类继承了许多方法(参见图 2)。对/about.action的 HTTP GET 请求会调用处理程序,并且不需要身份验证。

图 2:AboutAction 类扩展了 BambooActionSupport

AboutAction继承了其父类BambooActionSupport的方法。getBootstrapManager 方法继承自 BambooActionSupport,如图 3 所示,并返回一个 BootstrapManager 对象。

图 3:BambooActionSupport 对象继承 getBootstrapManager 方法

BootstrapManager 对象从其父类 DefaultAtlassianBootstrapManager继承方法。getApplicationConfig 方法返回 ApplicationConfiguration 对象,如图 4 所示。

图 4:BootstrapManager 继承 getApplicationConfig 方法

ApplicationConfiguration 实现 setCurrentSetupStep 方法,如图 5 所示。如果我们将此 currentSetupStep 值从“完整”修改,拦截器将允许对设置功能的请求。

图 5:ApplicationConfiguration 类中的 setCurrentSetupStep 方法

最终Bamboo链修改 currentSetupStep 值会产生如下所示的 GET 请求:

GET /about.action?bootstrapManager.applicationConfig.currentStep=test

该请求将被转换为以下 Java 调用链,该调用链会修改适当的 currentStep 值。

getBootstrapManager().getApplicationConfig.setCurrentStep(测试)

为什么Bamboo Chain不易受到伤害

在 Bamboo中,相关拦截代码位于两个 jar 文件struts2-core-2.5.31-atlassian.jaratlassian-xwork-core-2.5.30-struts-3.jar中。SafeParametersInterceptor 类(在atlassian-xwork-core-2.5.30-struts-3.jar!/com/atlassian/xwork/interceptors/SafeParametersInterceptor.class中)扩展了ParametersInterceptor 类(在 struts2-core-2.5.31-atlassian 中是1.jar!/com/opensymphony/xwork2/interceptor/ParametersInterceptor.class)。SafeParametersInterceptor 不会重写 doIntercept 方法,因此被调用的 doIntercept 方法是在ParametersInterceptor 中实现的方法,如图 6 所示。

图 6:ParametersInterceptor 类中的 doIntercept 方法

doIntercept调用setParameters来处理输入参数。setParameters 调用isAcceptableParameter来验证每个参数,然后再将值添加到处理堆栈(参见图 7)。

图 7:ParametersInterceptor 类中的 setParameters 方法

isAcceptableParameter 方法重写

需要强调的非常重要的一点是,Bamboo SafeParametersInterceptor 类确实重写了 isAcceptableParameter 方法。Confluence 拦截器不会重写 isAcceptableParameter 方法,这意味着系统会调用ParametersInterceptor 实现的默认方法。此外,Bamboo SafeParametersInterceptor isAcceptableParameter 函数会适当检查@ParameterSafe注释(参见图 8)。由于逻辑适当地过滤了值,因此恶意链将在 isAcceptableParameter 检查中失败。

图 8:Bamboo SafeParametersInterceptor 中的 isAcceptableParameter 方法

通过附加调试器,将请求发送到/about.action?bootstrapManager.applicationConfig.currentStep=test,并在 isAcceptableParameter 处中断(在 setParameter 方法内),我们可以在图 9 中看到 bootstrapManager 参数即将被检查,并且它包含我们在请求中发送的预期值。

图 9:即将执行的 isAcceptableParameter 方法调用

我们让 isAcceptableParameter 执行并检查参数如果我们继续到clearableStack赋值处的断点(在图9中突出显示),acceptableParameters的长度为0,这确认过滤按预期工作(参见图10)。

图 10:执行 isAcceptableParameter 后,acceptableParameters 列表仍为空

在 safeParameter 拦截器适当过滤掉利用尝试后,执行继续,并且应用程序提供对原始“about.action”请求的响应。由于拦截器适当地删除了参数,因此不处理任何 Ognl,因此 Bamboo 不易受到 CVE-2023-22515 中的相同攻击链的影响。


Paper 本文由 Seebug Paper 发布,如需转载请注明来源。本文地址:https://paper.seebug.org/3071/