Skip to content

Commit 245237d

Browse files
committed
Land rapid7#7288, Add LoginScannerfor Octopus Deploy server
2 parents 10efafe + dcf0d74 commit 245237d

File tree

3 files changed

+151
-0
lines changed

3 files changed

+151
-0
lines changed
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
require 'metasploit/framework/login_scanner/http'
2+
require 'json'
3+
4+
module Metasploit
5+
module Framework
6+
module LoginScanner
7+
8+
# Octopus Deploy login scanner
9+
class OctopusDeploy < HTTP
10+
11+
# Inherit LIKELY_PORTS,LIKELY_SERVICE_NAMES, and REALM_KEY from HTTP
12+
CAN_GET_SESSION = true
13+
DEFAULT_PORT = 80
14+
PRIVATE_TYPES = [ :password ]
15+
16+
# (see Base#set_sane_defaults)
17+
def set_sane_defaults
18+
uri = '/api/users/login' if uri.nil?
19+
method = 'POST' if method.nil?
20+
21+
super
22+
end
23+
24+
def attempt_login(credential)
25+
result_opts = {
26+
credential: credential,
27+
host: host,
28+
port: port,
29+
protocol: 'tcp'
30+
}
31+
if ssl
32+
result_opts[:service_name] = 'https'
33+
else
34+
result_opts[:service_name] = 'http'
35+
end
36+
begin
37+
json_post_data = JSON.pretty_generate({ Username: credential.public, Password: credential.private })
38+
cli = Rex::Proto::Http::Client.new(host, port, { 'Msf' => framework, 'MsfExploit' => framework_module }, ssl, ssl_version, http_username, http_password)
39+
configure_http_client(cli)
40+
cli.connect
41+
req = cli.request_cgi(
42+
'method' => 'POST',
43+
'uri' => uri,
44+
'ctype' => 'application/json',
45+
'data' => json_post_data
46+
)
47+
res = cli.send_recv(req)
48+
body = JSON.parse(res.body)
49+
if res && res.code == 200 && body.key?('IsActive') && body['IsActive']
50+
result_opts.merge!(status: Metasploit::Model::Login::Status::SUCCESSFUL, proof: res.body)
51+
else
52+
result_opts.merge!(status: Metasploit::Model::Login::Status::INCORRECT, proof: res)
53+
end
54+
rescue ::JSON::ParserError
55+
result_opts.merge!(status: Metasploit::Model::Login::Status::INCORRECT, proof: res.body)
56+
rescue ::EOFError, Errno::ETIMEDOUT, Rex::ConnectionError, ::Timeout::Error
57+
result_opts.merge!(status: Metasploit::Model::Login::Status::UNABLE_TO_CONNECT)
58+
end
59+
Result.new(result_opts)
60+
end
61+
end
62+
end
63+
end
64+
end
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
##
2+
# This module requires Metasploit: http://metasploit.com/download
3+
# Current source: https://github.com/rapid7/metasploit-framework
4+
##
5+
6+
require 'msf/core'
7+
require 'metasploit/framework/credential_collection'
8+
require 'metasploit/framework/login_scanner/octopusdeploy'
9+
10+
class MetasploitModule < Msf::Auxiliary
11+
12+
include Msf::Exploit::Remote::HttpClient
13+
include Msf::Auxiliary::Report
14+
include Msf::Auxiliary::AuthBrute
15+
include Msf::Auxiliary::Scanner
16+
17+
def initialize
18+
super(
19+
'Name' => 'Octopus Deploy Login Utility',
20+
'Description' => %q{
21+
This module simply attempts to login to a Octopus Deploy server using a specific
22+
username and password. It has been confirmed to work on version 3.4.4
23+
},
24+
'Author' => [ 'James Otten <jamesotten1[at]gmail.com>' ],
25+
'License' => MSF_LICENSE
26+
)
27+
28+
register_options(
29+
[
30+
Opt::RPORT(80),
31+
OptString.new('TARGETURI', [true, 'URI for login. Default is /api/users/login', '/api/users/login'])
32+
], self.class)
33+
end
34+
35+
def run_host(ip)
36+
cred_collection = Metasploit::Framework::CredentialCollection.new(
37+
blank_passwords: datastore['BLANK_PASSWORDS'],
38+
pass_file: datastore['PASS_FILE'],
39+
password: datastore['PASSWORD'],
40+
user_file: datastore['USER_FILE'],
41+
userpass_file: datastore['USERPASS_FILE'],
42+
username: datastore['USERNAME'],
43+
user_as_pass: datastore['USER_AS_PASS']
44+
)
45+
46+
scanner = Metasploit::Framework::LoginScanner::OctopusDeploy.new(
47+
configure_http_login_scanner(
48+
cred_details: cred_collection,
49+
stop_on_success: datastore['STOP_ON_SUCCESS'],
50+
bruteforce_speed: datastore['BRUTEFORCE_SPEED'],
51+
connection_timeout: 10,
52+
http_username: datastore['HttpUsername'],
53+
http_password: datastore['HttpPassword'],
54+
uri: datastore['TARGETURI']
55+
)
56+
)
57+
58+
scanner.scan! do |result|
59+
credential_data = result.to_h
60+
credential_data.merge!(
61+
module_fullname: fullname,
62+
workspace_id: myworkspace_id
63+
)
64+
65+
if result.success?
66+
credential_core = create_credential(credential_data)
67+
credential_data[:core] = credential_core
68+
create_credential_login(credential_data)
69+
70+
print_good "#{ip}:#{rport} - LOGIN SUCCESSFUL: #{result.credential}"
71+
else
72+
invalidate_login(credential_data)
73+
print_status "#{ip}:#{rport} - LOGIN FAILED: #{result.credential} (#{result.status})"
74+
end
75+
end
76+
end
77+
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/octopusdeploy'
3+
4+
RSpec.describe Metasploit::Framework::LoginScanner::OctopusDeploy 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)