1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131
|
module Net
module NTLM
class Message
# @private false
class Type3 < Message
string :sign, {:size => 8, :value => SSP_SIGN}
int32LE :type, {:value => 3}
security_buffer :lm_response, {:value => ""}
security_buffer :ntlm_response, {:value => ""}
security_buffer :domain, {:value => ""}
security_buffer :user, {:value => ""}
security_buffer :workstation, {:value => ""}
security_buffer :session_key, {:value => "", :active => false }
int32LE :flag, {:value => 0, :active => false }
string :os_version, {:size => 8, :active => false }
class << Type3
# Builds a Type 3 packet
# @note All options must be properly encoded with either unicode or oem encoding
# @return [Type3]
# @option arg [String] :lm_response The LM hash
# @option arg [String] :ntlm_response The NTLM hash
# @option arg [String] :domain The domain to authenticate to
# @option arg [String] :workstation The name of the calling workstation
# @option arg [String] :session_key The session key
# @option arg [Integer] :flag Flags for the packet
def create(arg, opt ={})
t = new
t.lm_response = arg[:lm_response]
t.ntlm_response = arg[:ntlm_response]
t.domain = arg[:domain]
t.user = arg[:user]
if arg[:workstation]
t.workstation = arg[:workstation]
end
if arg[:session_key]
t.enable(:session_key)
t.session_key = arg[:session_key]
end
if arg[:flag]
t.enable(:session_key)
t.enable(:flag)
t.flag = arg[:flag]
end
t
end
end
# @param server_challenge (see #password?)
def blank_password?(server_challenge)
password?('', server_challenge)
end
# @param password [String]
# @param server_challenge [String] The server's {Type2#challenge challenge} from the
# {Type2} message for which this object is a response.
# @return [true] if +password+ was the password used to generate this
# {Type3} message
# @return [false] otherwise
def password?(password, server_challenge)
case ntlm_version
when :ntlm2_session
ntlm2_session_password?(password, server_challenge)
when :ntlmv2
ntlmv2_password?(password, server_challenge)
else
raise
end
end
# @return [Symbol]
def ntlm_version
if ntlm_response.size == 24 && lm_response[0,8] != "\x00"*8 && lm_response[8,16] == "\x00"*16
:ntlm2_session
elsif ntlm_response.size == 24
:ntlmv1
elsif ntlm_response.size > 24
:ntlmv2
end
end
private
def ntlm2_session_password?(password, server_challenge)
hash = ntlm_response
_lm, empty_hash = NTLM.ntlm2_session(
{
:ntlm_hash => NTLM.ntlm_hash(password),
:challenge => server_challenge,
},
{
:client_challenge => lm_response[0,8]
}
)
hash == empty_hash
end
def ntlmv2_password?(password, server_challenge)
# The first 16 bytes of the ntlm_response are the HMAC of the blob
# that follows it.
blob = Blob.new
blob.parse(ntlm_response[16..-1])
empty_hash = NTLM.ntlmv2_response(
{
# user and domain came from the serialized data here, so
# they're already unicode
:ntlmv2_hash => NTLM.ntlmv2_hash(user, '', domain, :unicode => true),
:challenge => server_challenge,
:target_info => blob.target_info
},
{
:client_challenge => blob.challenge,
# The blob's timestamp is already in milliseconds since 1601,
# so convert it back to epoch time first
:timestamp => (blob.timestamp / 10_000_000) - NTLM::TIME_OFFSET,
}
)
empty_hash == ntlm_response
end
end
end
end
end
|