原文链接: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”改为允许另一个设置操作。
进入漏洞链的第一个强制入口点是公共“.action”端点,它公开具有相关方法的对象。about.action 扩展了 BambooActionSupport 类,并从其父类继承了许多方法(参见图 2)。对/about.action
的 HTTP GET 请求会调用处理程序,并且不需要身份验证。
AboutAction继承了其父类BambooActionSupport的方法。getBootstrapManager 方法继承自 BambooActionSupport,如图 3 所示,并返回一个 BootstrapManager 对象。
BootstrapManager 对象从其父类 DefaultAtlassianBootstrapManager继承方法。getApplicationConfig 方法返回 ApplicationConfiguration 对象,如图 4 所示。
ApplicationConfiguration 实现 setCurrentSetupStep 方法,如图 5 所示。如果我们将此 currentSetupStep 值从“完整”修改,拦截器将允许对设置功能的请求。
最终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.jar
和atlassian-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 所示。
doIntercept调用setParameters来处理输入参数。setParameters 调用isAcceptableParameter
来验证每个参数,然后再将值添加到处理堆栈(参见图 7)。
isAcceptableParameter 方法重写
需要强调的非常重要的一点是,Bamboo SafeParametersInterceptor 类确实重写了 isAcceptableParameter 方法。Confluence 拦截器不会重写 isAcceptableParameter 方法,这意味着系统会调用ParametersInterceptor 实现的默认方法。此外,Bamboo SafeParametersInterceptor isAcceptableParameter
函数会适当检查@ParameterSafe注释(参见图 8)。由于逻辑适当地过滤了值,因此恶意链将在 isAcceptableParameter 检查中失败。
通过附加调试器,将请求发送到/about.action?bootstrapManager.applicationConfig.currentStep=test
,并在 isAcceptableParameter 处中断(在 setParameter 方法内),我们可以在图 9 中看到 bootstrapManager 参数即将被检查,并且它包含我们在请求中发送的预期值。
我们让 isAcceptableParameter 执行并检查参数如果我们继续到clearableStack赋值处的断点(在图9中突出显示),acceptableParameters的长度为0,这确认过滤按预期工作(参见图10)。
在 safeParameter 拦截器适当过滤掉利用尝试后,执行继续,并且应用程序提供对原始“about.action”请求的响应。由于拦截器适当地删除了参数,因此不处理任何 Ognl,因此 Bamboo 不易受到 CVE-2023-22515 中的相同攻击链的影响。
本文由 Seebug Paper 发布,如需转载请注明来源。本文地址:https://paper.seebug.org/3071/
暂无评论