Path: blob/master/lib/rex/proto/ms_tds/ms_tds_login7.rb
27907 views
require 'rex/text'12module Rex::Proto::MsTds3# see: https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-tds/773a62b6-ee89-4c02-9e5e-344882630aac4class MsTdsLogin7 < BinData::Record5endian :little67class << self8private910@@buffer_fields = []11@@buffer_field_types = {}1213def buffer_field(field_name, encoding:, onlyif: true, field_type: nil)14@@buffer_fields << field_name1516uint16 "ib_#{field_name}".to_sym, initial_value: -> { buffer_field_offset(field_name) || 0 }, onlyif: onlyif17case encoding18when Encoding::ASCII_8BIT19@@buffer_field_types[field_name] = (field_type || :uint8_array)20uint16 "cb_#{field_name}".to_sym, initial_value: -> { send(field_name)&.length || 0 }, onlyif: onlyif21when Encoding::UTF_16LE22@@buffer_field_types[field_name] = (field_type || :string16)23uint16 "cch_#{field_name}".to_sym, initial_value: -> { send(field_name)&.length || 0 }, onlyif: onlyif24else25raise RuntimeError, "Unsupported encoding: #{encoding}"26end27end28end2930uint32 :packet_length, initial_value: :num_bytes31ms_tds_version :tds_version, initial_value: MsTdsVersion::VERSION_7_132uint32 :packet_size33uint32 :client_prog_ver, initial_value: 0x0734uint32 :client_pid, initial_value: -> { rand(1024+1) }35uint32 :connection_id3637struct :option_flags_1 do38bit1 :f_set_lang, initial_value: 139bit1 :f_database40bit1 :f_use_db, initial_value: 141bit1 :f_dump_load42bit2 :f_float43bit1 :f_char44bit1 :f_byte_order45end4647struct :option_flags_2 do48bit1 :f_int_security49bit3 :f_user_type50bit1 :f_tran_boundary51bit1 :f_cache_connect52bit1 :f_odbc, initial_value: 153bit1 :f_language54end5556struct :type_flags do57bit2 :f_reserved58bit1 :f_read_only_intent59bit1 :f_oledb60bit4 :f_sql_type61end6263struct :option_flags_3 do64bit3 :f_reserved65bit1 :f_extension66bit1 :f_unknown_collation_handling67bit1 :f_user_instance68bit1 :f_send_yukon_binary_xml69bit1 :f_change_password70end7172uint32 :client_time_zone73uint32 :client_lcid7475# Offset/Length pairs for variable-length data76buffer_field :hostname, encoding: Encoding::UTF_16LE77buffer_field :username, encoding: Encoding::UTF_16LE78buffer_field :password, encoding: Encoding::UTF_16LE, field_type: :ms_tds_login7_password79buffer_field :app_name, encoding: Encoding::UTF_16LE80buffer_field :server_name, encoding: Encoding::UTF_16LE81buffer_field :unused, encoding: Encoding::ASCII_8BIT82buffer_field :extension, encoding: Encoding::ASCII_8BIT, onlyif: -> { tds_version >= MsTdsVersion::VERSION_7_4 }83buffer_field :clt_int_name, encoding: Encoding::UTF_16LE84buffer_field :language, encoding: Encoding::UTF_16LE85buffer_field :database, encoding: Encoding::UTF_16LE8687# Client MAC address (6 bytes)88uint8_array :client_id, initial_length: 6, initial_value: -> { Random.new.bytes(6).bytes }8990# More offset/length pairs91buffer_field :sspi, encoding: Encoding::ASCII_8BIT92buffer_field :attach_db_file, encoding: Encoding::UTF_16LE93buffer_field :change_password, encoding: Encoding::UTF_16LE, onlyif: -> { tds_version >= MsTdsVersion::VERSION_7_2 }94uint32 :cb_sspi_long, onlyif: -> { tds_version >= MsTdsVersion::VERSION_7_2 }9596string :buffer, initial_value: -> { build_buffer }, read_length: -> { packet_length - offset_of(buffer) }97hide :buffer9899def initialize_instance100value = super101102self.server_name = self.hostname = Rex::Text.rand_text_alpha(rand(1..8))103self.clt_int_name = self.app_name = Rex::Text.rand_text_alpha(rand(1..8))104self.language = self.database = ""105106@@buffer_fields.each do |field_name|107parameter = get_parameter(field_name)108send("#{field_name}=", parameter) if parameter109end110111value112end113114def assign(value)115super116117@@buffer_fields.each do |field_name|118next unless value.key?(field_name)119120send("#{field_name}=", value[field_name])121end122end123124def initialize_shared_instance125@@buffer_fields.each do |field_name|126define_field_accessors_for2(field_name)127end128super129end130131def do_read(val)132value = super133134@@buffer_fields.each do |field_name|135# the offset field's prefix is always ib_136field_offset = send("ib_#{field_name}")137# the size field's prefix depends on the data type, but it's always right after the offset138field_size = send(field_names[field_names.index("ib_#{field_name}".to_sym) + 1])139140field_offset -= buffer.rel_offset141if field_offset < 0142instance_variable_set("@#{field_name}", nil)143next144end145146field_cls = BinData::RegisteredClasses.lookup(@@buffer_field_types[field_name])147148case @@buffer_field_types[field_name]149when :string16, :ms_tds_login7_password150field_size *= 2151field_obj = field_cls.new(read_length: field_size)152when :uint8_array153field_obj = field_cls.new(read_until: :eof)154end155156field_data = buffer[field_offset...(field_offset + field_size)]157instance_variable_set("@#{field_name}", field_obj.read(field_data))158end159160value161end162163def snapshot164snap = super165@@buffer_fields.each do |field_name|166snap[field_name] ||= send(field_name)&.snapshot167end168snap169end170171private172173def build_buffer174buf = ''175@@buffer_fields.each do |field_name|176field_value = send(field_name)177buf << field_value.to_binary_s if field_value178end179buf180end181182def buffer_field_offset(field)183return nil unless instance_variable_get("@#{field}")184185offset = buffer.rel_offset186@@buffer_fields.each do |field_name|187break if field_name == field188189field_name = instance_variable_get("@#{field_name}")190offset += field_name.num_bytes if field_name191end192193offset194end195196def define_field_accessors_for2(field_name)197define_singleton_method(field_name) do198instance_variable_get("@#{field_name}")199end200201define_singleton_method("#{field_name}=") do |value|202unless value.nil?203field_cls = BinData::RegisteredClasses.lookup(@@buffer_field_types[field_name])204value = field_cls.new(value)205end206207instance_variable_set("@#{field_name}", value)208end209210define_singleton_method("#{field_name}?") do211!send(field_name).nil?212end213end214end215end216217