module Net::NTLM

Constants

BLOB_SIGN
DATA_REGEXP

Valid format for an NTLM hash composed of `'<LAN Manager hex digest>:<NT LAN Manager hex digest>'`.

DEFAULT_FLAGS
FLAGS
FLAG_KEYS
LAN_MANAGER_HEX_DIGEST_REGEXP

Valid format for LAN Manager hex digest portion: 32 hexadecimal characters.

LM_MAGIC
MAX64
NT_LAN_MANAGER_HEX_DIGEST_REGEXP

Valid format for NT LAN Manager hex digest portion: 32 hexadecimal characters.

SSP_SIGN
TIME_OFFSET

Public Class Methods

apply_des(plain, keys) click to toggle source
# File lib/net/ntlm.rb, line 124
def apply_des(plain, keys)
  dec = OpenSSL::Cipher.new("des-cbc")
  dec.padding = 0
  keys.map {|k|
    dec.key = k
    dec.encrypt.update(plain) + dec.final
  }
end
gen_keys(str) click to toggle source

Not sure what this is doing @param [String] str String to generate keys for @api private

# File lib/net/ntlm.rb, line 116
def gen_keys(str)
  split7(str).map{ |str7|
    bits = split7(str7.unpack("B*")[0]).inject('')\
      {|ret, tkn| ret += tkn + (tkn.gsub('1', '').size % 2).to_s }
    [bits].pack("B*")
  }
end
is_ntlm_hash?(data) click to toggle source

Takes a string and determines whether it is a valid NTLM Hash @param [String] the string to validate @return [Boolean] whether or not the string is a valid NTLM hash

# File lib/net/ntlm.rb, line 86
def is_ntlm_hash?(data)
  decoded_data = data.dup
  decoded_data = EncodeUtil.decode_utf16le(decoded_data)
  if DATA_REGEXP.match(decoded_data)
    true
  else
    false
  end
end
lm_hash(password) click to toggle source

Generates a Lan Manager Hash @param [String] password The password to base the hash on

# File lib/net/ntlm.rb, line 135
def lm_hash(password)
  keys = gen_keys password.upcase.ljust(14, "\0")
  apply_des(LM_MAGIC, keys).join
end
lm_response(arg) click to toggle source
# File lib/net/ntlm.rb, line 170
def lm_response(arg)
  begin
    hash = arg[:lm_hash]
    chal = arg[:challenge]
  rescue
    raise ArgumentError
  end
  chal = NTLM::pack_int64le(chal) if chal.is_a?(Integer)
  keys = gen_keys hash.ljust(21, "\0")
  apply_des(chal, keys).join
end
lmv2_response(arg, opt = {}) click to toggle source
# File lib/net/ntlm.rb, line 225
def lmv2_response(arg, opt = {})
  key = arg[:ntlmv2_hash]
  chal = arg[:challenge]

  chal = NTLM::pack_int64le(chal) if chal.is_a?(Integer)

  if opt[:client_challenge]
    cc  = opt[:client_challenge]
  else
    cc = rand(MAX64)
  end
  cc = NTLM::pack_int64le(cc) if cc.is_a?(Integer)

  OpenSSL::HMAC.digest(OpenSSL::Digest::MD5.new, key, chal + cc) + cc
end
ntlm2_session(arg, opt = {}) click to toggle source
# File lib/net/ntlm.rb, line 241
def ntlm2_session(arg, opt = {})
  begin
    passwd_hash = arg[:ntlm_hash]
    chal = arg[:challenge]
  rescue
    raise ArgumentError
  end
  chal = NTLM::pack_int64le(chal) if chal.is_a?(Integer)

  if opt[:client_challenge]
    cc = opt[:client_challenge]
  else
    cc = rand(MAX64)
  end
  cc = NTLM::pack_int64le(cc) if cc.is_a?(Integer)

  keys = gen_keys(passwd_hash.ljust(21, "\0"))
  session_hash = OpenSSL::Digest::MD5.digest(chal + cc).slice(0, 8)
  response = apply_des(session_hash, keys).join
  [cc.ljust(24, "\0"), response]
end
ntlm_hash(password, opt = {}) click to toggle source

Generate a NTLM Hash @param [String] password The password to base the hash on @option opt :unicode (false) Unicode encode the password

# File lib/net/ntlm.rb, line 143
def ntlm_hash(password, opt = {})
  pwd = password.dup
  unless opt[:unicode]
    pwd = EncodeUtil.encode_utf16le(pwd)
  end
  OpenSSL::Digest::MD4.digest pwd
end
ntlm_response(arg) click to toggle source
# File lib/net/ntlm.rb, line 182
def ntlm_response(arg)
  hash = arg[:ntlm_hash]
  chal = arg[:challenge]
  chal = NTLM::pack_int64le(chal) if chal.is_a?(Integer)
  keys = gen_keys hash.ljust(21, "\0")
  apply_des(chal, keys).join
end
ntlmv2_hash(user, password, target, opt={}) click to toggle source

Generate a NTLMv2 Hash @param [String] user The username @param [String] password The password @param [String] target The domain or workstation to authenticate to @option opt :unicode (false) Unicode encode the domain

# File lib/net/ntlm.rb, line 156
def ntlmv2_hash(user, password, target, opt={})
  if is_ntlm_hash? password
    decoded_password = EncodeUtil.decode_utf16le(password)
    ntlmhash = [decoded_password.upcase[33,65]].pack('H32')
  else
    ntlmhash = ntlm_hash(password, opt)
  end
  userdomain = user.upcase + target
  unless opt[:unicode]
    userdomain = EncodeUtil.encode_utf16le(userdomain)
  end
  OpenSSL::HMAC.digest(OpenSSL::Digest::MD5.new, ntlmhash, userdomain)
end
ntlmv2_response(arg, opt = {}) click to toggle source
# File lib/net/ntlm.rb, line 190
def ntlmv2_response(arg, opt = {})
  begin
    key = arg[:ntlmv2_hash]
    chal = arg[:challenge]
    ti = arg[:target_info]
  rescue
    raise ArgumentError
  end
  chal = NTLM::pack_int64le(chal) if chal.is_a?(Integer)

  if opt[:client_challenge]
    cc  = opt[:client_challenge]
  else
    cc = rand(MAX64)
  end
  cc = NTLM::pack_int64le(cc) if cc.is_a?(Integer)

  if opt[:timestamp]
    ts = opt[:timestamp]
  else
    ts = Time.now.to_i
  end
  # epoch -> milsec from Jan 1, 1601
  ts = 10_000_000 * (ts + TIME_OFFSET)

  blob = Blob.new
  blob.timestamp = ts
  blob.challenge = cc
  blob.target_info = ti

  bb = blob.serialize

  OpenSSL::HMAC.digest(OpenSSL::Digest::MD5.new, key, chal + bb) + bb
end
pack_int64le(val) click to toggle source

Conver the value to a 64-Bit Little Endian Int @param [String] val The string to convert

# File lib/net/ntlm.rb, line 98
def pack_int64le(val)
    [val & 0x00000000ffffffff, val >> 32].pack("V2")
end
split7(str) click to toggle source

Builds an array of strings that are 7 characters long @param [String] str The string to split @api private

# File lib/net/ntlm.rb, line 105
def split7(str)
  s = str.dup
  until s.empty?
    (ret ||= []).push s.slice!(0, 7)
  end
  ret
end