Skip to content

Commit

Permalink
Convert ldap modules to use the new ldap session type
Browse files Browse the repository at this point in the history
  • Loading branch information
dwelch-r7 committed May 2, 2024
1 parent 49fe400 commit 03f4ce7
Show file tree
Hide file tree
Showing 16 changed files with 167 additions and 149 deletions.
16 changes: 8 additions & 8 deletions lib/msf/core/exploit/remote/kerberos/client.rb
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ module Client

# @!attribute client
# @return [Rex::Proto::Kerberos::Client] The kerberos client
attr_accessor :client
attr_accessor :kerberos_client

def initialize(info = {})
super
Expand Down Expand Up @@ -96,20 +96,20 @@ def connect(opts={})
protocol: 'tcp'
)

disconnect if client
self.client = kerb_client
disconnect if kerberos_client
self.kerberos_client = kerb_client

kerb_client
end

# Disconnects the Kerberos client
#
# @param kerb_client [Rex::Proto::Kerberos::Client] the client to disconnect
def disconnect(kerb_client = client)
def disconnect(kerb_client = kerberos_client)
kerb_client.close if kerb_client

if kerb_client == client
self.client = nil
if kerb_client == kerberos_client
self.kerberos_client = nil
end
end

Expand All @@ -129,7 +129,7 @@ def cleanup
def send_request_as(opts = {})
connect(opts)
req = opts.fetch(:req) { build_as_request(opts) }
res = client.send_recv(req)
res = kerberos_client.send_recv(req)
disconnect
res
end
Expand All @@ -143,7 +143,7 @@ def send_request_as(opts = {})
def send_request_tgs(opts = {})
connect(opts)
req = opts.fetch(:req) { build_tgs_request(opts) }
res = client.send_recv(req)
res = kerberos_client.send_recv(req)
disconnect
res
end
Expand Down
12 changes: 7 additions & 5 deletions lib/msf/core/exploit/remote/ldap.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ module Exploit::Remote::LDAP
include Msf::Exploit::Remote::Kerberos::Ticket::Storage
include Msf::Exploit::Remote::Kerberos::ServiceAuthenticator::Options
include Metasploit::Framework::LDAP::Client
include Msf::OptionalSession::LDAP

# Initialize the LDAP client and set up the LDAP specific datastore
# options to allow the client to perform authentication and timeout
Expand All @@ -28,6 +27,8 @@ def initialize(info = {})
super

register_options([
Opt::RHOST,
Opt::RPORT(389),
OptBool.new('SSL', [false, 'Enable SSL on the LDAP connection', false]),
Msf::OptString.new('DOMAIN', [false, 'The domain to authenticate to']),
Msf::OptString.new('USERNAME', [false, 'The username to authenticate with'], aliases: ['BIND_DN']),
Expand Down Expand Up @@ -95,7 +96,6 @@ def get_connect_opts
# @return [Object] The result of whatever the block that was
# passed in via the "block" parameter yielded.
def ldap_connect(opts = {}, &block)
return yield session.client if session
ldap_open(get_connect_opts.merge(opts), &block)
end

Expand All @@ -111,7 +111,6 @@ def ldap_connect(opts = {}, &block)
# @return [Object] The result of whatever the block that was
# passed in via the "block" parameter yielded.
def ldap_open(connect_opts, &block)
return yield session.client if session
opts = resolve_connect_opts(connect_opts)
Rex::Proto::LDAP::Client.open(opts, &block)
end
Expand All @@ -136,8 +135,6 @@ def resolve_connect_opts(connect_opts)
# @yieldparam ldap [Rex::Proto::LDAP::Client] The LDAP connection handle to use for connecting to
# the target LDAP server.
def ldap_new(opts = {})
return yield session.client if session

ldap = Rex::Proto::LDAP::Client.new(resolve_connect_opts(get_connect_opts.merge(opts)))

# NASTY, but required
Expand Down Expand Up @@ -182,6 +179,11 @@ def ldap.use_connection(args)
# bind request failed.
# @return [Nil] This function does not return any data.
def validate_bind_success!(ldap)
if defined?(:session) && session
vprint_good('Successfully bound to the LDAP server via existing SESSION!')
return
end

bind_result = ldap.get_operation_result.table

# Codes taken from https://ldap.com/ldap-result-code-reference-core-ldapv3-result-codes
Expand Down
33 changes: 26 additions & 7 deletions lib/msf/core/optional_session/ldap.rb
Original file line number Diff line number Diff line change
Expand Up @@ -34,19 +34,38 @@ def initialize(info = {})
)

add_info('New in Metasploit 6.4 - This module can target a %grnSESSION%clr or an %grnRHOST%clr')
else
register_options(
[
Msf::Opt::RHOST,
Msf::Opt::RPORT(389),
]
)
end
end

def optional_session_enabled?
framework.features.enabled?(Msf::FeatureManager::LDAP_SESSION_TYPE)
end

# @see #ldap_open
# @return [Object] The result of whatever the block that was
# passed in via the "block" parameter yielded.
def ldap_connect(opts = {}, &block)
if session && !opts[:base].blank?
session.client.base = opts[:base]
end
return yield session.client if session
ldap_open(get_connect_opts.merge(opts), &block)
end

# Create a new LDAP connection using Rex::Proto::LDAP::Client.new and yield the
# resulting connection object to the caller of this method.
#
# @param opts [Hash] A hash containing the connection options for the
# LDAP connection to the target server.
# @yieldparam ldap [Rex::Proto::LDAP::Client] The LDAP connection handle to use for connecting to
# the target LDAP server.
def ldap_new(opts = {})
if session && !opts[:base].blank?
session.client.base = opts[:base]
end
return yield session.client if session
super
end
end
end
end
2 changes: 1 addition & 1 deletion lib/rex/proto/kerberos/client.rb
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ def initialize(opts = {})
# @raise [RuntimeError] if the connection can not be created
def connect
return connection if connection

raise ArgumentError, 'Missing remote address' unless self.host && self.port
case protocol
when 'tcp'
self.connection = create_tcp_connection
Expand Down
1 change: 1 addition & 0 deletions modules/auxiliary/admin/ldap/ad_cs_cert_template.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
class MetasploitModule < Msf::Auxiliary

include Msf::Exploit::Remote::LDAP
include Msf::OptionalSession::LDAP
include Msf::Auxiliary::Report

IGNORED_ATTRIBUTES = [
Expand Down
1 change: 1 addition & 0 deletions modules/auxiliary/admin/ldap/rbcd.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
class MetasploitModule < Msf::Auxiliary

include Msf::Exploit::Remote::LDAP
include Msf::OptionalSession::LDAP

ATTRIBUTE = 'msDS-AllowedToActOnBehalfOfOtherIdentity'.freeze

Expand Down
1 change: 1 addition & 0 deletions modules/auxiliary/admin/ldap/shadow_credentials.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ class MetasploitModule < Msf::Auxiliary

include Msf::Auxiliary::Report
include Msf::Exploit::Remote::LDAP
include Msf::OptionalSession::LDAP

ATTRIBUTE = 'msDS-KeyCredentialLink'.freeze

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
class MetasploitModule < Msf::Auxiliary

include Msf::Exploit::Remote::LDAP
include Msf::OptionalSession::LDAP
include Msf::Exploit::Remote::CheckModule

def initialize(info = {})
Expand Down Expand Up @@ -42,6 +43,7 @@ def initialize(info = {})
'DefaultAction' => 'Add',
'DefaultOptions' => {
'SSL' => true,
'RPORT' => 636, # SSL/TLS
'CheckModule' => 'auxiliary/gather/vmware_vcenter_vmdir_ldap'
},
'Notes' => {
Expand All @@ -53,10 +55,9 @@ def initialize(info = {})
)

register_options([
Opt::RPORT(636), # SSL/TLS
OptString.new('BASE_DN', [false, 'LDAP base DN if you already have it']),
OptString.new('NEW_USERNAME', [false, 'Username of admin user to add']),
OptString.new('NEW_PASSWORD', [false, 'Password of admin user to add'])
OptString.new('NEW_USERNAME', [true, 'Username of admin user to add']),
OptString.new('NEW_PASSWORD', [true, 'Password of admin user to add'])
])
end

Expand Down Expand Up @@ -99,7 +100,7 @@ def run
end

ldap_connect do |ldap|
print_status("Bypassing LDAP auth in vmdir service at #{peer}")
print_status("Bypassing LDAP auth in vmdir service at #{ldap.peerinfo}")
auth_bypass(ldap)

print_status("Adding admin user #{new_username} with password #{new_password}")
Expand Down
9 changes: 8 additions & 1 deletion modules/auxiliary/gather/asrep.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ class MetasploitModule < Msf::Auxiliary
include Msf::Exploit::Remote::Kerberos::Client
include Msf::Exploit::Remote::LDAP
include Msf::Exploit::Remote::LDAP::Queries
include Msf::OptionalSession::LDAP

def initialize(info = {})
super(
Expand Down Expand Up @@ -42,11 +43,16 @@ def initialize(info = {})

register_options(
[
Opt::RHOSTS(nil, true, 'The target KDC, see https://docs.metasploit.com/docs/using-metasploit/basics/using-metasploit.html'),
OptPath.new('USER_FILE', [ false, 'File containing usernames, one per line' ], conditions: %w[ACTION == BRUTE_FORCE]),
OptBool.new('USE_RC4_HMAC', [ true, 'Request using RC4 hash instead of default encryption types (faster to crack)', true]),
OptString.new('Rhostname', [ true, "The domain controller's hostname"], aliases: ['LDAP::Rhostname']),
]
)
register_option_group(name: 'SESSION',
description: 'Used when connecting to LDAP over an existing SESSION',
option_names: %w[RHOSTS],
required_options: %w[SESSION RHOSTS])
register_advanced_options(
[
OptEnum.new('LDAP::Auth', [true, 'The Authentication mechanism to use', Msf::Exploit::Remote::AuthOption::NTLM, Msf::Exploit::Remote::AuthOption::LDAP_OPTIONS]),
Expand Down Expand Up @@ -136,7 +142,8 @@ def roast(username)
client_name: username,
realm: datastore['DOMAIN'],
offered_etypes: etypes,
rport: 88
rport: 88,
rhost: datastore['RHOST']
)
hash = format_as_rep_to_john_hash(res.as_rep)
print_line(hash)
Expand Down
3 changes: 2 additions & 1 deletion modules/auxiliary/gather/ldap_esc_vulnerable_cert_finder.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
class MetasploitModule < Msf::Auxiliary

include Msf::Exploit::Remote::LDAP
include Msf::OptionalSession::LDAP

ADS_GROUP_TYPE_BUILTIN_LOCAL_GROUP = 0x00000001
ADS_GROUP_TYPE_GLOBAL_GROUP = 0x00000002
Expand Down Expand Up @@ -459,7 +460,7 @@ def run
else
print_status('Discovering base DN automatically')

unless (@base_dn = discover_base_dn(ldap))
unless (@base_dn = ldap.base_dn)
fail_with(Failure::NotFound, "Couldn't discover base DN!")
end
end
Expand Down

0 comments on commit 03f4ce7

Please sign in to comment.