ruby_smb 0.0.8
Sign up to get free protection for your applications and to get access to all the features.
- 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
|