Parent

Class/Module Index [+]

Quicksearch

Rex::Proto::SMB::Client::NTLM_CRYPT

Constants

BASE
CONST

Public Class Methods

apply_des(plain, keys) click to toggle source
# File lib/rex/proto/ntlm/crypt.rb, line 88
def self.apply_des(plain, keys)
        raise RuntimeError, "No OpenSSL support" if not @@loaded_openssl
        dec = OpenSSL::Cipher::DES.new
        keys.map do |k|
                dec.key = k
                dec.encrypt.update(plain)
        end
end
decrypt_sessionkey(encrypted_session_key, user_session_key) click to toggle source
# File lib/rex/proto/ntlm/crypt.rb, line 385
def self.decrypt_sessionkey(encrypted_session_key, user_session_key)
        raise RuntimeError, "No OpenSSL support" if not @@loaded_openssl
        cipher = OpenSSL::Cipher::Cipher.new('rc4')
        cipher.decrypt
        cipher.key = user_session_key
        cipher.update(encrypted_session_key)
end
des_56_to_64(ckey56s) click to toggle source
# File lib/rex/proto/ntlm/crypt.rb, line 74
def self.des_56_to_64(ckey56s)
        ckey64 = []
        ckey56 = ckey56s.unpack('C*')
        ckey64[0] = ckey56[0]
        ckey64[1] = ((ckey56[0] << 7) & 0xFF) | (ckey56[1] >> 1)
        ckey64[2] = ((ckey56[1] << 6) & 0xFF) | (ckey56[2] >> 2)
        ckey64[3] = ((ckey56[2] << 5) & 0xFF) | (ckey56[3] >> 3)
        ckey64[4] = ((ckey56[3] << 4) & 0xFF) | (ckey56[4] >> 4)
        ckey64[5] = ((ckey56[4] << 3) & 0xFF) | (ckey56[5] >> 5)
        ckey64[6] = ((ckey56[5] << 2) & 0xFF) | (ckey56[6] >> 6)
        ckey64[7] =  (ckey56[6] << 1) & 0xFF
        ckey64.pack('C*')
end
encrypt_sessionkey(session_key, user_session_key) click to toggle source
# File lib/rex/proto/ntlm/crypt.rb, line 377
def self.encrypt_sessionkey(session_key, user_session_key)
        raise RuntimeError, "No OpenSSL support" if not @@loaded_openssl
        cipher = OpenSSL::Cipher::Cipher.new('rc4')
        cipher.encrypt
        cipher.key = user_session_key
        cipher.update(session_key)
end
gen_keys(str) click to toggle source
# File lib/rex/proto/ntlm/crypt.rb, line 70
def self.gen_keys(str)
        str.scan(/.{7}/).map{ |key| des_56_to_64(key) }
end
is_hash_from_empty_pwd?(arg) click to toggle source

this function will check if the net lm response provided correspond to en empty password

# File lib/rex/proto/ntlm/crypt.rb, line 238
def self.is_hash_from_empty_pwd?(arg)
        hash_type = arg[:type]
        raise ArgumentError,"arg[:type] is mandatory" if not hash_type
        raise ArgumentError,"arg[:type] must be lm or ntlm" if not hash_type  =~ /^((lm)|(ntlm))$/

        ntlm_ver = arg[:ntlm_ver]
        raise ArgumentError,"arg[:ntlm_ver] is mandatory" if not ntlm_ver

        hash = arg[:hash]
        raise ArgumentError,"arg[:hash] is mandatory" if not hash

        srv_chall = arg[:srv_challenge]
        raise ArgumentError,"arg[:srv_challenge] is mandatory" if not srv_chall
        raise ArgumentError,"Server challenge length must be exactly 8 bytes" if srv_chall.length != 8

        #calculate responses for empty pwd
        case ntlm_ver
        when CONST::NTLM_V1_RESPONSE
                if hash.length != 24
                        raise ArgumentError,"hash length must be exactly 24 bytes "
                end
                case hash_type
                when 'lm'
                        arglm = {   :lm_hash => self.lm_hash(''),
                                        :challenge => srv_chall}
                        calculatedhash = self.lm_response(arglm)
                when 'ntlm'
                        argntlm = {         :ntlm_hash =>  self.ntlm_hash(''),
                                        :challenge => srv_chall }
                        calculatedhash = self.ntlm_response(argntlm)
                end
        when CONST::NTLM_V2_RESPONSE
                raise ArgumentError,"hash length must be exactly 16 bytes " if hash.length != 16
                cli_chall = arg[:cli_challenge]
                raise ArgumentError,"arg[:cli_challenge] is mandatory in this case" if not cli_chall
                user = arg[:user]
                raise ArgumentError,"arg[:user] is mandatory in this case" if not user
                domain = arg[:domain]
                raise ArgumentError,"arg[:domain] is mandatory in this case" if not domain

                case hash_type
                when 'lm'
                        raise ArgumentError,"Client challenge length must be exactly 8 bytes " if cli_chall.length != 8
                        arglm = {   :ntlmv2_hash =>  self.ntlmv2_hash(user,'', domain),
                                        :challenge => srv_chall }
                        optlm = {   :client_challenge => cli_chall}
                        calculatedhash = self.lmv2_response(arglm, optlm)[0,16]
                when 'ntlm'
                        raise ArgumentError,"Client challenge length must be bigger then 8 bytes " if cli_chall.length <= 8
                        argntlm = {         :ntlmv2_hash =>  self.ntlmv2_hash(user, '', domain),
                                        :challenge => srv_chall }
                        optntlm = {         :nt_client_challenge => cli_chall}
                        calculatedhash = self.ntlmv2_response(argntlm,optntlm)[0,16]
                end
        when CONST::NTLM_2_SESSION_RESPONSE
                raise ArgumentError,"hash length must be exactly 16 bytes " if hash.length != 24
                cli_chall = arg[:cli_challenge]
                raise ArgumentError,"arg[:cli_challenge] is mandatory in this case" if not cli_chall
                raise ArgumentError,"Client challenge length must be exactly 8 bytes " if cli_chall.length != 8
                case hash_type
                when 'lm'
                        raise ArgumentError, "ntlm2_session is incompatible with lm"
                when 'ntlm'
                        argntlm = {         :ntlm_hash =>  self.ntlm_hash(''),
                                        :challenge => srv_chall }
                        optntlm = { :client_challenge => cli_chall}
                end
                calculatedhash = self.ntlm2_session(argntlm,optntlm).join[24,24]
        else
                raise ArgumentError,"ntlm_ver is of unknow type"
        end
        hash == calculatedhash
end
lanman_des(password, challenge) click to toggle source

Synonym of lm_response for old compatibility with lib/rex/proto/smb/crypt

# File lib/rex/proto/ntlm/crypt.rb, line 144
def self.lanman_des(password, challenge)
        lm_response({
                :lm_hash => self.lm_hash(password),
                :challenge => challenge
        })
end
lanman_session_key(pass, srvchall, opt = {}) click to toggle source

Used when LanMan Key flag is set

# File lib/rex/proto/ntlm/crypt.rb, line 362
def self.lanman_session_key(pass, srvchall, opt = {})
        if opt[:pass_is_hash]
                halfhash = pass[0,8]
        else
                halfhash = lm_hash(pass.upcase[0,7],true)
        end
        plain = self.lm_response({
                :lm_hash => halfhash[0,7],
                :challenge => srvchall
        }, true )
        key = halfhash  + ["bdbdbdbdbdbd"].pack("H*")
keys = self.gen_keys(key)
apply_des(plain, keys).join
end
lm_hash(password, half = false) click to toggle source
# File lib/rex/proto/ntlm/crypt.rb, line 97
def self.lm_hash(password, half = false)
        size = half ? 7 : 14
        keys = gen_keys(password.upcase.ljust(size, "\00""))
        apply_des(CONST::LM_MAGIC, keys).join
end
lm_response(arg, half = false) click to toggle source

Create the LANMAN response

# File lib/rex/proto/ntlm/crypt.rb, line 130
def self.lm_response(arg, half = false)
        begin
                hash = arg[:lm_hash]
                chal = arg[:challenge]
        rescue
                raise ArgumentError
        end
        chal = BASE::pack_int64le(chal) if chal.is_a?(Integer)
        if half then size = 7 else  size = 21 end
        keys = gen_keys hash.ljust(size, "\00"")
        apply_des(chal, keys).join
end
lmv1_user_session_key(pass, opt = {}) click to toggle source

Used when only the LMv1 response is provided (i.e., with Win9x clients)

# File lib/rex/proto/ntlm/crypt.rb, line 319
def self.lmv1_user_session_key(pass, opt = {})
        if opt[:pass_is_hash]
                usk = pass[0,8]
        else
                usk = self.lm_hash(pass.upcase[0,7],true)
        end
        usk.ljust(16,"\x00")
end
lmv2_response(arg, opt = {}) click to toggle source
# File lib/rex/proto/ntlm/crypt.rb, line 209
def self.lmv2_response(arg, opt = {})
        raise RuntimeError, "No OpenSSL support" if not @@loaded_openssl
        key = arg[:ntlmv2_hash]
        chal = arg[:challenge]

        chal = BASE::pack_int64le(chal) if chal.is_a?(::Integer)
        cc   = opt[:client_challenge] || rand(CONST::MAX64)
        cc   = BASE::pack_int64le(cc) if cc.is_a?(::Integer)

        OpenSSL::HMAC.digest(OpenSSL::Digest::MD5.new, key, chal + cc) + cc
end
lmv2_user_session_key(user, pass, domain, srv_chall, cli_chall, opt = {}) click to toggle source

Used when the LMv2 response is sent

# File lib/rex/proto/ntlm/crypt.rb, line 350
def self.lmv2_user_session_key(user, pass, domain, srv_chall, cli_chall, opt = {})
        raise RuntimeError, "No OpenSSL support" if not @@loaded_openssl

        ntlmv2_key = self.ntlmv2_hash(user, pass, domain, opt)
        hash1 = OpenSSL::HMAC.digest(OpenSSL::Digest::MD5.new, ntlmv2_key, srv_chall + cli_chall)
        OpenSSL::HMAC.digest(OpenSSL::Digest::MD5.new, ntlmv2_key, hash1)
end
Also aliased as: ntlmv2_user_session_key
make_weak_sessionkey(session_key,key_size,lanman_key = false) click to toggle source
# File lib/rex/proto/ntlm/crypt.rb, line 393
def self.make_weak_sessionkey(session_key,key_size,lanman_key = false)
        case key_size
        when 40
                if lanman_key
                        return session_key[0,5] + "\xe5\x38\xb0"
                else
                        return session_key[0,5]
                end
        when 56
                if lanman_key
                        return session_key[0,7]  + "\xa0"
                else
                        return session_key[0,7]
                end
        else #128
                return session_key[0,16]
        end
end
ntlm2_session(arg, opt = {}) click to toggle source
# File lib/rex/proto/ntlm/crypt.rb, line 221
def self.ntlm2_session(arg, opt = {})
        raise RuntimeError, "No OpenSSL support" if not @@loaded_openssl
        passwd_hash,chal = arg[:ntlm_hash],arg[:challenge]
        if not (passwd_hash and chal)
                raise RuntimeError, "ntlm_hash and challenge are required"
        end

        cc = opt[:client_challenge] || rand(CONST::MAX64)
        cc = BASE::pack_int64le(cc) if cc.is_a?(Integer)

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

Used when NTLMv1 authentication is employed with NTLM2 session security

# File lib/rex/proto/ntlm/crypt.rb, line 341
def self.ntlm2_session_user_session_key(pass, srv_chall, cli_chall, opt = {})
        raise RuntimeError, "No OpenSSL support" if not @@loaded_openssl

        ntlm_key = self.ntlmv1_user_session_key(pass, opt )
        session_chal = srv_chall + cli_chall
        OpenSSL::HMAC.digest(OpenSSL::Digest::MD5.new, ntlm_key, session_chal)
end
ntlm_hash(password, opt = {}) click to toggle source
# File lib/rex/proto/ntlm/crypt.rb, line 103
def self.ntlm_hash(password, opt = {})
        raise RuntimeError, "No OpenSSL support" if not @@loaded_openssl
        pwd = password.dup
        unless opt[:unicode]
                pwd = Rex::Text.to_unicode(pwd)
        end
        OpenSSL::Digest::MD4.digest(pwd)
end
ntlm_md4(password, challenge) click to toggle source

synonym of ntlm_response for old compatibility with lib/rex/proto/smb/crypt

# File lib/rex/proto/ntlm/crypt.rb, line 160
def self.ntlm_md4(password, challenge)
        ntlm_response({
                :ntlm_hash =>  self.ntlm_hash(password),
                :challenge => challenge
        })
end
ntlm_response(arg) click to toggle source
# File lib/rex/proto/ntlm/crypt.rb, line 151
def self.ntlm_response(arg)
        hash = arg[:ntlm_hash]
        chal = arg[:challenge]
        chal = BASE::pack_int64le(chal) if chal.is_a?(::Integer)
        keys = gen_keys(hash.ljust(21, "\00""))
        apply_des(chal, keys).join
end
ntlmv1_user_session_key(pass, opt = {}) click to toggle source

This variant is used when the client sends the NTLMv1 response

# File lib/rex/proto/ntlm/crypt.rb, line 329
def self.ntlmv1_user_session_key(pass, opt = {})
        raise RuntimeError, "No OpenSSL support" if not @@loaded_openssl

        if opt[:pass_is_hash]
                usk = pass
        else
                usk = self.ntlm_hash(pass)
        end
        OpenSSL::Digest::MD4.digest(usk)
end
ntlmv2_hash(user, password, domain, opt={}) click to toggle source

This hash is used for lmv2/ntlmv2 response calculation

# File lib/rex/proto/ntlm/crypt.rb, line 113
def self.ntlmv2_hash(user, password, domain, opt={})
        raise RuntimeError, "No OpenSSL support" if not @@loaded_openssl

        if opt[:pass_is_hash]
                ntlmhash = password
        else
                ntlmhash = ntlm_hash(password, opt)
        end
        # With Win 7 and maybe other OSs we sometimes get the domain not uppercased
        userdomain = user.upcase  + domain
        unless opt[:unicode]
                userdomain = Rex::Text.to_unicode(userdomain)
        end
        OpenSSL::HMAC.digest(OpenSSL::Digest::MD5.new, ntlmhash, userdomain)
end
ntlmv2_response(arg, opt = {}) click to toggle source
# File lib/rex/proto/ntlm/crypt.rb, line 167
def self.ntlmv2_response(arg, opt = {})
        raise RuntimeError, "No OpenSSL support" if not @@loaded_openssl

        key, chal = arg[:ntlmv2_hash], arg[:challenge]
        if not (key and chal)
                raise ArgumentError , 'ntlmv2_hash and challenge are mandatory'
        end

        chal = BASE::pack_int64le(chal) if chal.is_a?(::Integer)
        bb   = nil

        if opt[:nt_client_challenge]
                if opt[:nt_client_challenge].to_s.length <= 8
                        raise ArgumentError,"nt_client_challenge is not in a correct format "
                end
                bb = opt[:nt_client_challenge]
        else
                if not arg[:target_info]
                        raise ArgumentError, "target_info is mandatory in this case"
                end

                ti = arg[:target_info]
                cc = opt[:client_challenge] || rand(CONST::MAX64)
                cc = BASE::pack_int64le(cc) if cc.is_a?(::Integer)

                ts = opt[:timestamp] || Time.now.to_i

                # Convert the unix timestamp to windows format
                #   epoch -> milsec from Jan 1, 1601
                ts = 10000000 * (ts + CONST::TIME_OFFSET)

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

                bb = blob.serialize
        end

        OpenSSL::HMAC.digest(OpenSSL::Digest::MD5.new, key, chal + bb) + bb
end
ntlmv2_user_session_key(user, pass, domain, srv_chall, cli_chall, opt = {}) click to toggle source

[Validate]

Generated with the Darkfish Rdoc Generator 2.