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