diff --git a/lib/metasploit/framework/login_scanner/jenkins.rb b/lib/metasploit/framework/login_scanner/jenkins.rb index f181da09fca9..9d51cad58a7c 100644 --- a/lib/metasploit/framework/login_scanner/jenkins.rb +++ b/lib/metasploit/framework/login_scanner/jenkins.rb @@ -15,7 +15,7 @@ class Jenkins < HTTP # (see Base#set_sane_defaults) def set_sane_defaults - self.uri = "/j_acegi_security_check" if self.uri.nil? + self.uri = "/" if self.uri.nil? self.method = "POST" if self.method.nil? if self.uri[0] != '/' @@ -25,6 +25,30 @@ def set_sane_defaults super end + # (see Base#check_setup) + def check_setup + login_uri = jenkins_login_uri + if login_uri.nil? + error_message = 'Unable to locate the Jenkins login path' + else + self.uri = normalize_uri(login_uri) + error_message = false + end + + error_message + end + + def jenkins_login_uri + res = send_request({ 'uri' => normalize_uri('login') }) + if res&.code == 200 && res&.body =~ Msf::Exploit::Remote::HTTP::Jenkins::LOGIN_PATH_REGEX + login_path = Regexp.last_match(1) + ilog("Found jenkins login path: #{login_path}") + return login_path + end + wlog('Failed to identify the login resource.') + nil + end + def attempt_login(credential) result_opts = { credential: credential, diff --git a/lib/msf/core/exploit/remote/http/jenkins.rb b/lib/msf/core/exploit/remote/http/jenkins.rb index cd33bb589f1f..e1b0b8b30e99 100644 --- a/lib/msf/core/exploit/remote/http/jenkins.rb +++ b/lib/msf/core/exploit/remote/http/jenkins.rb @@ -6,6 +6,8 @@ class Remote module HTTP # This module provides a way of logging into Jenkins module Jenkins + + LOGIN_PATH_REGEX = /action="(j_([a-z0-9_]+))"/ # Returns the Jenkins version. # # @return [String] Jenkins version. @@ -15,7 +17,7 @@ def jenkins_version res = send_request_cgi({ 'uri' => uri }) unless res - return nil + return nil end # shortcut for new versions such as 2.426.2 and 2.440 @@ -36,7 +38,7 @@ def jenkins_uri_check(target_uri, keep_cookies: false) # if keep_cookies is true we get the first cookie that's needed by newer Jenkins versions res = send_request_cgi({ 'uri' => normalize_uri(target_uri, 'login'), 'keep_cookies' => keep_cookies }) fail_with(Msf::Module::Failure::UnexpectedReply, 'Unexpected reply from server') unless res&.code == 200 - if res.body =~ /action="(j_([a-z0-9_]+))"/ + if res.body =~ LOGIN_PATH_REGEX uri = Regexp.last_match(1) else fail_with(Msf::Module::Failure::UnexpectedReply, 'Failed to identify the login resource.') diff --git a/modules/auxiliary/scanner/http/jenkins_login.rb b/modules/auxiliary/scanner/http/jenkins_login.rb index c652808abee0..31d6a1787590 100644 --- a/modules/auxiliary/scanner/http/jenkins_login.rb +++ b/modules/auxiliary/scanner/http/jenkins_login.rb @@ -38,10 +38,10 @@ def run_host(ip) password: datastore['PASSWORD'] ) - login_uri = jenkins_uri_check(target_uri) scanner = Metasploit::Framework::LoginScanner::Jenkins.new( configure_http_login_scanner( - uri: normalize_uri(login_uri), + uri: '/', + ssl: datastore['SSL'], method: datastore['HTTP_METHOD'], cred_details: cred_collection, stop_on_success: datastore['STOP_ON_SUCCESS'], @@ -52,6 +52,12 @@ def run_host(ip) ) ) + msg = scanner.check_setup + if msg + print_brute level: :error, ip: ip, msg: msg + return + end + scanner.scan! do |result| credential_data = result.to_h credential_data.merge!(