CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
rapid7

CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!

GitHub Repository: rapid7/metasploit-framework
Path: blob/master/lib/metasploit/framework/login_scanner/softing_sis.rb
Views: 1904
1
require 'metasploit/framework/login_scanner/http'
2
3
module Metasploit
4
module Framework
5
module LoginScanner
6
class SoftingSIS < HTTP
7
8
DEFAULT_PORT = 8099
9
DEFAULT_SSL_PORT = 443
10
PRIVATE_TYPES = [ :password ]
11
LOGIN_STATUS = Metasploit::Model::Login::Status
12
13
# Check if the target is Softing Secure Integration Server
14
#
15
# @return [Boolean] TrueClass if target is SIS, otherwise FalseClass
16
def check_setup
17
# we can interact with this endpoint as an unauthenticated user
18
uri = normalize_uri("#{uri}/runtime/core/product-version")
19
res = send_request({ 'uri' => uri })
20
# make sure we get a response, and that the check was successful
21
unless res && res.code == 200
22
return { status: LOGIN_STATUS::UNABLE_TO_CONNECT, proof: res.to_s }
23
end
24
25
# convert the response to JSON
26
# we expect to see a response like {"version" : "1.22.0.8686"}
27
res_json = res.get_json_document
28
# if we successfully get the version
29
if res_json['version']
30
# return true
31
return res_json['version']
32
end
33
34
false
35
end
36
37
# get the authentication token
38
#
39
# @param user [String] The username
40
# @return [Hash]
41
# * status [Metasploit::Model::Login::Status]
42
# * proof [String] the authentication token
43
def get_auth_token(user)
44
auth_token_uri = normalize_uri("#{uri}/runtime/core/user/#{user}/authentication-token")
45
46
# send the request to get an authentication token
47
auth_res = send_request({
48
'method' => 'GET',
49
'uri' => auth_token_uri,
50
'cookie' => 'lang=en; user=guest'
51
})
52
53
# check if we get a response
54
unless auth_res
55
return { status: LOGIN_STATUS::UNABLE_TO_CONNECT, proof: auth_res.to_s }
56
end
57
58
# convert the response to JSON
59
auth_json = auth_res.get_json_document
60
# if the response code is 404, the user does not exist
61
if auth_res.code == 404 && auth_json && auth_json['Message']
62
return { status: LOGIN_STATUS::INCORRECT, proof: auth_json['Message'] }
63
end
64
65
# if the response code is 403, the user exists but access is denied
66
if auth_res.code == 403 && auth_json && auth_json['Message']
67
return { status: LOGIN_STATUS::DENIED_ACCESS, proof: auth_json['Message'] }
68
end
69
70
# get authentication token
71
auth_token = auth_json['authentication-token']
72
# check that the token is not blank
73
if auth_token.blank?
74
framework_module.vprint_error('Received empty authentication token!')
75
return { status: LOGIN_STATUS::INCORRECT, proof: auth_res.body.to_s }
76
end
77
78
{ status: LOGIN_STATUS::SUCCESSFUL, proof: auth_token }
79
end
80
81
# generate a signature from the authentication token, username, and password
82
#
83
# @param auth_token [String] The authentication token retrieved by calling get_auth_token
84
# @param user [String] The username
85
# @param pass [String] The password
86
# @return [String] A hexadecimal string representation of the signature
87
def generate_signature(auth_token, user, pass)
88
Digest::MD5.hexdigest(auth_token + pass + auth_token + user + auth_token)
89
end
90
91
# the actual login method, called by #attempt_login
92
#
93
# @param user [String] The username to try
94
# @param pass [String] The password to try
95
# @return [Hash]
96
# * status [Metasploit::Model::Login::Status]
97
# * proof [String] the HTTP response body
98
def do_login(user, pass)
99
# prep the data needed for login
100
protocol = ssl ? 'https' : 'http'
101
# attempt to get an authentication token
102
auth_token_res = get_auth_token(user)
103
# get_auth_token always returns a hash - check that status is SUCCESSFUL
104
# if not, just return as it is
105
unless auth_token_res[:status] == LOGIN_STATUS::SUCCESSFUL
106
return auth_token_res
107
end
108
109
# extract the authentication token from the hash
110
auth_token = auth_token_res[:proof]
111
112
login_uri = normalize_uri("#{uri}/runtime/core/user/#{user}/authentication")
113
# calculate signature to use when logging in
114
signature = generate_signature(auth_token, user, pass)
115
# GET parameters for login
116
vars_get = {
117
'Signature' => signature,
118
'User' => user
119
}
120
121
# do the login
122
res = send_request({
123
'method' => 'GET',
124
'uri' => login_uri,
125
'cookie' => 'lang=en; user=guest',
126
'headers' => { 'Referer' => "#{protocol}://#{host}:#{port}" },
127
'vars_get' => vars_get
128
})
129
130
unless res
131
return { status: LOGIN_STATUS::UNABLE_TO_CONNECT, proof: res.to_s }
132
end
133
134
# the response is in JSON format
135
res_json = res.get_json_document
136
# a successful response will contain {"Message": "Success"}
137
if res.code == 200 && res_json && res_json['Message'] == 'Success'
138
return { status: LOGIN_STATUS::SUCCESSFUL, proof: res.body }
139
end
140
141
{ status: LOGIN_STATUS::INCORRECT, proof: res.body }
142
end
143
144
# Attempts to login to Softing Secure Integration Server
145
#
146
# @param credential [Metasploit::Framework::Credential] The credential object
147
# @return [Result] A Result object indicating success or failure
148
def attempt_login(credential)
149
result_opts = {
150
credential: credential,
151
status: Metasploit::Model::Login::Status::INCORRECT,
152
proof: nil,
153
host: host,
154
port: port,
155
protocol: 'tcp'
156
}
157
158
begin
159
result_opts.merge!(do_login(credential.public, credential.private))
160
rescue ::Rex::ConnectionError => e
161
# something went wrong during login
162
result_opts.merge!(status: LOGIN_STATUS::UNABLE_TO_CONNECT, proof: e.message)
163
end
164
165
Result.new(result_opts)
166
end
167
168
end
169
end
170
end
171
end
172
173