Skip to content

Commit b8c2643

Browse files
committed
Converting Module to LoginScanner w/ Specs
The previous commits for this Jenkins CI module relied on an obsolete pattern. Consequently, it was necessary to write this module as a LoginScanner and incorporate the appropriate specs so that the tests will run properly.
1 parent 69400cf commit b8c2643

File tree

3 files changed

+113
-48
lines changed

3 files changed

+113
-48
lines changed
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
require 'metasploit/framework/login_scanner/http'
2+
3+
module Metasploit
4+
module Framework
5+
module LoginScanner
6+
7+
# Tomcat Manager login scanner
8+
class Jenkins < HTTP
9+
10+
# Inherit LIKELY_PORTS,LIKELY_SERVICE_NAMES, and REALM_KEY from HTTP
11+
CAN_GET_SESSION = true
12+
DEFAULT_PORT = 8080
13+
PRIVATE_TYPES = [ :password ]
14+
15+
# (see Base#set_sane_defaults)
16+
def set_sane_defaults
17+
self.uri = "/j_acegi_security_check" if self.uri.nil?
18+
self.method = "POST" if self.method.nil?
19+
20+
super
21+
end
22+
23+
def attempt_login(credential)
24+
result_opts = {
25+
credential: credential,
26+
host: host,
27+
port: port,
28+
protocol: 'tcp'
29+
}
30+
if ssl
31+
result_opts[:service_name] = 'https'
32+
else
33+
result_opts[:service_name] = 'http'
34+
end
35+
begin
36+
cli = Rex::Proto::Http::Client.new(host, port, {}, ssl, ssl_version)
37+
cli.connect
38+
req = cli.request_cgi({
39+
'method'=>'POST',
40+
'uri'=>'/j_acegi_security_check',
41+
'vars_post'=> {
42+
'j_username' => credential.public,
43+
'j_password'=>credential.private
44+
}
45+
})
46+
res = cli.send_recv(req)
47+
if res && !res.headers['location'].include?('loginError')
48+
result_opts.merge!(status: Metasploit::Model::Login::Status::SUCCESSFUL, proof: res.headers)
49+
else
50+
result_opts.merge!(status: Metasploit::Model::Login::Status::INCORRECT, proof: res)
51+
end
52+
rescue ::EOFError, Errno::ETIMEDOUT, Rex::ConnectionError, ::Timeout::Error
53+
result_opts.merge!(status: Metasploit::Model::Login::Status::UNABLE_TO_CONNECT)
54+
end
55+
Result.new(result_opts)
56+
end
57+
end
58+
end
59+
end
60+
end

modules/auxiliary/scanner/http/jenkins_login.rb

Lines changed: 43 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,16 @@
44
##
55
require 'pry'
66
require 'msf/core'
7+
require 'metasploit/framework/credential_collection'
8+
require 'metasploit/framework/login_scanner/jenkins'
79

810
class Metasploit3 < Msf::Auxiliary
9-
11+
12+
include Msf::Auxiliary::Scanner
1013
include Msf::Exploit::Remote::HttpClient
1114
include Msf::Auxiliary::Report
1215
include Msf::Auxiliary::AuthBrute
13-
include Msf::Auxiliary::Scanner
14-
16+
1517
def initialize
1618
super(
1719
'Name' => 'Jenkins-CI Login Utility',
@@ -22,59 +24,52 @@ def initialize
2224

2325
register_options(
2426
[
25-
Opt::RPORT(8080),
26-
OptAddress.new('RHOST', [ true, "The target address", true])
27+
Opt::RPORT(8080)
2728
], self.class)
2829

2930
register_autofilter_ports([ 80, 443, 8080, 8081, 8000 ])
30-
deregister_options('RHOSTS')
3131
end
3232

33-
def run
34-
each_user_pass do |user, pass|
35-
next if (user.blank? or pass.blank?)
36-
vprint_status("Trying #{user} : #{pass}")
37-
if (datastore['SSL'].to_s.match(/^(t|y|1)/i))
38-
protocol = 'https://'
33+
def run_host(ip)
34+
cred_collection = Metasploit::Framework::CredentialCollection.new(
35+
blank_passwords: datastore['BLANK_PASSWORDS'],
36+
pass_file: datastore['PASS_FILE'],
37+
password: datastore['PASSWORD'],
38+
user_file: datastore['USER_FILE'],
39+
userpass_file: datastore['USERPASS_FILE'],
40+
username: datastore['USERNAME'],
41+
user_as_pass: datastore['USER_AS_PASS'],
42+
)
43+
44+
scanner = Metasploit::Framework::LoginScanner::Jenkins.new(
45+
host: ip,
46+
port: rport,
47+
proxies: datastore['PROXIES'],
48+
cred_details: cred_collection,
49+
stop_on_success: datastore['STOP_ON_SUCCESS'],
50+
connection_timeout: 10,
51+
user_agent: datastore['UserAgent'],
52+
vhost: datastore['VHOST']
53+
)
54+
55+
scanner.scan! do |result|
56+
credential_data = result.to_h
57+
credential_data.merge!(
58+
module_fullname: self.fullname,
59+
workspace_id: myworkspace_id
60+
)
61+
if result.success?
62+
credential_core = create_credential(credential_data)
63+
credential_data[:core] = credential_core
64+
create_credential_login(credential_data)
65+
66+
print_good "#{ip}:#{rport} - LOGIN SUCCESSFUL: #{result.credential}"
3967
else
40-
protocol = 'http://'
41-
do_login(user, pass)
68+
invalidate_login(credential_data)
69+
print_status "#{ip}:#{rport} - LOGIN FAILED: #{result.credential} (#{result.status}: #{result.proof})"
4270
end
4371
end
44-
end
4572

46-
def do_login(user, pass)
47-
begin
48-
post_data = {
49-
'j_username' => user,
50-
'j_password' => pass
51-
}
52-
res = send_request_cgi({
53-
'uri' => '/j_acegi_security_check',
54-
'method' => 'POST',
55-
'vars_post' => post_data
56-
})
57-
rescue ::Rex::ConnectionError => e
58-
vprint_error("#{rhost}:#{rport}#{url} - #{e}")
59-
return
60-
end
61-
if not res
62-
vprint_error("#{rhost}:#{rport}#{url} - #{e}")
63-
return
64-
end
65-
if !res.headers['location'].include? 'loginError'
66-
print_good("SUCCESSFUL LOGIN. '#{user} : #{pass}'")
67-
report_hash = {
68-
:host => datastore['RHOST'],
69-
:port => datastore['RPORT'],
70-
:sname => 'jenkins',
71-
:user => user,
72-
:pass => pass,
73-
:active => true,
74-
:type => 'password'
75-
}
76-
report_auth_info(report_hash)
77-
return :next_user
78-
end
7973
end
74+
8075
end
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
require 'spec_helper'
2+
require 'metasploit/framework/login_scanner/jenkins'
3+
4+
describe Metasploit::Framework::LoginScanner::Jenkins do
5+
6+
it_behaves_like 'Metasploit::Framework::LoginScanner::Base', has_realm_key: true, has_default_realm: false
7+
it_behaves_like 'Metasploit::Framework::LoginScanner::RexSocket'
8+
it_behaves_like 'Metasploit::Framework::LoginScanner::HTTP'
9+
10+
end

0 commit comments

Comments
 (0)