Path: blob/master/lib/metasploit/framework/login_scanner/softing_sis.rb
32597 views
require 'metasploit/framework/login_scanner/http'12module Metasploit3module Framework4module LoginScanner5class SoftingSIS < HTTP67DEFAULT_PORT = 80998DEFAULT_SSL_PORT = 4439PRIVATE_TYPES = [ :password ]10LOGIN_STATUS = Metasploit::Model::Login::Status1112# Check if the target is Softing Secure Integration Server13#14# @return [Boolean] TrueClass if target is SIS, otherwise FalseClass15def check_setup16# we can interact with this endpoint as an unauthenticated user17uri = normalize_uri("#{uri}/runtime/core/product-version")18res = send_request({ 'uri' => uri })19# make sure we get a response, and that the check was successful20if res && res.code == 20021# convert the response to JSON22# we expect to see a response like {"version" : "1.22.0.8686"}23res_json = res.get_json_document24# if we successfully get the version25if res_json['version']26# report the version27framework_module.print_brute(level: :good, ip: ip, msg: "Softing Secure Integration Server #{res_json['version']}") if framework_module28return false29end30end3132'Unable to locate product version in body. (Is this really SoftingSIS?)'33end3435# get the authentication token36#37# @param user [String] The username38# @return [Hash]39# * status [Metasploit::Model::Login::Status]40# * proof [String] the authentication token41def get_auth_token(user)42auth_token_uri = normalize_uri("#{uri}/runtime/core/user/#{user}/authentication-token")4344# send the request to get an authentication token45auth_res = send_request({46'method' => 'GET',47'uri' => auth_token_uri,48'cookie' => 'lang=en; user=guest'49})5051# check if we get a response52unless auth_res53return { status: LOGIN_STATUS::UNABLE_TO_CONNECT, proof: auth_res.to_s }54end5556# convert the response to JSON57auth_json = auth_res.get_json_document58# if the response code is 404, the user does not exist59if auth_res.code == 404 && auth_json && auth_json['Message']60return { status: LOGIN_STATUS::INCORRECT, proof: auth_json['Message'] }61end6263# if the response code is 403, the user exists but access is denied64if auth_res.code == 403 && auth_json && auth_json['Message']65return { status: LOGIN_STATUS::DENIED_ACCESS, proof: auth_json['Message'] }66end6768# get authentication token69auth_token = auth_json['authentication-token']70# check that the token is not blank71if auth_token.blank?72framework_module.vprint_error('Received empty authentication token!')73return { status: LOGIN_STATUS::INCORRECT, proof: auth_res.body.to_s }74end7576{ status: LOGIN_STATUS::SUCCESSFUL, proof: auth_token }77end7879# generate a signature from the authentication token, username, and password80#81# @param auth_token [String] The authentication token retrieved by calling get_auth_token82# @param user [String] The username83# @param pass [String] The password84# @return [String] A hexadecimal string representation of the signature85def generate_signature(auth_token, user, pass)86Digest::MD5.hexdigest(auth_token + pass + auth_token + user + auth_token)87end8889# the actual login method, called by #attempt_login90#91# @param user [String] The username to try92# @param pass [String] The password to try93# @return [Hash]94# * status [Metasploit::Model::Login::Status]95# * proof [String] the HTTP response body96def do_login(user, pass)97# prep the data needed for login98protocol = ssl ? 'https' : 'http'99# attempt to get an authentication token100auth_token_res = get_auth_token(user)101# get_auth_token always returns a hash - check that status is SUCCESSFUL102# if not, just return as it is103unless auth_token_res[:status] == LOGIN_STATUS::SUCCESSFUL104return auth_token_res105end106107# extract the authentication token from the hash108auth_token = auth_token_res[:proof]109110login_uri = normalize_uri("#{uri}/runtime/core/user/#{user}/authentication")111# calculate signature to use when logging in112signature = generate_signature(auth_token, user, pass)113# GET parameters for login114vars_get = {115'Signature' => signature,116'User' => user117}118119# do the login120res = send_request({121'method' => 'GET',122'uri' => login_uri,123'cookie' => 'lang=en; user=guest',124'headers' => { 'Referer' => "#{protocol}://#{host}:#{port}" },125'vars_get' => vars_get126})127128unless res129return { status: LOGIN_STATUS::UNABLE_TO_CONNECT, proof: res.to_s }130end131132# the response is in JSON format133res_json = res.get_json_document134# a successful response will contain {"Message": "Success"}135if res.code == 200 && res_json && res_json['Message'] == 'Success'136return { status: LOGIN_STATUS::SUCCESSFUL, proof: res.body }137end138139{ status: LOGIN_STATUS::INCORRECT, proof: res.body }140end141142# Attempts to login to Softing Secure Integration Server143#144# @param credential [Metasploit::Framework::Credential] The credential object145# @return [Result] A Result object indicating success or failure146def attempt_login(credential)147result_opts = {148credential: credential,149status: Metasploit::Model::Login::Status::INCORRECT,150proof: nil,151host: host,152port: port,153protocol: 'tcp'154}155156begin157result_opts.merge!(do_login(credential.public, credential.private))158rescue ::Rex::ConnectionError => e159# something went wrong during login160result_opts.merge!(status: LOGIN_STATUS::UNABLE_TO_CONNECT, proof: e.message)161end162163Result.new(result_opts)164end165166end167end168end169end170171172