328 lines
6.9 KiB
Ruby
328 lines
6.9 KiB
Ruby
# -*- coding: binary -*-
|
|
#
|
|
# 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:)
|
|
|
|
require 'rex/proto/ntlm/constants'
|
|
|
|
module Rex
|
|
module Proto
|
|
module NTLM
|
|
# The base type needed for other modules like message and crypt
|
|
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)
|
|
klass = Class.new(self) do
|
|
def self.inherited(subclass)
|
|
proto = @proto
|
|
|
|
subclass.instance_eval do
|
|
@proto = proto
|
|
end
|
|
end
|
|
end
|
|
|
|
klass.module_eval(&block)
|
|
|
|
klass
|
|
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
|