Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
rapid7
GitHub Repository: rapid7/metasploit-framework
Path: blob/master/lib/rex/proto/ms_tds/ms_tds_login7.rb
27907 views
1
require 'rex/text'
2
3
module Rex::Proto::MsTds
4
# see: https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-tds/773a62b6-ee89-4c02-9e5e-344882630aac
5
class MsTdsLogin7 < BinData::Record
6
endian :little
7
8
class << self
9
private
10
11
@@buffer_fields = []
12
@@buffer_field_types = {}
13
14
def buffer_field(field_name, encoding:, onlyif: true, field_type: nil)
15
@@buffer_fields << field_name
16
17
uint16 "ib_#{field_name}".to_sym, initial_value: -> { buffer_field_offset(field_name) || 0 }, onlyif: onlyif
18
case encoding
19
when Encoding::ASCII_8BIT
20
@@buffer_field_types[field_name] = (field_type || :uint8_array)
21
uint16 "cb_#{field_name}".to_sym, initial_value: -> { send(field_name)&.length || 0 }, onlyif: onlyif
22
when Encoding::UTF_16LE
23
@@buffer_field_types[field_name] = (field_type || :string16)
24
uint16 "cch_#{field_name}".to_sym, initial_value: -> { send(field_name)&.length || 0 }, onlyif: onlyif
25
else
26
raise RuntimeError, "Unsupported encoding: #{encoding}"
27
end
28
end
29
end
30
31
uint32 :packet_length, initial_value: :num_bytes
32
ms_tds_version :tds_version, initial_value: MsTdsVersion::VERSION_7_1
33
uint32 :packet_size
34
uint32 :client_prog_ver, initial_value: 0x07
35
uint32 :client_pid, initial_value: -> { rand(1024+1) }
36
uint32 :connection_id
37
38
struct :option_flags_1 do
39
bit1 :f_set_lang, initial_value: 1
40
bit1 :f_database
41
bit1 :f_use_db, initial_value: 1
42
bit1 :f_dump_load
43
bit2 :f_float
44
bit1 :f_char
45
bit1 :f_byte_order
46
end
47
48
struct :option_flags_2 do
49
bit1 :f_int_security
50
bit3 :f_user_type
51
bit1 :f_tran_boundary
52
bit1 :f_cache_connect
53
bit1 :f_odbc, initial_value: 1
54
bit1 :f_language
55
end
56
57
struct :type_flags do
58
bit2 :f_reserved
59
bit1 :f_read_only_intent
60
bit1 :f_oledb
61
bit4 :f_sql_type
62
end
63
64
struct :option_flags_3 do
65
bit3 :f_reserved
66
bit1 :f_extension
67
bit1 :f_unknown_collation_handling
68
bit1 :f_user_instance
69
bit1 :f_send_yukon_binary_xml
70
bit1 :f_change_password
71
end
72
73
uint32 :client_time_zone
74
uint32 :client_lcid
75
76
# Offset/Length pairs for variable-length data
77
buffer_field :hostname, encoding: Encoding::UTF_16LE
78
buffer_field :username, encoding: Encoding::UTF_16LE
79
buffer_field :password, encoding: Encoding::UTF_16LE, field_type: :ms_tds_login7_password
80
buffer_field :app_name, encoding: Encoding::UTF_16LE
81
buffer_field :server_name, encoding: Encoding::UTF_16LE
82
buffer_field :unused, encoding: Encoding::ASCII_8BIT
83
buffer_field :extension, encoding: Encoding::ASCII_8BIT, onlyif: -> { tds_version >= MsTdsVersion::VERSION_7_4 }
84
buffer_field :clt_int_name, encoding: Encoding::UTF_16LE
85
buffer_field :language, encoding: Encoding::UTF_16LE
86
buffer_field :database, encoding: Encoding::UTF_16LE
87
88
# Client MAC address (6 bytes)
89
uint8_array :client_id, initial_length: 6, initial_value: -> { Random.new.bytes(6).bytes }
90
91
# More offset/length pairs
92
buffer_field :sspi, encoding: Encoding::ASCII_8BIT
93
buffer_field :attach_db_file, encoding: Encoding::UTF_16LE
94
buffer_field :change_password, encoding: Encoding::UTF_16LE, onlyif: -> { tds_version >= MsTdsVersion::VERSION_7_2 }
95
uint32 :cb_sspi_long, onlyif: -> { tds_version >= MsTdsVersion::VERSION_7_2 }
96
97
string :buffer, initial_value: -> { build_buffer }, read_length: -> { packet_length - offset_of(buffer) }
98
hide :buffer
99
100
def initialize_instance
101
value = super
102
103
self.server_name = self.hostname = Rex::Text.rand_text_alpha(rand(1..8))
104
self.clt_int_name = self.app_name = Rex::Text.rand_text_alpha(rand(1..8))
105
self.language = self.database = ""
106
107
@@buffer_fields.each do |field_name|
108
parameter = get_parameter(field_name)
109
send("#{field_name}=", parameter) if parameter
110
end
111
112
value
113
end
114
115
def assign(value)
116
super
117
118
@@buffer_fields.each do |field_name|
119
next unless value.key?(field_name)
120
121
send("#{field_name}=", value[field_name])
122
end
123
end
124
125
def initialize_shared_instance
126
@@buffer_fields.each do |field_name|
127
define_field_accessors_for2(field_name)
128
end
129
super
130
end
131
132
def do_read(val)
133
value = super
134
135
@@buffer_fields.each do |field_name|
136
# the offset field's prefix is always ib_
137
field_offset = send("ib_#{field_name}")
138
# the size field's prefix depends on the data type, but it's always right after the offset
139
field_size = send(field_names[field_names.index("ib_#{field_name}".to_sym) + 1])
140
141
field_offset -= buffer.rel_offset
142
if field_offset < 0
143
instance_variable_set("@#{field_name}", nil)
144
next
145
end
146
147
field_cls = BinData::RegisteredClasses.lookup(@@buffer_field_types[field_name])
148
149
case @@buffer_field_types[field_name]
150
when :string16, :ms_tds_login7_password
151
field_size *= 2
152
field_obj = field_cls.new(read_length: field_size)
153
when :uint8_array
154
field_obj = field_cls.new(read_until: :eof)
155
end
156
157
field_data = buffer[field_offset...(field_offset + field_size)]
158
instance_variable_set("@#{field_name}", field_obj.read(field_data))
159
end
160
161
value
162
end
163
164
def snapshot
165
snap = super
166
@@buffer_fields.each do |field_name|
167
snap[field_name] ||= send(field_name)&.snapshot
168
end
169
snap
170
end
171
172
private
173
174
def build_buffer
175
buf = ''
176
@@buffer_fields.each do |field_name|
177
field_value = send(field_name)
178
buf << field_value.to_binary_s if field_value
179
end
180
buf
181
end
182
183
def buffer_field_offset(field)
184
return nil unless instance_variable_get("@#{field}")
185
186
offset = buffer.rel_offset
187
@@buffer_fields.each do |field_name|
188
break if field_name == field
189
190
field_name = instance_variable_get("@#{field_name}")
191
offset += field_name.num_bytes if field_name
192
end
193
194
offset
195
end
196
197
def define_field_accessors_for2(field_name)
198
define_singleton_method(field_name) do
199
instance_variable_get("@#{field_name}")
200
end
201
202
define_singleton_method("#{field_name}=") do |value|
203
unless value.nil?
204
field_cls = BinData::RegisteredClasses.lookup(@@buffer_field_types[field_name])
205
value = field_cls.new(value)
206
end
207
208
instance_variable_set("@#{field_name}", value)
209
end
210
211
define_singleton_method("#{field_name}?") do
212
!send(field_name).nil?
213
end
214
end
215
end
216
end
217