packetgen-plugin-smb 0.3.0 → 0.6.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (57) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/specs.yml +28 -0
  3. data/.rubocop.yml +8 -1
  4. data/Gemfile +15 -3
  5. data/README.md +59 -3
  6. data/Rakefile +10 -4
  7. data/examples/llmnr-responder +110 -0
  8. data/examples/smb-responder +233 -0
  9. data/lib/packetgen-plugin-smb.rb +5 -2
  10. data/lib/packetgen/plugin/gssapi.rb +11 -6
  11. data/lib/packetgen/plugin/llmnr.rb +58 -0
  12. data/lib/packetgen/plugin/netbios.rb +19 -0
  13. data/lib/packetgen/plugin/netbios/datagram.rb +108 -0
  14. data/lib/packetgen/plugin/netbios/name.rb +64 -0
  15. data/lib/packetgen/plugin/netbios/session.rb +72 -0
  16. data/lib/packetgen/plugin/ntlm.rb +211 -0
  17. data/lib/packetgen/plugin/ntlm/authenticate.rb +197 -0
  18. data/lib/packetgen/plugin/ntlm/av_pair.rb +115 -0
  19. data/lib/packetgen/plugin/ntlm/challenge.rb +140 -0
  20. data/lib/packetgen/plugin/ntlm/negotiate.rb +127 -0
  21. data/lib/packetgen/plugin/ntlm/ntlmv2_response.rb +59 -0
  22. data/lib/packetgen/plugin/smb.rb +27 -15
  23. data/lib/packetgen/plugin/smb/blocks.rb +2 -4
  24. data/lib/packetgen/plugin/smb/browser.rb +8 -8
  25. data/lib/packetgen/plugin/smb/browser/domain_announcement.rb +2 -7
  26. data/lib/packetgen/plugin/smb/browser/host_announcement.rb +10 -7
  27. data/lib/packetgen/plugin/smb/browser/local_master_announcement.rb +2 -7
  28. data/lib/packetgen/plugin/smb/close.rb +2 -2
  29. data/lib/packetgen/plugin/smb/close/request.rb +3 -3
  30. data/lib/packetgen/plugin/smb/close/response.rb +3 -3
  31. data/lib/packetgen/plugin/smb/filetime.rb +30 -3
  32. data/lib/packetgen/plugin/smb/negotiate.rb +20 -0
  33. data/lib/packetgen/plugin/smb/negotiate/dialect.rb +39 -0
  34. data/lib/packetgen/plugin/smb/negotiate/request.rb +35 -0
  35. data/lib/packetgen/plugin/smb/negotiate/response.rb +29 -0
  36. data/lib/packetgen/plugin/smb/nt_create_and_x.rb +2 -2
  37. data/lib/packetgen/plugin/smb/ntcreateandx/request.rb +5 -5
  38. data/lib/packetgen/plugin/smb/ntcreateandx/response.rb +3 -3
  39. data/lib/packetgen/plugin/smb/string.rb +60 -23
  40. data/lib/packetgen/plugin/smb/trans.rb +2 -2
  41. data/lib/packetgen/plugin/smb/trans/request.rb +4 -4
  42. data/lib/packetgen/plugin/smb/trans/response.rb +3 -3
  43. data/lib/packetgen/plugin/smb2.rb +20 -9
  44. data/lib/packetgen/plugin/smb2/base.rb +5 -7
  45. data/lib/packetgen/plugin/smb2/error.rb +3 -4
  46. data/lib/packetgen/plugin/smb2/guid.rb +6 -4
  47. data/lib/packetgen/plugin/smb2/negotiate.rb +2 -2
  48. data/lib/packetgen/plugin/smb2/negotiate/context.rb +28 -27
  49. data/lib/packetgen/plugin/smb2/negotiate/request.rb +16 -12
  50. data/lib/packetgen/plugin/smb2/negotiate/response.rb +25 -14
  51. data/lib/packetgen/plugin/smb2/session_setup.rb +2 -2
  52. data/lib/packetgen/plugin/smb2/session_setup/request.rb +12 -7
  53. data/lib/packetgen/plugin/smb2/session_setup/response.rb +13 -8
  54. data/lib/packetgen/plugin/smb_version.rb +3 -1
  55. data/packetgen-plugin-smb.gemspec +10 -15
  56. metadata +28 -81
  57. data/.travis.yml +0 -12
@@ -1,10 +1,10 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # This file is part of packetgen-plugin-smb.
2
4
  # See https://github.com/sdaubert/packetgen-plugin-smb for more informations
3
5
  # Copyright (C) 2018 Sylvain Daubert <sylvain.daubert@laposte.net>
4
6
  # This program is published under MIT license.
5
7
 
6
- # frozen_string_literal: true
7
-
8
8
  module PacketGen::Plugin
9
9
  # Server Message Block version 2 and 3 (SMB2) header.
10
10
  # @author Sylvain Daubert
@@ -38,6 +38,9 @@ module PacketGen::Plugin
38
38
  # SMB2 header size
39
39
  HEADER_SIZE = 64
40
40
 
41
+ # SMB2 pad field at its maximum length
42
+ MAX_PADDING = [0].pack('q').freeze
43
+
41
44
  # @!attribute protocol
42
45
  # This field must contain {MARKER SMB2 marker}
43
46
  # @return [String]
@@ -81,7 +84,7 @@ module PacketGen::Plugin
81
84
  # 64-bit unique ID that is created by the server to handle operations
82
85
  # asynchronously. Only present for asynchronous messages.
83
86
  # @return [Integer]
84
- define_field :async_id, PacketGen::Types::Int64le, optional: ->(h) { h.flags & 2 == 2}
87
+ define_field :async_id, PacketGen::Types::Int64le, optional: ->(h) { h.flags & 2 == 2 }
85
88
  # @!attribute reserved
86
89
  # 32-bit reserved field.
87
90
  # Only present for synchronous messages.
@@ -145,7 +148,19 @@ module PacketGen::Plugin
145
148
  PacketGen::Header.add_class krequest
146
149
  self.bind krequest, command: SMB2::COMMANDS[command], flags: ->(v) { v.nil? ? 0 : (v & 1).zero? }
147
150
  PacketGen::Header.add_class kresponse
148
- self.bind kresponse, command: SMB2::COMMANDS[command], flags: ->(v) { v.nil? ? 0 : (v & 1 == 1) }
151
+ self.bind kresponse, command: SMB2::COMMANDS[command], flags: ->(v) { v.nil? ? 1 : (v & 1 == 1) }
152
+ end
153
+
154
+ # Invert {#flags_response?}
155
+ # @return [self]
156
+ def reply!
157
+ self.flags_response = !flags_response?
158
+ end
159
+
160
+ # Check if this is really a SMB2 header. Check {#protocol} has value {MARKER}.
161
+ # @return [Boolean]
162
+ def parse?
163
+ protocol == MARKER
149
164
  end
150
165
 
151
166
  # @return [String]
@@ -166,13 +181,9 @@ module PacketGen::Plugin
166
181
  end
167
182
  end
168
183
  end
169
- # TODO: move this in netbios file when packetgen3 will be out
170
- PacketGen::Header::TCP.bind PacketGen::Header::NetBIOS::Session, dport: 445
171
- PacketGen::Header::TCP.bind PacketGen::Header::NetBIOS::Session, sport: 445
172
184
 
173
185
  PacketGen::Header.add_class SMB2
174
- PacketGen::Header::NetBIOS::Session.bind SMB2, body: ->(val) { val.nil? ? SMB2::MARKER : val[0..3] == SMB2::MARKER }
175
- PacketGen::Header::NetBIOS::Datagram.bind SMB2, body: ->(val) { val.nil? ? SMB2::MARKER : val[0..3] == SMB2::MARKER }
186
+ NetBIOS::Session.bind SMB2, body: ->(val) { val.nil? ? SMB2::MARKER : val[0..3] == SMB2::MARKER }
176
187
  end
177
188
 
178
189
  require_relative 'smb2/base'
@@ -1,10 +1,10 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # This file is part of packetgen-plugin-smb.
2
4
  # See https://github.com/sdaubert/packetgen-plugin-smb for more informations
3
5
  # Copyright (C) 2018 Sylvain Daubert <sylvain.daubert@laposte.net>
4
6
  # This program is published under MIT license.
5
7
 
6
- # frozen_string_literal: true
7
-
8
8
  require_relative 'guid'
9
9
 
10
10
  module PacketGen::Plugin
@@ -19,13 +19,11 @@ module PacketGen::Plugin
19
19
  def self.define_smb2_pad_field(name)
20
20
  prev_field = self.fields.last
21
21
  lf = lambda do |hdr|
22
- len = 8 - (hdr.offset_of(prev_field) + hdr[prev_field].sz) % 8
23
- len = 0 if len == 8
24
- len
22
+ (8 - (hdr.offset_of(prev_field) + hdr[prev_field].sz) % 8) % 8
25
23
  end
26
- define_field name, PacketGen::Types::String, default: [0].pack('q').freeze,
24
+ define_field name, PacketGen::Types::String, default: SMB2::MAX_PADDING,
27
25
  builder: ->(h, t) { t.new(length_from: -> { lf[h] }) }
28
26
  end
29
27
  end
30
28
  end
31
- end
29
+ end
@@ -1,10 +1,10 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # This file is part of packetgen-plugin-smb.
2
4
  # See https://github.com/sdaubert/packetgen-plugin-smb for more informations
3
5
  # Copyright (C) 2018 Sylvain Daubert <sylvain.daubert@laposte.net>
4
6
  # This program is published under MIT license.
5
7
 
6
- # frozen_string_literal: true
7
-
8
8
  module PacketGen::Plugin
9
9
  class SMB2
10
10
  # SMB2 Error response structure
@@ -45,6 +45,5 @@ module PacketGen::Plugin
45
45
  end
46
46
  end
47
47
  PacketGen::Header.add_class SMB2::ErrorResponse
48
- SMB2.bind SMB2::ErrorResponse, status: ->(v) { v > 0 }
48
+ SMB2.bind SMB2::ErrorResponse, status: ->(v) { v.positive? }
49
49
  end
50
-
@@ -1,10 +1,10 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # This file is part of packetgen-plugin-smb.
2
4
  # See https://github.com/sdaubert/packetgen-plugin-smb for more informations
3
5
  # Copyright (C) 2018 Sylvain Daubert <sylvain.daubert@laposte.net>
4
6
  # This program is published under MIT license.
5
7
 
6
- # frozen_string_literal: true
7
-
8
8
  module PacketGen::Plugin
9
9
  class SMB2
10
10
  # GUID, also known as UUID, is a 16-byte structure, intended to serve
@@ -22,6 +22,8 @@ module PacketGen::Plugin
22
22
  # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
23
23
  # @author Sylvain Daubert
24
24
  class GUID < PacketGen::Types::Fields
25
+ include PacketGen::Types::Fieldable
26
+
25
27
  # @!attribute data1
26
28
  # 32-bit little-endian data1
27
29
  # @return [Integer]
@@ -52,7 +54,8 @@ module PacketGen::Plugin
52
54
  # @param [String] guid
53
55
  # @return [self]
54
56
  def from_human(guid)
55
- return self
57
+ return self if guid.nil? || guid.empty?
58
+
56
59
  values = guid.split('-')
57
60
  return self if values.size != 5
58
61
 
@@ -65,4 +68,3 @@ module PacketGen::Plugin
65
68
  end
66
69
  end
67
70
  end
68
-
@@ -1,10 +1,10 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # This file is part of packetgen-plugin-smb.
2
4
  # See https://github.com/sdaubert/packetgen-plugin-smb for more informations
3
5
  # Copyright (C) 2018 Sylvain Daubert <sylvain.daubert@laposte.net>
4
6
  # This program is published under MIT license.
5
7
 
6
- # frozen_string_literal: true
7
-
8
8
  require_relative 'guid'
9
9
 
10
10
  module PacketGen::Plugin
@@ -1,10 +1,10 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # This file is part of packetgen-plugin-smb.
2
4
  # See https://github.com/sdaubert/packetgen-plugin-smb for more informations
3
5
  # Copyright (C) 2018 Sylvain Daubert <sylvain.daubert@laposte.net>
4
6
  # This program is published under MIT license.
5
7
 
6
- # frozen_string_literal: true
7
-
8
8
  module PacketGen::Plugin
9
9
  class SMB2
10
10
  module Negotiate
@@ -47,31 +47,11 @@ module PacketGen::Plugin
47
47
  # @!attribute pad
48
48
  # Padding to align next context on a 8-byte offset
49
49
  # @return [String]
50
- define_field :pad, PacketGen::Types::String, builder: ->(h, t) { t.new(length_from: -> { v = 8 - (h.offset_of(:data) + h.data_length) % 8; v == 8 ? 0 : v }) }
50
+ define_field :pad, PacketGen::Types::String, builder: ->(h, t) { t.new(length_from: -> { 8 - (h.offset_of(:data) + h.data_length) % 8 }) }
51
51
 
52
52
  # @private
53
53
  alias old_read read
54
54
 
55
- # Populate object from a binary string
56
- # @param [String] str
57
- # @return [Context]
58
- def read(str)
59
- return self if str.nil?
60
- if self.instance_of? Context
61
- self[:type].read str[0, 2]
62
- name = TYPES.key(type)
63
- return old_read(str) if name.nil?
64
-
65
- klassname = name.downcase.capitalize.gsub(/_(\w)/) { $1.upcase }
66
- return old_read(str) unless Negotiate.const_defined? klassname
67
-
68
- klass = Negotiate.const_get(klassname)
69
- klass.new.read str
70
- else
71
- old_read str
72
- end
73
- end
74
-
75
55
  # Get human-readable type
76
56
  # @return [String]
77
57
  def human_type
@@ -83,8 +63,16 @@ module PacketGen::Plugin
83
63
  def to_human
84
64
  human_type
85
65
  end
66
+
67
+ # Set {#data_length} field
68
+ # @return [Integer]
69
+ def calc_length
70
+ self[:pad].read SMB2::MAX_PADDING
71
+ self.data_length = sz - self[:pad].sz - 8
72
+ end
86
73
  end
87
74
 
75
+ # Specialized {Context} for PREAUTH_INTEGRITY_CAP type.
88
76
  class PreauthIntegrityCap < Context
89
77
  remove_field :data
90
78
  # @!attribute hash_alg_count
@@ -104,9 +92,10 @@ module PacketGen::Plugin
104
92
  # Salt value for hash
105
93
  # @return [String]
106
94
  define_field_before :pad, :salt, PacketGen::Types::String, builder: ->(h, t) { t.new(length_from: h[:salt_length]) }
107
- update_field :pad, builder: ->(h, t) { t.new(length_from: -> { v = 8 - (h.offset_of(:salt) + h.salt_length) % 8; v == 8 ? 0 : v }) }
95
+ update_field :pad, builder: ->(h, t) { t.new(length_from: -> { (8 - (h.offset_of(:salt) + h.salt_length) % 8) % 8 }) }
108
96
  end
109
97
 
98
+ # Specialized {Context} for ENCRYPTION_CAP type.
110
99
  class EncryptionCap < Context
111
100
  remove_field :data
112
101
  # @!attribute cipher_count
@@ -117,15 +106,27 @@ module PacketGen::Plugin
117
106
  # Array of 16-bit integer IDs specifying the supported encryption
118
107
  # algorithms
119
108
  # @return [PacketGen::Types::ArrayOfInt16le]
120
- define_field_before :pad, :ciphers, PacketGen::Types::ArrayOfInt16le, builder: ->(h, t) { t.new(counter: h[:hash_alg_count]) }
121
- update_field :pad, builder: ->(h, t) { t.new(length_from: -> { v = 8 - (h.offset_of(:cipher_count) + h[:cipher_count].sz) % 8; v == 8 ? 0 : v }) }
109
+ define_field_before :pad, :ciphers, PacketGen::Types::ArrayOfInt16le, builder: ->(h, t) { t.new(counter: h[:cipher_count]) }
110
+ update_field :pad, builder: ->(h, t) { t.new(length_from: -> { (8 - (h.offset_of(:cipher_count) + h[:cipher_count].sz) % 8) % 8 }) }
122
111
  end
123
112
 
124
113
  # Array of {Context}
125
114
  # @author Sylvain Daubert
126
115
  class ArrayOfContext < PacketGen::Types::Array
127
116
  set_of Context
117
+
118
+ private
119
+
120
+ def real_type(ctx)
121
+ name = Context::TYPES.key(ctx.type).to_s
122
+ klassname = name.downcase.capitalize.gsub(/_(\w)/) { $1.upcase }
123
+ if !klassname.empty? && Negotiate.const_defined?(klassname)
124
+ Negotiate.const_get(klassname)
125
+ else
126
+ ctx.class
127
+ end
128
+ end
128
129
  end
129
130
  end
130
131
  end
131
- end
132
+ end
@@ -1,10 +1,10 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # This file is part of packetgen-plugin-smb.
2
4
  # See https://github.com/sdaubert/packetgen-plugin-smb for more informations
3
5
  # Copyright (C) 2018 Sylvain Daubert <sylvain.daubert@laposte.net>
4
6
  # This program is published under MIT license.
5
7
 
6
- # frozen_string_literal: true
7
-
8
8
  module PacketGen::Plugin
9
9
  class SMB2
10
10
  module Negotiate
@@ -48,7 +48,7 @@ module PacketGen::Plugin
48
48
  SECURITY_MODES = {
49
49
  'signing_enabled' => 1,
50
50
  'signing required' => 2
51
- }
51
+ }.freeze
52
52
 
53
53
  # @!attribute structure_size
54
54
  # 16-bit negotiate request structure size. Should be 36.
@@ -124,6 +124,12 @@ module PacketGen::Plugin
124
124
  # @return [ArrayOfContext]
125
125
  define_field :context_list, ArrayOfContext, builder: ->(h, t) { t.new(counter: h[:context_count]) }
126
126
 
127
+ # Protocol name
128
+ # @return [String]
129
+ def self.protocol_name
130
+ 'SMB2::Negotiate::Request'
131
+ end
132
+
127
133
  # @return [String]
128
134
  def inspect
129
135
  super do |attr|
@@ -140,7 +146,7 @@ module PacketGen::Plugin
140
146
  str << PacketGen::Inspect::FMT_ATTR % [self[attr].class.to_s.sub(/.*::/, ''),
141
147
  attr, value]
142
148
  when :dialects
143
- list = self.dialects.map { |v| "%#04x" % v.to_i }.join(',')
149
+ list = self.dialects.map { |v| '%#x' % v.to_i }.join(',')
144
150
  str = PacketGen::Inspect.shift_level
145
151
  str << PacketGen::Inspect::FMT_ATTR % [self[attr].class.to_s.sub(/.*::/, ''),
146
152
  attr, list]
@@ -148,17 +154,15 @@ module PacketGen::Plugin
148
154
  end
149
155
  end
150
156
 
151
- # Calculate and set {#context_offset} field. Also calculate
152
- # lengths in {Context contexts}.
157
+ # Calculate and set {#context_offset} and {#pad} fields.
158
+ # Also calculate lengths in {Context contexts}.
153
159
  # @return [Integer]
154
160
  def calc_length
155
- self.context_offset = SMB2.new.sz + offset_of(:context_list)
156
- end
161
+ self[:pad].read SMB2::MAX_PADDING
157
162
 
158
- # Protocol name
159
- # @return [String]
160
- def protocol_name
161
- 'SMB2::Negotiate::Request'
163
+ self.context_offset = 0
164
+ self.context_offset = SMB2::HEADER_SIZE + offset_of(:context_list) unless context_list.empty?
165
+ context_list.each { |ctx| ctx.calc_length if ctx.respond_to? :calc_length }
162
166
  end
163
167
  end
164
168
  end
@@ -1,10 +1,10 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # This file is part of packetgen-plugin-smb.
2
4
  # See https://github.com/sdaubert/packetgen-plugin-smb for more informations
3
5
  # Copyright (C) 2018 Sylvain Daubert <sylvain.daubert@laposte.net>
4
6
  # This program is published under MIT license.
5
7
 
6
- # frozen_string_literal: true
7
-
8
8
  module PacketGen::Plugin
9
9
  class SMB2
10
10
  module Negotiate
@@ -140,10 +140,11 @@ module PacketGen::Plugin
140
140
  define_field :context_offset, PacketGen::Types::Int32le
141
141
  # @!attribute buffer
142
142
  # @return [GSSAPI]
143
- define_field :buffer, GSSAPI, token: :init
143
+ define_field :buffer, GSSAPI, token: :init, optional: ->(h) { h.buffer_offset.positive? }
144
144
  # @!attribute pad
145
145
  # Optional padding between the end of the {#buffer} field and the first negotiate
146
- # context in {#context_list} so that the first negotiate context is 8-byte aligned.
146
+ # context in {#context_list} so that the first negotiate context is 8-byte aligned
147
+ # with start of SMB2 header.
147
148
  # @return [String]
148
149
  define_smb2_pad_field :pad
149
150
  # @!attribute context_list
@@ -152,10 +153,17 @@ module PacketGen::Plugin
152
153
  # @return [ArrayOfContext]
153
154
  define_field :context_list, ArrayOfContext, builder: ->(h, t) { t.new(counter: h[:context_count]) }
154
155
 
156
+ # Protocol name
157
+ # @return [String]
158
+ def self.protocol_name
159
+ 'SMB2::Negotiate::Response'
160
+ end
161
+
155
162
  # @return [String]
156
163
  def inspect
157
164
  super do |attr|
158
165
  next unless attr == :capabilities
166
+
159
167
  value = bits_on(attr).reject { |_, v| v > 1 }
160
168
  .keys
161
169
  .select { |b| send("#{b}?") }
@@ -169,20 +177,23 @@ module PacketGen::Plugin
169
177
  end
170
178
  end
171
179
 
172
- # Calculate and set {#context_offset}, {#buffer_offset} and {#buffer_length} fields.
180
+ # Calculate and set {#context_offset}, {#buffer_offset}, {#buffer_length} and
181
+ # {#pad} fields.
173
182
  # Also calculate lengths in {Context contexts}.
174
183
  # @return [void]
175
184
  def calc_length
176
- self.context_offset = SMB2.new.sz + offset_of(:context_list)
177
- self.buffer_offset = SMB2.new.sz + offset_of(:buffer)
178
- self.buffer_length = buffer.sz
179
- context_list.each { |ctx| ctx.calc_length if ctx.respond_to? :calc_length }
180
- end
185
+ self[:pad].read SMB2::MAX_PADDING
181
186
 
182
- # Protocol name
183
- # @return [String]
184
- def protocol_name
185
- 'SMB2::Negotiate::Response'
187
+ self.buffer_length = self[:buffer].sz
188
+ self.buffer_offset = if self.buffer_length.zero?
189
+ 0
190
+ else
191
+ SMB2::HEADER_SIZE + offset_of(:buffer)
192
+ end
193
+
194
+ self.context_offset = 0
195
+ self.context_offset = SMB2::HEADER_SIZE + offset_of(:context_list) unless context_list.empty?
196
+ context_list.each { |ctx| ctx.calc_length if ctx.respond_to? :calc_length }
186
197
  end
187
198
  end
188
199
  end
@@ -1,10 +1,10 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # This file is part of packetgen-plugin-smb.
2
4
  # See https://github.com/sdaubert/packetgen-plugin-smb for more informations
3
5
  # Copyright (C) 2018 Sylvain Daubert <sylvain.daubert@laposte.net>
4
6
  # This program is published under MIT license.
5
7
 
6
- # frozen_string_literal: true
7
-
8
8
  require_relative 'guid'
9
9
 
10
10
  module PacketGen::Plugin
@@ -1,10 +1,10 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # This file is part of packetgen-plugin-smb.
2
4
  # See https://github.com/sdaubert/packetgen-plugin-smb for more informations
3
5
  # Copyright (C) 2018 Sylvain Daubert <sylvain.daubert@laposte.net>
4
6
  # This program is published under MIT license.
5
7
 
6
- # frozen_string_literal: true
7
-
8
8
  module PacketGen::Plugin
9
9
  class SMB2
10
10
  module SessionSetup
@@ -79,20 +79,25 @@ module PacketGen::Plugin
79
79
  define_field :prev_session_id, PacketGen::Types::Int64le
80
80
  # @!attribute buffer
81
81
  # @return [GSSAPI]
82
- define_field :buffer, GSSAPI, token: :response
82
+ define_field :buffer, GSSAPI, token: :response, optional: ->(h) { h.buffer_offset.positive? }
83
83
 
84
- # Calculate and set {#buffer_length} field.
84
+ # Calculate and set {#buffer_length} and {#buffer_offset} fields.
85
85
  # @return [void]
86
86
  def calc_length
87
- self.buffer_length = buffer.sz
87
+ self.buffer_length = self[:buffer].sz
88
+ self.buffer_offset = if self.buffer_length.zero?
89
+ 0
90
+ else
91
+ SMB2.new.sz + offset_of(:buffer)
92
+ end
88
93
  end
89
94
 
90
95
  # Protocol name
91
96
  # @return [String]
92
- def protocol_name
97
+ def self.protocol_name
93
98
  'SMB2::SessionSetup::Request'
94
99
  end
95
100
  end
96
101
  end
97
102
  end
98
- end
103
+ end