ruby_smb 0.0.8
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 +7 -0
- checksums.yaml.gz.sig +0 -0
- data/.gitignore +21 -0
- data/.rspec +3 -0
- data/.simplecov +42 -0
- data/.travis.yml +5 -0
- data/.yardopts +1 -0
- data/CONTRIBUTING.md +119 -0
- data/Gemfile +13 -0
- data/LICENSE.txt +18 -0
- data/README.md +64 -0
- data/Rakefile +22 -0
- data/examples/authenticate.rb +30 -0
- data/examples/negotiate.rb +25 -0
- data/lib/ruby_smb/client/authentication.rb +236 -0
- data/lib/ruby_smb/client/negotiation.rb +126 -0
- data/lib/ruby_smb/client/signing.rb +48 -0
- data/lib/ruby_smb/client.rb +164 -0
- data/lib/ruby_smb/dispatcher/base.rb +18 -0
- data/lib/ruby_smb/dispatcher/socket.rb +53 -0
- data/lib/ruby_smb/dispatcher.rb +4 -0
- data/lib/ruby_smb/error.rb +17 -0
- data/lib/ruby_smb/field/file_time.rb +62 -0
- data/lib/ruby_smb/field/nt_status.rb +16 -0
- data/lib/ruby_smb/field/stringz16.rb +55 -0
- data/lib/ruby_smb/field.rb +7 -0
- data/lib/ruby_smb/generic_packet.rb +179 -0
- data/lib/ruby_smb/gss.rb +109 -0
- data/lib/ruby_smb/smb1/andx_block.rb +13 -0
- data/lib/ruby_smb/smb1/bit_field/capabilities.rb +39 -0
- data/lib/ruby_smb/smb1/bit_field/header_flags.rb +19 -0
- data/lib/ruby_smb/smb1/bit_field/header_flags2.rb +27 -0
- data/lib/ruby_smb/smb1/bit_field/security_mode.rb +16 -0
- data/lib/ruby_smb/smb1/bit_field.rb +10 -0
- data/lib/ruby_smb/smb1/commands.rb +9 -0
- data/lib/ruby_smb/smb1/data_block.rb +42 -0
- data/lib/ruby_smb/smb1/dialect.rb +11 -0
- data/lib/ruby_smb/smb1/packet/error_packet.rb +14 -0
- data/lib/ruby_smb/smb1/packet/negotiate_request.rb +52 -0
- data/lib/ruby_smb/smb1/packet/negotiate_response.rb +46 -0
- data/lib/ruby_smb/smb1/packet/negotiate_response_extended.rb +47 -0
- data/lib/ruby_smb/smb1/packet/session_setup_request.rb +71 -0
- data/lib/ruby_smb/smb1/packet/session_setup_response.rb +48 -0
- data/lib/ruby_smb/smb1/packet.rb +12 -0
- data/lib/ruby_smb/smb1/parameter_block.rb +42 -0
- data/lib/ruby_smb/smb1/smb_header.rb +21 -0
- data/lib/ruby_smb/smb1.rb +16 -0
- data/lib/ruby_smb/smb2/bit_field/session_flags.rb +17 -0
- data/lib/ruby_smb/smb2/bit_field/smb2_capabailities.rb +23 -0
- data/lib/ruby_smb/smb2/bit_field/smb2_header_flags.rb +23 -0
- data/lib/ruby_smb/smb2/bit_field/smb2_security_mode.rb +15 -0
- data/lib/ruby_smb/smb2/bit_field/smb2_security_mode_single.rb +14 -0
- data/lib/ruby_smb/smb2/bit_field.rb +11 -0
- data/lib/ruby_smb/smb2/commands.rb +25 -0
- data/lib/ruby_smb/smb2/packet/negotiate_request.rb +50 -0
- data/lib/ruby_smb/smb2/packet/negotiate_response.rb +33 -0
- data/lib/ruby_smb/smb2/packet/session_setup_request.rb +53 -0
- data/lib/ruby_smb/smb2/packet/session_setup_response.rb +38 -0
- data/lib/ruby_smb/smb2/packet.rb +10 -0
- data/lib/ruby_smb/smb2/smb2_header.rb +22 -0
- data/lib/ruby_smb/smb2.rb +12 -0
- data/lib/ruby_smb/version.rb +3 -0
- data/lib/ruby_smb.rb +22 -0
- data/ruby_smb.gemspec +38 -0
- data/spec/lib/ruby_smb/client_spec.rb +638 -0
- data/spec/lib/ruby_smb/dispatcher/dispatcher_base_spec.rb +22 -0
- data/spec/lib/ruby_smb/dispatcher/socket_spec.rb +60 -0
- data/spec/lib/ruby_smb/field/file_time_spec.rb +59 -0
- data/spec/lib/ruby_smb/field/nt_status_spec.rb +19 -0
- data/spec/lib/ruby_smb/field/stringz16_spec.rb +50 -0
- data/spec/lib/ruby_smb/generic_packet_spec.rb +58 -0
- data/spec/lib/ruby_smb/smb1/andx_block_spec.rb +41 -0
- data/spec/lib/ruby_smb/smb1/bit_field/capabilities_spec.rb +245 -0
- data/spec/lib/ruby_smb/smb1/bit_field/header_flags2_spec.rb +146 -0
- data/spec/lib/ruby_smb/smb1/bit_field/header_flags_spec.rb +102 -0
- data/spec/lib/ruby_smb/smb1/bit_field/security_mode_spec.rb +44 -0
- data/spec/lib/ruby_smb/smb1/data_block_spec.rb +26 -0
- data/spec/lib/ruby_smb/smb1/dialect_spec.rb +26 -0
- data/spec/lib/ruby_smb/smb1/packet/error_packet_spec.rb +39 -0
- data/spec/lib/ruby_smb/smb1/packet/negotiate_request_spec.rb +77 -0
- data/spec/lib/ruby_smb/smb1/packet/negotiate_response_extended_spec.rb +149 -0
- data/spec/lib/ruby_smb/smb1/packet/negotiate_response_spec.rb +150 -0
- data/spec/lib/ruby_smb/smb1/packet/session_setup_request_spec.rb +100 -0
- data/spec/lib/ruby_smb/smb1/packet/session_setup_response_spec.rb +72 -0
- data/spec/lib/ruby_smb/smb1/parameter_block_spec.rb +26 -0
- data/spec/lib/ruby_smb/smb1/smb_header_spec.rb +96 -0
- data/spec/lib/ruby_smb/smb2/bit_field/header_flags_spec.rb +81 -0
- data/spec/lib/ruby_smb/smb2/bit_field/session_flags_spec.rb +28 -0
- data/spec/lib/ruby_smb/smb2/bit_field/smb2_capabilities_spec.rb +72 -0
- data/spec/lib/ruby_smb/smb2/bit_field/smb_secruity_mode_spec.rb +22 -0
- data/spec/lib/ruby_smb/smb2/packet/negotiate_request_spec.rb +122 -0
- data/spec/lib/ruby_smb/smb2/packet/negotiate_response_spec.rb +147 -0
- data/spec/lib/ruby_smb/smb2/packet/session_setup_request_spec.rb +79 -0
- data/spec/lib/ruby_smb/smb2/packet/session_setup_response_spec.rb +54 -0
- data/spec/lib/ruby_smb/smb2/smb2_header_spec.rb +127 -0
- data/spec/lib/ruby_smb_spec.rb +2 -0
- data/spec/spec_helper.rb +100 -0
- data/spec/support/mock_socket_dispatcher.rb +8 -0
- data/spec/support/shared/examples/bit_field_single_flag.rb +14 -0
- data.tar.gz.sig +0 -0
- metadata +384 -0
- metadata.gz.sig +0 -0
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
module RubySMB
|
|
2
|
+
# Parent class for all SMB Packets.
|
|
3
|
+
class GenericPacket < BinData::Record
|
|
4
|
+
# Outputs a nicely formatted string representation
|
|
5
|
+
# of the Packet's structure.
|
|
6
|
+
#
|
|
7
|
+
# @return [String] formatted string representation of the packet structure
|
|
8
|
+
def self.describe
|
|
9
|
+
description = ''
|
|
10
|
+
fields_hashed.each do |field|
|
|
11
|
+
description << format_field(field)
|
|
12
|
+
end
|
|
13
|
+
description
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def display
|
|
17
|
+
display_str = ''
|
|
18
|
+
self.class.fields_hashed.each do |field|
|
|
19
|
+
display_str << display_field(field)
|
|
20
|
+
end
|
|
21
|
+
display_str
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def packet_smb_version
|
|
26
|
+
class_name = self.class.to_s
|
|
27
|
+
case class_name
|
|
28
|
+
when /SMB1/
|
|
29
|
+
'SMB1'
|
|
30
|
+
when /SMB2/
|
|
31
|
+
'SMB2'
|
|
32
|
+
else
|
|
33
|
+
''
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def status_code
|
|
38
|
+
smb_version = packet_smb_version
|
|
39
|
+
case smb_version
|
|
40
|
+
when 'SMB1'
|
|
41
|
+
status_code = WindowsError::NTStatus.find_by_retval(self.smb_header.nt_status.value).first
|
|
42
|
+
when 'SMB2'
|
|
43
|
+
status_code = WindowsError::NTStatus.find_by_retval(self.smb2_header.nt_status.value).first
|
|
44
|
+
else
|
|
45
|
+
nil
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
private
|
|
50
|
+
|
|
51
|
+
# Returns an array of hashes representing the
|
|
52
|
+
# fields for this record.
|
|
53
|
+
#
|
|
54
|
+
# @return [Array<Hash>] the array of hash representations of the record's fields
|
|
55
|
+
def self.fields_hashed
|
|
56
|
+
walk_fields(fields)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# Takes a hash representation of a field and spits out a formatted
|
|
60
|
+
# string representation.
|
|
61
|
+
#
|
|
62
|
+
# @param field [Hash] the hash representing the field
|
|
63
|
+
# @param depth [Fixnum] the recursive depth level to track indentation
|
|
64
|
+
# @return [String] the formatted string representation of the field
|
|
65
|
+
def self.format_field(field, depth = 0)
|
|
66
|
+
name = field[:name].to_s
|
|
67
|
+
if field[:class].ancestors.include? BinData::Record
|
|
68
|
+
class_str = ''
|
|
69
|
+
name.upcase!
|
|
70
|
+
else
|
|
71
|
+
class_str = field[:class].to_s.split('::').last
|
|
72
|
+
class_str = "(#{class_str})"
|
|
73
|
+
name.capitalize!
|
|
74
|
+
end
|
|
75
|
+
formatted_name = "\n" + ("\t" * depth) + name
|
|
76
|
+
formatted_string = sprintf '%-30s %-10s %s', formatted_name, class_str, field[:label]
|
|
77
|
+
field[:fields].each do |sub_field|
|
|
78
|
+
formatted_string << format_field(sub_field, (depth + 1))
|
|
79
|
+
end
|
|
80
|
+
formatted_string
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# Recursively walks through a field, building a hash representation
|
|
84
|
+
# of that field and all of it's sub-fields.
|
|
85
|
+
#
|
|
86
|
+
# @param fields [Array<BinData::SanitizedField>] an array of fields to walk
|
|
87
|
+
# @return [Array<Hash>] an array of hashes representing the fields
|
|
88
|
+
def self.walk_fields(fields)
|
|
89
|
+
field_hashes = []
|
|
90
|
+
fields.each do |field|
|
|
91
|
+
field_hash = {}
|
|
92
|
+
field_hash[:name] = field.name
|
|
93
|
+
prototype = field.prototype
|
|
94
|
+
field_hash[:class] = prototype.instance_variable_get(:@obj_class)
|
|
95
|
+
params = prototype.instance_variable_get(:@obj_params)
|
|
96
|
+
field_hash[:label] = params[:label]
|
|
97
|
+
field_hash[:value] = params[:value]
|
|
98
|
+
sub_fields = params[:fields]
|
|
99
|
+
field_hash[:fields] = if sub_fields.nil?
|
|
100
|
+
[]
|
|
101
|
+
else
|
|
102
|
+
walk_fields(sub_fields)
|
|
103
|
+
end
|
|
104
|
+
field_hashes << field_hash
|
|
105
|
+
end
|
|
106
|
+
field_hashes
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
# Takes a hash representation of a field in the packet structure and formats it
|
|
110
|
+
# into a string representing the contents of that field.
|
|
111
|
+
#
|
|
112
|
+
# @param field [Hash] hash representation of the field to display
|
|
113
|
+
# @param depth [Fixnum] the recursion depth for setting indent levels
|
|
114
|
+
# @param parents [Array<Symbol>] the name of the parent field, if any, of this field
|
|
115
|
+
# @return [String] a formatted string representing the field and it's current contents
|
|
116
|
+
def display_field(field, depth = 0, parents = [])
|
|
117
|
+
my_parents = parents.dup
|
|
118
|
+
field_str = ''
|
|
119
|
+
name = field[:name]
|
|
120
|
+
if field[:class] == BinData::Array
|
|
121
|
+
field_str = "\n" + ("\t" * depth) + name.to_s.upcase
|
|
122
|
+
parent = self
|
|
123
|
+
my_parents.each do |pfield|
|
|
124
|
+
parent = parent.send(pfield)
|
|
125
|
+
end
|
|
126
|
+
array_field = parent.send(name)
|
|
127
|
+
field_str << process_array_field(array_field, (depth + 1))
|
|
128
|
+
else
|
|
129
|
+
if my_parents.empty?
|
|
130
|
+
label = "\n" + ("\t" * depth) + name.to_s.upcase
|
|
131
|
+
if field[:class].ancestors.include? BinData::Record
|
|
132
|
+
field_str = label
|
|
133
|
+
else
|
|
134
|
+
value = self.send(name)
|
|
135
|
+
field_str = sprintf '%-30s %s', label, value
|
|
136
|
+
end
|
|
137
|
+
else
|
|
138
|
+
parent = self
|
|
139
|
+
my_parents.each do |pfield|
|
|
140
|
+
parent = parent.send(pfield)
|
|
141
|
+
end
|
|
142
|
+
value = parent.send(name)
|
|
143
|
+
label = field[:label] || name.to_s.capitalize
|
|
144
|
+
label = "\n" + ("\t" * depth) + label
|
|
145
|
+
field_str = sprintf '%-30s %s', label, value
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
my_parents << name
|
|
149
|
+
field[:fields].each do |sub_field|
|
|
150
|
+
field_str << display_field(sub_field, (depth + 1), my_parents)
|
|
151
|
+
end
|
|
152
|
+
field_str
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
# Takes a {BinData::Array} field and processes it to get
|
|
156
|
+
# the structure elements and values out since they cannot be
|
|
157
|
+
# evaluated at the class level.
|
|
158
|
+
#
|
|
159
|
+
# @param array_field [BinData::Array] the Array field to be processed
|
|
160
|
+
# @return [String] the formatted string representing the contents of the array
|
|
161
|
+
def process_array_field(array_field, depth)
|
|
162
|
+
array_field_str = ''
|
|
163
|
+
array_field.each do |sub_field|
|
|
164
|
+
fields = sub_field.class.fields.fields
|
|
165
|
+
sub_field_hashes = self.class.walk_fields(fields)
|
|
166
|
+
sub_field_hashes.each do |sub_field_hash|
|
|
167
|
+
name = sub_field_hash[:name]
|
|
168
|
+
label = sub_field_hash[:label]
|
|
169
|
+
value = sub_field.send(name)
|
|
170
|
+
label ||= name
|
|
171
|
+
label = "\n" + "\t" * depth + label
|
|
172
|
+
sub_field_str = sprintf '%-30s %s', label, value
|
|
173
|
+
array_field_str << sub_field_str
|
|
174
|
+
end
|
|
175
|
+
end
|
|
176
|
+
array_field_str
|
|
177
|
+
end
|
|
178
|
+
end
|
|
179
|
+
end
|
data/lib/ruby_smb/gss.rb
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
module RubySMB
|
|
2
|
+
|
|
3
|
+
# module containing methods required for using the [GSS-API](http://www.rfc-editor.org/rfc/rfc2743.txt)
|
|
4
|
+
# for Secure Protected Negotiation(SPNEGO) in SMB Authentication.
|
|
5
|
+
module Gss
|
|
6
|
+
|
|
7
|
+
# Cargo culted from Rex. Hacked Together ASN1 encoding that works for our GSS purposes
|
|
8
|
+
# @todo Document these magic numbers
|
|
9
|
+
def self.asn1encode(str = '')
|
|
10
|
+
# If the high bit of the first byte is 1, it contains the number of
|
|
11
|
+
# length bytes that follow
|
|
12
|
+
case str.length
|
|
13
|
+
when 0..0x7F
|
|
14
|
+
encoded_string = [str.length].pack('C') + str
|
|
15
|
+
when 0x80..0xFF
|
|
16
|
+
encoded_string = [0x81, str.length].pack('CC') + str
|
|
17
|
+
when 0x100..0xFFFF
|
|
18
|
+
encoded_string = [0x82, str.length].pack('Cn') + str
|
|
19
|
+
when 0x10000..0xffffff
|
|
20
|
+
encoded_string = [0x83, str.length >> 16, str.length & 0xFFFF].pack('CCn') + str
|
|
21
|
+
when 0x1000000..0xffffffff
|
|
22
|
+
encoded_string = [0x84, str.length].pack('CN') + str
|
|
23
|
+
else
|
|
24
|
+
raise RubySMB::Error::ASN1Encoding, "Source string is too long. Size is #{str.length}"
|
|
25
|
+
end
|
|
26
|
+
encoded_string
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# Create a GSS Security Blob of an NTLM Type 1 Message.
|
|
30
|
+
# This code has been cargo culted and needs to be researched
|
|
31
|
+
# and refactored into something better later.
|
|
32
|
+
#@todo Refactor this into non-magical code
|
|
33
|
+
def self.gss_type1(type1)
|
|
34
|
+
"\x60".force_encoding("binary") + self.asn1encode(
|
|
35
|
+
"\x06".force_encoding("binary") + self.asn1encode(
|
|
36
|
+
"\x2b\x06\x01\x05\x05\x02".force_encoding("binary")
|
|
37
|
+
) +
|
|
38
|
+
"\xa0".force_encoding("binary") + self.asn1encode(
|
|
39
|
+
"\x30".force_encoding("binary") + self.asn1encode(
|
|
40
|
+
"\xa0".force_encoding("binary") + self.asn1encode(
|
|
41
|
+
"\x30".force_encoding("binary") + self.asn1encode(
|
|
42
|
+
"\x06".force_encoding("binary") + self.asn1encode(
|
|
43
|
+
"\x2b\x06\x01\x04\x01\x82\x37\x02\x02\x0a".force_encoding("binary")
|
|
44
|
+
)
|
|
45
|
+
)
|
|
46
|
+
) +
|
|
47
|
+
"\xa2".force_encoding("binary") + self.asn1encode(
|
|
48
|
+
"\x04".force_encoding("binary") + self.asn1encode(
|
|
49
|
+
type1
|
|
50
|
+
)
|
|
51
|
+
)
|
|
52
|
+
)
|
|
53
|
+
)
|
|
54
|
+
)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
# Create a GSS Security Blob of an NTLM Type 2 Message.
|
|
59
|
+
# This code has been cargo culted and needs to be researched
|
|
60
|
+
# and refactored into something better later.
|
|
61
|
+
def self.gss_type2(type2)
|
|
62
|
+
|
|
63
|
+
blob =
|
|
64
|
+
"\xa1" + self.asn1encode(
|
|
65
|
+
"\x30" + self.asn1encode(
|
|
66
|
+
"\xa0" + self.asn1encode(
|
|
67
|
+
"\x0a" + self.asn1encode(
|
|
68
|
+
"\x01"
|
|
69
|
+
)
|
|
70
|
+
) +
|
|
71
|
+
"\xa1" + self.asn1encode(
|
|
72
|
+
"\x06" + self.asn1encode(
|
|
73
|
+
"\x2b\x06\x01\x04\x01\x82\x37\x02\x02\x0a"
|
|
74
|
+
)
|
|
75
|
+
) +
|
|
76
|
+
"\xa2" + self.asn1encode(
|
|
77
|
+
"\x04" + self.asn1encode(
|
|
78
|
+
type2
|
|
79
|
+
)
|
|
80
|
+
)
|
|
81
|
+
)
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
return blob
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# Create a GSS Security Blob of an NTLM Type 3 Message.
|
|
88
|
+
# This code has been cargo culted and needs to be researched
|
|
89
|
+
# and refactored into something better later.
|
|
90
|
+
#@todo Refactor this into non-magical code
|
|
91
|
+
def self.gss_type3(type3)
|
|
92
|
+
gss =
|
|
93
|
+
"\xa1".force_encoding("binary") + self.asn1encode(
|
|
94
|
+
"\x30".force_encoding("binary") + self.asn1encode(
|
|
95
|
+
"\xa2".force_encoding("binary") + self.asn1encode(
|
|
96
|
+
"\x04".force_encoding("binary") + self.asn1encode(
|
|
97
|
+
type3
|
|
98
|
+
)
|
|
99
|
+
)
|
|
100
|
+
)
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
gss
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
end
|
|
109
|
+
end
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
module RubySMB
|
|
2
|
+
module SMB1
|
|
3
|
+
# Represents the ANDX Block in SMB1 ANDX Command Packets
|
|
4
|
+
# [2.2.3.4 Batched Messages ("AndX" Messages)](https://msdn.microsoft.com/en-us/library/ee442210.aspx)
|
|
5
|
+
class AndXBlock < BinData::Record
|
|
6
|
+
endian :little
|
|
7
|
+
|
|
8
|
+
bit8 :andx_command, label: 'Next Command Code', initial_value: RubySMB::SMB1::Commands::SMB_COM_NO_ANDX_COMMAND
|
|
9
|
+
bit8 :andx_reserved, label: 'AndX Reserved', initial_value: 0x00
|
|
10
|
+
bit16 :andx_offset, label: 'Andx Offset', initial_value: 0x00
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
module RubySMB
|
|
2
|
+
module SMB1
|
|
3
|
+
module BitField
|
|
4
|
+
# The Capabilities bit-field for a NegotiateResponse as defined in
|
|
5
|
+
# [2.2.4.52.2 Response](https://msdn.microsoft.com/en-us/library/ee441946.aspx)
|
|
6
|
+
class Capabilities < BinData::Record
|
|
7
|
+
endian :little
|
|
8
|
+
bit1 :level_2_oplocks, label: 'Level II OpLocks', initial_value: 1
|
|
9
|
+
bit1 :nt_status, label: 'NTStatus Codes', initial_value: 1
|
|
10
|
+
bit1 :rpc_remote_apis, label: 'MS-RPC Supported'
|
|
11
|
+
bit1 :nt_smbs, label: 'NT Lan Manager', initial_value: 1
|
|
12
|
+
bit1 :large_files, label: '64-bit File offsets'
|
|
13
|
+
bit1 :unicode, label: 'Unicode Strings', initial_value: 1
|
|
14
|
+
bit1 :mpx_mode, label: 'Multiplex Mode'
|
|
15
|
+
bit1 :raw_mode, label: 'Raw Mode'
|
|
16
|
+
# Byte Border
|
|
17
|
+
bit1 :large_writex, label: 'Large Write Andx'
|
|
18
|
+
bit1 :large_readx, label: 'Large Read Andx'
|
|
19
|
+
bit1 :info_level_passthru, label: 'Infolevel Passthrough'
|
|
20
|
+
bit1 :dfs, label: 'DFS'
|
|
21
|
+
bit1 :reserved1, label: 'Reserved', initial_value: 0
|
|
22
|
+
bit1 :bulk_transfer, label: 'Bulk Transfer', initial_value: 0
|
|
23
|
+
bit1 :nt_find, label: 'Trans2 Find'
|
|
24
|
+
bit1 :lock_and_read, label: 'Lock And Read'
|
|
25
|
+
# Byte Border
|
|
26
|
+
bit1 :unix, label: 'UNIX Extensions'
|
|
27
|
+
bit6 :reserved2, label: 'Reserved', initial_value: 0
|
|
28
|
+
bit1 :lwio, label: 'LWIO IOCTL/FSCTL'
|
|
29
|
+
# Byte Border
|
|
30
|
+
bit1 :extended_security, label: 'Extended Security', initial_value: 1
|
|
31
|
+
bit1 :reserved3, label: 'Reserved', initial_value: 0
|
|
32
|
+
bit1 :dynamic_reauth, label: 'Dynamic Reauth'
|
|
33
|
+
bit3 :reserved4, label: 'Reserved', initial_value: 0
|
|
34
|
+
bit1 :compressed_data, label: 'Compressed Data'
|
|
35
|
+
bit1 :reserved5, label: 'Reserved', initial_value: 0
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
module RubySMB
|
|
2
|
+
module SMB1
|
|
3
|
+
module BitField
|
|
4
|
+
# The Flags bit-field for an SMB1 Header as defined in
|
|
5
|
+
# [2.2.3.1 SMB Header Extensions](https://msdn.microsoft.com/en-us/library/cc246254.aspx)
|
|
6
|
+
class HeaderFlags < BinData::Record
|
|
7
|
+
endian :little
|
|
8
|
+
bit1 :reply, label: 'Response Packet?'
|
|
9
|
+
bit1 :opbatch, label: 'Batch OpLock', initial_value: 0
|
|
10
|
+
bit1 :oplock, label: 'Exclusive Oplock', initial_value: 0
|
|
11
|
+
bit1 :canonicalized_paths, label: 'Canonicalized Pathnames', initial_value: 1
|
|
12
|
+
bit1 :case_insensitive, label: 'Pathnames Case Insensitive', initial_value: 1
|
|
13
|
+
bit1 :reserved, label: 'Flags Reserved', initial_value: 0
|
|
14
|
+
bit1 :buf_avail, label: 'Receive Buffer Available', initial_value: 0
|
|
15
|
+
bit1 :lock_and_read_ok, label: 'Lock&Read Supported'
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
module RubySMB
|
|
2
|
+
module SMB1
|
|
3
|
+
module BitField
|
|
4
|
+
# The Flags2 bit-field for an SMB1 Header as defined in
|
|
5
|
+
# [2.2.3.1 SMB Header Extensions](https://msdn.microsoft.com/en-us/library/cc246254.aspx)
|
|
6
|
+
class HeaderFlags2 < BinData::Record
|
|
7
|
+
endian :little
|
|
8
|
+
bit1 :reserved1, label: 'Reserved', initial_value: 0
|
|
9
|
+
bit1 :is_long_name, label: 'Long Names Used'
|
|
10
|
+
bit1 :reserved2, label: 'Reserved', initial_value: 0
|
|
11
|
+
bit1 :signature_required, label: 'Security Signature Required'
|
|
12
|
+
bit1 :compressed, label: 'Compressed'
|
|
13
|
+
bit1 :security_signature, label: 'Security Signing'
|
|
14
|
+
bit1 :eas, label: 'Extended Attributes'
|
|
15
|
+
bit1 :long_names, label: 'Long Names Allowed', initial_value: 1
|
|
16
|
+
# Byte Border
|
|
17
|
+
bit1 :unicode, label: 'Unicode Strings', initial_value: 0
|
|
18
|
+
bit1 :nt_status, label: 'NTStatus Errors', initial_value: 1
|
|
19
|
+
bit1 :paging_io, label: 'Read if Execute', initial_value: 1
|
|
20
|
+
bit1 :dfs, label: 'Use DFS'
|
|
21
|
+
bit1 :extended_security, label: 'Extended Security', inital_value: 1
|
|
22
|
+
bit1 :reparse_path, label: '@GMT Token Required'
|
|
23
|
+
resume_byte_alignment
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
module RubySMB
|
|
2
|
+
module SMB1
|
|
3
|
+
module BitField
|
|
4
|
+
# The SecurityMode bit-field for a NegotiateResponse as defined in
|
|
5
|
+
# [2.2.4.52.2 Response](https://msdn.microsoft.com/en-us/library/ee441946.aspx)
|
|
6
|
+
class SecurityMode < BinData::Record
|
|
7
|
+
endian :little
|
|
8
|
+
bit4 :reserved, label: 'Reserved'
|
|
9
|
+
bit1 :security_signatures_required, label: 'Signatures Required'
|
|
10
|
+
bit1 :security_signatures_enabled, label: 'Signatures Enabled'
|
|
11
|
+
bit1 :encrypt_passwords, label: 'Encrypted Password', initial_value: 1
|
|
12
|
+
bit1 :user_security, label: 'User Level Access', initial_value: 1
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
module RubySMB
|
|
2
|
+
module SMB1
|
|
3
|
+
module BitField
|
|
4
|
+
require 'ruby_smb/smb1/bit_field/header_flags'
|
|
5
|
+
require 'ruby_smb/smb1/bit_field/header_flags2'
|
|
6
|
+
require 'ruby_smb/smb1/bit_field/security_mode'
|
|
7
|
+
require 'ruby_smb/smb1/bit_field/capabilities'
|
|
8
|
+
end
|
|
9
|
+
end
|
|
10
|
+
end
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
module RubySMB
|
|
2
|
+
module SMB1
|
|
3
|
+
# Represents the DataBlock portion of an SMB1 Packet. The DataBlock will
|
|
4
|
+
# always contain a byte_count field that gives the size of the rest of
|
|
5
|
+
# the data block in bytes.
|
|
6
|
+
class DataBlock < BinData::Record
|
|
7
|
+
endian :little
|
|
8
|
+
|
|
9
|
+
uint16 :byte_count, label: 'Byte Count', value: -> { calculate_byte_count }
|
|
10
|
+
|
|
11
|
+
# Class method to stub byte count calculation during
|
|
12
|
+
# lazy evaluation.
|
|
13
|
+
#
|
|
14
|
+
# @return [Fixnum] will always return 0
|
|
15
|
+
def self.calculate_byte_count
|
|
16
|
+
0
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# Returns the name of all fields, other than byte_count, in
|
|
20
|
+
# the DataBlock as symbols.
|
|
21
|
+
#
|
|
22
|
+
# @return [Array<Symbol>] the names of all other DataBlock fields
|
|
23
|
+
def self.data_fields
|
|
24
|
+
fields = self.fields.collect(&:name)
|
|
25
|
+
fields.reject { |field| field == :byte_count }
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# Calculates the size of the other fields in the DataBlock
|
|
29
|
+
# in Bytes.
|
|
30
|
+
#
|
|
31
|
+
# @return [Fixnum] The size of the DataBlock in Words
|
|
32
|
+
def calculate_byte_count
|
|
33
|
+
total_count = 0
|
|
34
|
+
self.class.data_fields.each do |field_name|
|
|
35
|
+
field_value = send(field_name)
|
|
36
|
+
total_count += field_value.do_num_bytes
|
|
37
|
+
end
|
|
38
|
+
total_count
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
module RubySMB
|
|
2
|
+
module SMB1
|
|
3
|
+
# This class represents the Dialect for a NegotiateRequest.
|
|
4
|
+
# [2.2.4.52.1](https://msdn.microsoft.com/en-us/library/ee441572.aspx)
|
|
5
|
+
class Dialect < BinData::Record
|
|
6
|
+
endian :little
|
|
7
|
+
bit8 :buffer_format, label: 'Buffer Format ID', initial_value: 0x2
|
|
8
|
+
stringz :dialect_string, label: 'Dialect Name'
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
module RubySMB
|
|
2
|
+
module SMB1
|
|
3
|
+
module Packet
|
|
4
|
+
|
|
5
|
+
# This packet rpresent an SMB1 Response Packet when an Error has occured.
|
|
6
|
+
# The Parameter and Data Blocks will be empty, for reasons.
|
|
7
|
+
class ErrorPacket < RubySMB::GenericPacket
|
|
8
|
+
smb_header :smb_header
|
|
9
|
+
parameter_block :parameter_block
|
|
10
|
+
data_block :data_block
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
module RubySMB
|
|
2
|
+
module SMB1
|
|
3
|
+
module Packet
|
|
4
|
+
# A SMB1 SMB_COM_NEGOTIATE Request Packet as defined in
|
|
5
|
+
# [2.2.4.52.1](https://msdn.microsoft.com/en-us/library/ee441572.aspx)
|
|
6
|
+
class NegotiateRequest < RubySMB::GenericPacket
|
|
7
|
+
# Represents the specific layout of the DataBlock for a NegotiateRequest Packet.
|
|
8
|
+
class DataBlock < RubySMB::SMB1::DataBlock
|
|
9
|
+
array :dialects, label: 'Dialects', type: :dialect, read_until: :eof
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
smb_header :smb_header
|
|
13
|
+
parameter_block :parameter_block
|
|
14
|
+
data_block :data_block
|
|
15
|
+
|
|
16
|
+
def initialize_instance
|
|
17
|
+
super
|
|
18
|
+
smb_header.command = RubySMB::SMB1::Commands::SMB_COM_NEGOTIATE
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# Add an individual Dialect string to the list of
|
|
22
|
+
# Dialects in the packet.
|
|
23
|
+
#
|
|
24
|
+
# @param dialect_string [String] The string representing the Dialect to be negotiated
|
|
25
|
+
# @return [BinData::Array] A BinData array containing all the currently set dialects.
|
|
26
|
+
def add_dialect(dialect_string)
|
|
27
|
+
new_dialect = Dialect.new(dialect_string: dialect_string)
|
|
28
|
+
data_block.dialects << new_dialect
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Returns the Dialects array as a normal Ruby {Array}.
|
|
32
|
+
#
|
|
33
|
+
# @return [Array<Hash>] array of the set dialects on the packet
|
|
34
|
+
def dialects
|
|
35
|
+
data_block.dialects.to_a
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# Sets the entire list of dialects for the Negotiate Request.
|
|
39
|
+
#
|
|
40
|
+
# @param dialect_array [Array<String>] An array of dialect strings to set on the packet
|
|
41
|
+
# @return [BinData::Array] A BinData array containing all the currently set dialects.
|
|
42
|
+
def set_dialects(dialect_array)
|
|
43
|
+
data_block.dialects.clear
|
|
44
|
+
dialect_array.each do |dialect_string|
|
|
45
|
+
add_dialect(dialect_string)
|
|
46
|
+
end
|
|
47
|
+
data_block.dialects
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
module RubySMB
|
|
2
|
+
module SMB1
|
|
3
|
+
module Packet
|
|
4
|
+
# A SMB1 SMB_COM_NEGOTIATE Non-Extended Security Response Packet as defined in
|
|
5
|
+
# [2.2.4.5.2.2 Non-Extended Security Response](https://msdn.microsoft.com/en-us/library/cc246327.aspx)
|
|
6
|
+
class NegotiateResponse < RubySMB::GenericPacket
|
|
7
|
+
# An SMB_Parameters Block as defined by the {NegotiateResponse}.
|
|
8
|
+
class ParameterBlock < RubySMB::SMB1::ParameterBlock
|
|
9
|
+
uint16 :dialect_index, label: 'Dialect Index'
|
|
10
|
+
security_mode :security_mode
|
|
11
|
+
uint16 :max_mpx_count, label: 'Max Multiplex Count'
|
|
12
|
+
uint16 :max_number_vcs, label: 'Max Virtual Circuits'
|
|
13
|
+
uint32 :max_buffer_size, label: 'Max Buffer Size'
|
|
14
|
+
uint32 :max_raw_size, label: 'Max Raw Size'
|
|
15
|
+
uint32 :session_key, label: 'Session Key'
|
|
16
|
+
capabilities :capabilities
|
|
17
|
+
file_time :system_time, label: 'Server System Time'
|
|
18
|
+
int16 :server_time_zone, label: 'Server TimeZone'
|
|
19
|
+
uint8 :challenge_length, label: 'Challenge Length', initial_value: 0x08
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# An SMB_Data Block as defined by the {NegotiateResponse}
|
|
23
|
+
class DataBlock < RubySMB::SMB1::DataBlock
|
|
24
|
+
string :challenge, label: 'Auth Challenge', length: 8
|
|
25
|
+
stringz16 :domain_name, label: 'Primary Domain'
|
|
26
|
+
stringz16 :server_name, label: 'Server Name'
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
smb_header :smb_header
|
|
30
|
+
parameter_block :parameter_block
|
|
31
|
+
data_block :data_block
|
|
32
|
+
|
|
33
|
+
def initialize_instance
|
|
34
|
+
super
|
|
35
|
+
header = smb_header
|
|
36
|
+
header.command = RubySMB::SMB1::Commands::SMB_COM_NEGOTIATE
|
|
37
|
+
header.flags.reply = 1
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def valid?
|
|
41
|
+
smb_header.command == RubySMB::SMB1::Commands::SMB_COM_NEGOTIATE
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
module RubySMB
|
|
2
|
+
module SMB1
|
|
3
|
+
module Packet
|
|
4
|
+
# A SMB1 SMB_COM_NEGOTIATE Extended Security Response Packet as defined in
|
|
5
|
+
# [2.2.4.5.2.1 Extended Security Response](https://msdn.microsoft.com/en-us/library/cc246326.aspx)
|
|
6
|
+
class NegotiateResponseExtended < RubySMB::GenericPacket
|
|
7
|
+
# An SMB_Parameters Block as defined by the {NegotiateResponseExtended}.
|
|
8
|
+
class ParameterBlock < RubySMB::SMB1::ParameterBlock
|
|
9
|
+
uint16 :dialect_index, label: 'Dialect Index'
|
|
10
|
+
security_mode :security_mode
|
|
11
|
+
uint16 :max_mpx_count, label: 'Max Multiplex Count'
|
|
12
|
+
uint16 :max_number_vcs, label: 'Max Virtual Circuits'
|
|
13
|
+
uint32 :max_buffer_size, label: 'Max Buffer Size'
|
|
14
|
+
uint32 :max_raw_size, label: 'Max Raw Size'
|
|
15
|
+
uint32 :session_key, label: 'Session Key'
|
|
16
|
+
capabilities :capabilities
|
|
17
|
+
file_time :system_time, label: 'Server System Time'
|
|
18
|
+
int16 :server_time_zone, label: 'Server TimeZone'
|
|
19
|
+
uint8 :challenge_length, label: 'Challenge Length', initial_value: 0x00
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# An SMB_Data Block as defined by the {NegotiateResponseExtended}
|
|
23
|
+
class DataBlock < RubySMB::SMB1::DataBlock
|
|
24
|
+
string :server_guid, label: 'Server GUID', length: 16
|
|
25
|
+
rest :security_blob, label: 'GSS Security BLOB'
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
smb_header :smb_header
|
|
29
|
+
parameter_block :parameter_block
|
|
30
|
+
data_block :data_block
|
|
31
|
+
|
|
32
|
+
def initialize_instance
|
|
33
|
+
super
|
|
34
|
+
header = smb_header
|
|
35
|
+
header.command = RubySMB::SMB1::Commands::SMB_COM_NEGOTIATE
|
|
36
|
+
header.flags.reply = 1
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def valid?
|
|
40
|
+
return false unless smb_header.command == RubySMB::SMB1::Commands::SMB_COM_NEGOTIATE
|
|
41
|
+
return false unless parameter_block.capabilities.extended_security == 1
|
|
42
|
+
true
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|