Skip to content

Commit 10bb77a

Browse files
committed
Land rapid7#3716, @wchen-r7's Glassfish LoginScanner update
2 parents 28aa742 + 6cdfd32 commit 10bb77a

File tree

4 files changed

+672
-140
lines changed

4 files changed

+672
-140
lines changed
Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
2+
require 'metasploit/framework/login_scanner/http'
3+
4+
module Metasploit
5+
module Framework
6+
module LoginScanner
7+
8+
# I don't want to raise RuntimeError to be able to abort login
9+
class GlassfishError < StandardError
10+
end
11+
12+
# The Glassfish HTTP LoginScanner class provides methods to do login routines
13+
# for Glassfish 2, 3 and 4.
14+
class Glassfish < HTTP
15+
16+
DEFAULT_PORT = 4848
17+
PRIVATE_TYPES = [ :password ]
18+
19+
# @!attribute version
20+
# @return [String] Glassfish version
21+
attr_accessor :version
22+
23+
# @!attribute jsession
24+
# @return [String] Cookie session
25+
attr_accessor :jsession
26+
27+
28+
# Sends a HTTP request with Rex
29+
#
30+
# @param (see Rex::Proto::Http::Resquest#request_raw)
31+
# @return [Rex::Proto::Http::Response] The HTTP response
32+
def send_request(opts)
33+
cli = Rex::Proto::Http::Client.new(host, port, {}, ssl, ssl_version)
34+
cli.connect
35+
req = cli.request_raw(opts)
36+
res = cli.send_recv(req)
37+
38+
# Found a cookie? Set it. We're going to need it.
39+
if res && res.get_cookies =~ /JSESSIONID=(\w*);/i
40+
self.jsession = $1
41+
end
42+
43+
res
44+
end
45+
46+
47+
# As of Sep 2014, if Secure Admin is disabled, it simply means the admin isn't allowed
48+
# to login remotely. However, the authentication will still run and hint whether the
49+
# password is correct or not.
50+
#
51+
# @param res [Rex::Proto::Http::Response] The HTTP auth response
52+
# @return [boolean] True if disabled, otherwise false
53+
def is_secure_admin_disabled?(res)
54+
return (res.body =~ /Secure Admin must be enabled/i) ? true : false
55+
end
56+
57+
58+
# Sends a login request
59+
#
60+
# @param credential [Metasploit::Framework::Credential] The credential object
61+
# @return [Rex::Proto::Http::Response] The HTTP auth response
62+
def try_login(credential)
63+
data = "j_username=#{Rex::Text.uri_encode(credential.public)}&"
64+
data << "j_password=#{Rex::Text.uri_encode(credential.private)}&"
65+
data << 'loginButton=Login'
66+
67+
opts = {
68+
'uri' => '/j_security_check',
69+
'method' => 'POST',
70+
'data' => data,
71+
'headers' => {
72+
'Content-Type' => 'application/x-www-form-urlencoded',
73+
'Cookie' => "JSESSIONID=#{self.jsession}",
74+
}
75+
}
76+
77+
send_request(opts)
78+
end
79+
80+
81+
# Tries to login to Glassfish version 2
82+
#
83+
# @param credential [Metasploit::Framework::Credential] The credential object
84+
# @return [Hash]
85+
# * :status [Metasploit::Model::Login::Status]
86+
# * :proof [String] the HTTP response body
87+
def try_glassfish_2(credential)
88+
res = try_login(credential)
89+
if res && res.code == 302
90+
opts = {
91+
'uri' => '/applications/upload.jsf',
92+
'method' => 'GET',
93+
'headers' => {
94+
'Cookie' => "JSESSIONID=#{self.jsession}"
95+
}
96+
}
97+
res = send_request(opts)
98+
p = /<title>Deploy Enterprise Applications\/Modules/
99+
if (res && res.code.to_i == 200 && res.body.match(p) != nil)
100+
return {:status => Metasploit::Model::Login::Status::SUCCESSFUL, :proof => res.body}
101+
end
102+
end
103+
104+
{:status => Metasploit::Model::Login::Status::INCORRECT, :proof => res.body}
105+
end
106+
107+
108+
# Tries to login to Glassfish version 3 or 4 (as of now it's the latest)
109+
#
110+
# @param (see #try_glassfish_2)
111+
# @return (see #try_glassfish_2)
112+
def try_glassfish_3(credential)
113+
res = try_login(credential)
114+
if res && res.code == 302
115+
opts = {
116+
'uri' => '/common/applications/uploadFrame.jsf',
117+
'method' => 'GET',
118+
'headers' => {
119+
'Cookie' => "JSESSIONID=#{self.jsession}"
120+
}
121+
}
122+
res = send_request(opts)
123+
124+
p = /<title>Deploy Applications or Modules/
125+
if (res && res.code.to_i == 200 && res.body.match(p) != nil)
126+
return {:status => Metasploit::Model::Login::Status::SUCCESSFUL, :proof => res.body}
127+
end
128+
elsif res && is_secure_admin_disabled?(res)
129+
return {:status => Metasploit::Model::Login::Status::SUCCESSFUL, :proof => res.body}
130+
elsif res && res.code == 400
131+
raise GlassfishError, "400: Bad HTTP request from try_login"
132+
end
133+
134+
{:status => Metasploit::Model::Login::Status::INCORRECT, :proof => res.body}
135+
end
136+
137+
138+
# Decides which login routine and returns the results
139+
#
140+
# @param credential [Metasploit::Framework::Credential] The credential object
141+
# @return [Result]
142+
def attempt_login(credential)
143+
result_opts = { credential: credential }
144+
145+
begin
146+
case self.version
147+
when /^[29]\.x$/
148+
status = try_glassfish_2(credential)
149+
result_opts.merge!(status: status[:status], proof:status[:proof])
150+
when /^[34]\./
151+
status = try_glassfish_3(credential)
152+
result_opts.merge!(status: status[:status], proof:status[:proof])
153+
else
154+
raise GlassfishError, "Glassfish version '#{self.version}' not supported"
155+
end
156+
rescue ::EOFError, Rex::ConnectionError, ::Timeout::Error
157+
result_opts.merge!(status: Metasploit::Model::Login::Status::UNABLE_TO_CONNECT)
158+
end
159+
160+
Result.new(result_opts)
161+
end
162+
163+
end
164+
end
165+
end
166+
end
167+

0 commit comments

Comments
 (0)