class Rex::Proto::NTLM::Message

Constants

BASE

The base type needed for other modules like message and crypt

CONST
CRYPT
Type0

sub class definitions

Type1
Type2
Type3

Public Class Methods

decode64(str) click to toggle source
# File lib/rex/proto/ntlm/message.rb, line 77
def decode64(str)
  parse(Rex::Text::decode_base64(str))
end
downgrade_type_message(message) click to toggle source

Downgrading Type messages to LMv1/NTLMv1 and removing signing

# File lib/rex/proto/ntlm/message.rb, line 489
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
parse(str) click to toggle source
# File lib/rex/proto/ntlm/message.rb, line 61
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
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) click to toggle source

Process Type 1 NTLM Messages, return a Base64 Type 2 Message

# File lib/rex/proto/ntlm/message.rb, line 399
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
process_type3_message(message) click to toggle source

Process Type 3 NTLM Message (in Base64)

from 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
# File lib/rex/proto/ntlm/message.rb, line 364
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

Public Instance Methods

data_size() click to toggle source
# File lib/rex/proto/ntlm/message.rb, line 109
def data_size
  security_buffers.inject(0){|sum, a| sum += a[1].data_size}
end
decode64(str) click to toggle source
# File lib/rex/proto/ntlm/message.rb, line 103
def decode64(str)
  parse(Rex::Text::decode_base64(str))
end
dump_flags() click to toggle source
# File lib/rex/proto/ntlm/message.rb, line 90
def dump_flags
  CONST::FLAG_KEYS.each{ |k| print(k, "=", flag?(k), "\n") }
end
encode64() click to toggle source
# File lib/rex/proto/ntlm/message.rb, line 99
def encode64
  Rex::Text::encode_base64(serialize)
end
has_flag?(flag) click to toggle source
# File lib/rex/proto/ntlm/message.rb, line 82
def has_flag?(flag)
  (self[:flag].value & CONST::FLAGS[flag]) == CONST::FLAGS[flag]
end
head_size()
Alias for: size
serialize() click to toggle source
# File lib/rex/proto/ntlm/message.rb, line 94
def serialize
  deflag
  super + security_buffers.map{|n, f| f.value}.join
end
set_flag(flag) click to toggle source
# File lib/rex/proto/ntlm/message.rb, line 86
def set_flag(flag)
  self[:flag].value  |= CONST::FLAGS[flag]
end
size() click to toggle source
# File lib/rex/proto/ntlm/message.rb, line 113
def size
  head_size + data_size
end
Also aliased as: head_size

Private Instance Methods

data_edge() click to toggle source
# File lib/rex/proto/ntlm/message.rb, line 130
def data_edge
  security_buffers.map{ |n, f| f.active ? f.offset : size}.min
end
deflag() click to toggle source
# File lib/rex/proto/ntlm/message.rb, line 123
def deflag
  security_buffers.inject(head_size){|cur, a|
    a[1].offset = cur
    cur += a[1].data_size
  }
end
security_buffers() click to toggle source
# File lib/rex/proto/ntlm/message.rb, line 119
def security_buffers
  @alist.find_all{|n, f| f.instance_of?(BASE::SecurityBuffer)}
end