Skip to content

Commit 93ac8b4

Browse files
committed
Land rapid7#5178, @jboss_vulnscan check for console default admin
* And minor fixes
2 parents c6806b4 + 697c6c2 commit 93ac8b4

File tree

1 file changed

+111
-31
lines changed

1 file changed

+111
-31
lines changed

modules/auxiliary/scanner/http/jboss_vulnscan.rb

Lines changed: 111 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -7,18 +7,21 @@
77
require 'msf/core'
88

99
class Metasploit3 < Msf::Auxiliary
10-
1110
include Msf::Exploit::Remote::HttpClient
1211
include Msf::Auxiliary::Scanner
1312
include Msf::Auxiliary::Report
1413

1514
def initialize(info = {})
1615
super(update_info(info,
1716
'Name' => 'JBoss Vulnerability Scanner',
18-
'Description' => %q{
17+
'Description' => %q(
1918
This module scans a JBoss instance for a few vulnerablities.
20-
},
21-
'Author' => [ 'Tyler Krpata' ],
19+
),
20+
'Author' =>
21+
[
22+
'Tyler Krpata',
23+
'Zach Grace <@ztgrace>'
24+
],
2225
'References' =>
2326
[
2427
[ 'CVE', '2010-0738' ] # VERB auth bypass
@@ -28,31 +31,29 @@ def initialize(info = {})
2831

2932
register_options(
3033
[
31-
OptString.new('VERB', [ true, "Verb for auth bypass testing", "HEAD"]),
34+
OptString.new('VERB', [ true, "Verb for auth bypass testing", "HEAD"])
3235
], self.class)
3336
end
3437

35-
3638
def run_host(ip)
37-
3839
res = send_request_cgi(
3940
{
40-
'uri' => "/"+Rex::Text.rand_text_alpha(12),
41+
'uri' => "/" + Rex::Text.rand_text_alpha(12),
4142
'method' => 'GET',
42-
'ctype' => 'text/plain',
43-
44-
}, 20)
43+
'ctype' => 'text/plain'
44+
})
4545

4646
if res
4747

48-
info = http_fingerprint({ :response => res })
48+
info = http_fingerprint(:response => res)
4949
print_status(info)
5050

51-
if(res.body and />(JBoss[^<]+)/.match(res.body) )
51+
if res.body && />(JBoss[^<]+)/.match(res.body)
5252
print_error("#{rhost}:#{rport} JBoss error message: #{$1}")
5353
end
5454

55-
apps = [ '/jmx-console/HtmlAdaptor',
55+
apps = [
56+
'/jmx-console/HtmlAdaptor',
5657
'/status',
5758
'/web-console/ServerInfo.jsp',
5859
# apps added per Patrick Hof
@@ -65,29 +66,30 @@ def run_host(ip)
6566
check_app(app)
6667
end
6768

69+
jboss_as_default_creds
70+
6871
ports = {
6972
# 1098i, 1099, and 4444 needed to use twiddle
7073
1098 => 'Naming Service',
7174
1099 => 'Naming Service',
7275
4444 => 'RMI invoker'
7376
}
7477
print_status("#{rhost}:#{rport} Checking services...")
75-
ports.each do |port,service|
76-
status = test_connection(ip,port) == :up ? "open" : "closed";
78+
ports.each do |port, service|
79+
status = test_connection(ip, port) == :up ? "open" : "closed"
7780
print_status("#{rhost}:#{rport} #{service} tcp/#{port}: #{status}")
7881
end
7982
end
8083
end
8184

8285
def check_app(app)
83-
8486
res = send_request_cgi({
8587
'uri' => app,
8688
'method' => 'GET',
87-
'ctype' => 'text/plain',
88-
}, 20)
89+
'ctype' => 'text/plain'
90+
})
8991

90-
if (res)
92+
if res
9193
case
9294
when res.code == 200
9395
print_good("#{rhost}:#{rport} #{app} does not require authentication (200)")
@@ -96,6 +98,7 @@ def check_app(app)
9698
when res.code == 401
9799
print_status("#{rhost}:#{rport} #{app} requires authentication (401): #{res.headers['WWW-Authenticate']}")
98100
bypass_auth(app)
101+
basic_auth_default_creds(app)
99102
when res.code == 404
100103
print_status("#{rhost}:#{rport} #{app} not found (404)")
101104
when res.code == 301, res.code == 302
@@ -108,48 +111,125 @@ def check_app(app)
108111
end
109112
end
110113

111-
def bypass_auth(app)
114+
def jboss_as_default_creds
115+
print_status("#{rhost}:#{rport} Checking for JBoss AS default creds")
116+
117+
session = jboss_as_session_setup(rhost, rport)
118+
return false if session.nil?
119+
120+
# Default AS creds
121+
username = 'admin'
122+
password = 'admin'
123+
124+
res = send_request_raw({
125+
'uri' => '/admin-console/login.seam',
126+
'method' => 'POST',
127+
'version' => '1.1',
128+
'vhost' => "#{rhost}",
129+
'headers' => { 'Content-Type' => 'application/x-www-form-urlencoded',
130+
'Cookie' => "JSESSIONID=#{session['jsessionid']}"
131+
},
132+
'data' => "login_form=login_form&login_form%3Aname=#{username}&login_form%3Apassword=#{password}&login_form%3Asubmit=Login&javax.faces.ViewState=#{session["viewstate"]}"
133+
})
134+
135+
# Valid creds if 302 redirected to summary.seam and not error.seam
136+
if res && res.code == 302 && res.headers.to_s !~ /error.seam/m && res.headers.to_s =~ /summary.seam/m
137+
print_good("#{rhost}:#{rport} Authenticated using #{username}:#{password} at /admin-console/")
138+
add_creds(username, password)
139+
else
140+
print_status("#{rhost}:#{rport} Could not guess admin credentials")
141+
end
142+
end
112143

144+
def add_creds(username, password)
145+
service_data = {
146+
address: rhost,
147+
port: rport,
148+
service_name: 'jboss',
149+
protocol: 'tcp',
150+
workspace_id: framework.db.workspace.id
151+
}
152+
153+
credential_data = {
154+
module_fullname: self.fullname,
155+
origin_type: :service,
156+
private_data: password,
157+
private_type: :password,
158+
username: username
159+
}.merge(service_data)
160+
161+
credential_core = create_credential(credential_data)
162+
credential_data[:core] = credential_core
163+
create_credential_login(credential_data)
164+
end
165+
166+
def jboss_as_session_setup(rhost, rport)
167+
res = send_request_raw({
168+
'uri' => '/admin-console/login.seam',
169+
'method' => 'GET',
170+
'version' => '1.1',
171+
'vhost' => "#{rhost}"
172+
})
173+
174+
unless res
175+
return nil
176+
end
177+
178+
begin
179+
viewstate = /javax.faces.ViewState" value="(.*)" auto/.match(res.body).captures[0]
180+
jsessionid = /JSESSIONID=(.*);/.match(res.headers.to_s).captures[0]
181+
rescue ::NoMethodError
182+
print_status("#{rhost}:#{rport} Could not guess admin credentials")
183+
return nil
184+
end
185+
186+
{ 'jsessionid' => jsessionid, 'viewstate' => viewstate }
187+
end
188+
189+
def bypass_auth(app)
113190
print_status("#{rhost}:#{rport} Check for verb tampering (HEAD)")
114191

115192
res = send_request_raw({
116193
'uri' => app,
117194
'method' => datastore['VERB'],
118195
'version' => '1.0' # 1.1 makes the head request wait on timeout for some reason
119-
}, 20)
120-
if (res and res.code == 200)
196+
})
197+
198+
if res && res.code == 200
121199
print_good("#{rhost}:#{rport} Got authentication bypass via HTTP verb tampering")
122200
else
123201
print_status("#{rhost}:#{rport} Could not get authentication bypass via HTTP verb tampering")
124202
end
203+
end
125204

205+
def basic_auth_default_creds(app)
126206
res = send_request_cgi({
127207
'uri' => app,
128208
'method' => 'GET',
129209
'ctype' => 'text/plain',
130-
'authorization' => basic_auth('admin','admin')
131-
}, 20)
132-
if (res and res.code == 200)
133-
print_good("#{rhost}:#{rport} Authenticated using admin:admin")
210+
'authorization' => basic_auth('admin', 'admin')
211+
})
212+
213+
if res && res.code == 200
214+
print_good("#{rhost}:#{rport} Authenticated using admin:admin at #{app}")
215+
add_creds("admin", "admin")
134216
else
135217
print_status("#{rhost}:#{rport} Could not guess admin credentials")
136218
end
137-
138219
end
139220

140221
# function stole'd from mssql_ping
141-
def test_connection(ip,port)
222+
def test_connection(ip, port)
142223
begin
143224
sock = Rex::Socket::Tcp.create(
144225
'PeerHost' => ip,
145226
'PeerPort' => port,
146227
'Timeout' => 20
147-
)
228+
)
148229
rescue Rex::ConnectionError
149230
return :down
150231
end
151232
sock.close
152233
return :up
153234
end
154-
155235
end

0 commit comments

Comments
 (0)