Confluence Vulnerability (CVE-2023–22515): A Deep Dive into Atlassian Bamboo’s Chain Security Landscape
Recently, a security team disclosed a vulnerability in Confluence called SafeParameterFilter, which allows an unauthenticated remote attacker to bypass XWork functionality to create new administrative user accounts. We took this opportunity to study another related Atlassian product, Atlassian Bamboo, to determine whether a similar vulnerability exists in this application. In this article, we describe the vulnerability in Confluence and analyze why Atlassian Bamboo is not vulnerable to this vulnerability.
Confluence vulnerability (CVE-2023–22515)
Recently, a Confluence vulnerability analysis was published on AttackerKB. The article pointed out that the vulnerability exists in the error filtering implemented in the SafeParametersInterceptor class. And pointed out: “Although it identifies and filters the supplied parameters through SafeParametersInterceptor.filterSafeParameters, this has no impact on the base class ParametersInterceptor.”
In order to find potential variants in other Atlassian products, we need to understand the root cause of the problem in SafeParametersInterceptor.
Unfortunately, the above article doesn’t actually delve into the core issues with SafeParametersInterceptor, which makes it difficult to determine whether Atlassian Bamboo suffers from the same or similar issues in its application. Therefore, our goal is to supplement the useful information provided in the article by providing some additional analysis of the SafeParametersInterceptor and ParametersInterceptor classes.
We analyzed SafeParametersInterceptor by executing static code on the SafeParametersInterceptor and ParametersInterceptor classes. After static code analysis, we performed dynamic analysis using a debugger to confirm our hypothesis. It is observed that processing HTTP parameters will call the intercept function as a filtering mechanism.
We also notice that the doIntercept function in SafeParametersInterceptor first calls SafeParametersInterceptor.before the function on line 117 filterSafeParameters and saves it to a local variable called parameters. If we call bootstrapStatusProvider.applicationConfig.setupComplete the server-info.action endpoint with a parameter name and a value of “false”, filterSafeParameters will filter out the parameter name, so the parameters object instantiated on line 117 will not contain any elements.
Under normal circumstances, if the parameter names are not filtered, the application will iterate through the parameter array in the for loop on line 136 and call the setValue function on line 146. By analyzing the application using debugging tools, we know that the type of stack variable is OgnlValueStack. The OgnlValueStack.setValue function documentation explains that the setValue function attempts to set the property of the given expression on the bean in the stack using the default search order.
From the above figure, we observe that in SafeParametersInterceptor the class, the doIntercept function first calls the before function, which calls the filter parameter filtering and verification functions, and then calls the function ParametersInterceptor中 of the parent class doIntercept .
The above image OgnlStack.setValue shows that the documentation for the function indicates that it attempts to call a setter on the Java bean object using the provided expression, and it also passes the value of that setter to the property to be set.
At this point, you might think that the system is safe because it correctly recognizes that the provided input bootstrapStatusProvider.applicationConfig.setupComplete is an unsafe OGNL expression. Also, SafeParametersInterceptor the class filters it correctly. However, as shown above, the function SafeParametersInterceptor in doIntercept also calls a function ParametersInterceptor in the parent class super.doIntercept.
Next, let’s review doIntercept the code of the function in ParametersInterceptor to see if there are potential vulnerabilities. What we observe here is that on line 118 the HTTP request parameters associated with the request are obtained and then ParametersInterceptor.setParameters the function is called using the previously read parameters. Interestingly, these parameters SafeParametersInterceptor are the same as those processed previously. After reading the parameters, the code on line 132 calls ParametersInterceptor.setParameters .
From the above figure we observe that in ParametersInterceptor the class, the doIntercept function reads the parameters from the object passed to doIntercept the function .ActionContext
If we dig deeper into ParametersInterceptor.setParametersthe function, we can observe that while each parameter is being processed, line 181 is called ParametersInterceptor.isAcceptableParameter . This function does not perform any security-related filtering checks, it just checks whether the parameter names are valid. Next, each parameter is processed in the loop and called newStack.setParameter . From this, we can conclude that SafeParametersInterceptor.doIntercept calling super.doIntercept will result in complete circumvention of SafeParametersInterceptor.before all filtering is performed in the function.
The analysis of the setParameters function in the above figure shows that this function only uses user-supplied parameters as OGNL expressions for processing, without performing any security-related filtering.
The above figure shows that the isAcceptableParameter function does not perform any security-related checks on the processed values.
Explore Confluence with the Java debugger
First, we SafeParametersInterceptor.doInterceptplace a breakpoint on and run the following curl command to exploit the vulnerability. The following image shows filterSafeParametersthe result of the call to: it returns a map containing zero objects because SafeParametersInterceptorthe parameters we provided are filtered.
curl -vk http://localhost:8090/server-info.action\?bootstrapStatusProvider.applicationConfig.setupComplete\=false
From the above figure, we observe that the parameter array is null because bootstrapStatusProvider.applicationConfig.setupCompletethe input parameter filtered by the filterSafeParameters function has a value of false.
As a result, the before function never executes its for loop because the parameters array is empty and the before function returns directly. However, line 52 super.doIntercept calls a method ParametersInterceptorin the parent class doIntercept. We step through the function and observe that the original HTTP parameters are read from the request again.
From the above figure we observe that ParametersInterceptor.doInterceptthe function is called retrieveParameters, which reads parameters from the HTTP request. This means that SafeParametersInterceptor will not have any influence on the behavior of ParametersInterceptor.
After we call the setParameters function, the malicious parameters are passed through isAcceptableParameterthe function, which is not intended to perform any security checks to prevent malicious operations. Line 140 then adds our malicious parameter to the acceptedParameter array.
The above figure shows that our malicious parameters are obtained by calling ParameterInspector.setParametersthe function .isAcceptableParameter
The process then creates an iterator object to iterate over acceptableParametersthe array and then newStack.setParameterhandles each accepted argument through a call. As we mentioned before, this logic essentially handles OGNL expressions to call attacker-controlled setters without any input validation that SafeParameterInspector would normally apply.
The above figure shows that attacker-controlled parameter names and values serve as inputs to setParameter, which allows the attacker to execute attacker-controlled OGNL expressions.
A call to setParameter results in ApplicationConfig.setSetupCompletea call to , which theApplicationConfig.setupCompletesets the variable to a false value. This ultimately allowed the attacker to gain administrative access to the affected Confluence instance.
The above image shows that an attacker can call ApplicationConfig.setSetupCompletethe function to mark setupComplete as false to gain administrative access to the Confluence instance.
From the above figure we observe that ParametersInspector.setParametersthe call to setParameter in ultimately leads to the compilation and execution of the OGNL expression. This in turn causes Java reflection to call the setSetupComplete function to modify the application configuration.
Investigate potential Bamboo Chain
After digging into Confluence’s vulnerability, we looked at Atlassian Bamboo and began recreating the exploit chain. We looked specifically at docker 9.3.4 images.
As in the Confluence chain, the interceptor checks the status of the Bamboo deployment to determine whether it has been set up. We found that the SetupCheckInterceptor (in atlassian-bamboo-web-9.3.4.jar) class performs related checks before continuing the setup operation. The interception method in SetupCheckInterceptor calls getCurrentStep and determines whether the return value is equal to complete. If the returned value is not equal to complete, the intercept method calls the requested operation and continues with the setup steps to create a new administrative account. Therefore, the underlying Bamboo Chain must modify the interceptor’s currentStep value from “complete” to allow another set operation.
The first mandatory entry point into the vulnerability chain is the public “.action” endpoint, which exposes an object with relevant methods. about.action extends the BambooActionSupport class and inherits many methods from its parent class (see Figure 2). The handler /about.actionis called for HTTP GET requests and does not require authentication.
AboutAction inherits the methods of its parent class BambooActionSupport. The getBootstrapManager method inherits from BambooActionSupport, as shown in Figure 3, and returns a BootstrapManager object.
The BootstrapManager object inherits methods from its parent class, DefaultAtlassianBootstrapManager. The getApplicationConfig method returns the ApplicationConfiguration object, as shown in Figure 4.
ApplicationConfiguration implements the setCurrentSetupStep method, as shown in Figure 5. If we modify this currentSetupStep value from “Full”, the interceptor will allow requests for the setup function.
Finally, the Bamboo chain modifying the currentSetupStep value will generate a GET request as shown below:
The request is translated into the following chain of Java calls, which modifies the appropriate currentStep value.
Why Bamboo Chain Isn’t Vulnerable
In Bamboo, the relevant interception code is located in two jar files struts2-core-2.5.31-atlassian.jarand atlassian-xwork-core-2.5.30-struts-3.jar. The SafeParametersInterceptor class ( atlassian-xwork-core-2.5.30-struts-3.jar!/com/atlassian/xwork/interceptors/SafeParametersInterceptor.classin ) extends the ParametersInterceptor class (in ). struts2-core-2.5.31-atlassianSafeParametersInterceptor 1.jar!/com/opensymphony/xwork2/interceptor/ParametersInterceptor.class)does not override the doIntercept method, so the doIntercept method that is called is the method implemented in ParametersInterceptor, as shown in Figure 6.
doIntercept calls setParameters to handle input parameters. setParameters is called isAcceptableParameterto validate each parameter before adding the value to the processing stack (see Figure 7).
isAcceptableParameter method override
It is very important to emphasize that the Bamboo SafeParametersInterceptor class does override the isAcceptableParameter method. Confluence interceptors do not override the isAcceptableParameter method, which means the default method implemented by ParametersInterceptor is called. Additionally, Bamboo SafeParametersInterceptor isAcceptableParameterthe function checks the @ParameterSafe annotation appropriately (see Figure 8). Since the logic filters the value appropriately, the malicious chain will fail on the isAcceptableParameter check.
By attaching the debugger, sending the request to /about.action?bootstrapManager.applicationConfig.currentStep=test, and breaking at isAcceptableParameter (within the setParameter method), we can see in Figure 9 that the bootstrapManager parameter is about to be checked and that it contains the expected value we sent in the request.
We let isAcceptableParameter execute and check the parameters. If we continue to the breakpoint at the clearableStack assignment (highlighted in Figure 9), the length of acceptableParameters is 0, which confirms that filtering is working as expected (see Figure 10).
After the safeParameter interceptor appropriately filters out the exploit attempt, execution continues and the application provides a response to the original “about.action” request. Since the interceptor appropriately removes parameters and therefore does not process any Ognl, Bamboo is not vulnerable to the same attack chain in CVE-2023–22515.