# -*- coding: binary -*- require 'rex/proto/mssql/client' require 'metasploit/framework/tcp/client' module Msf ### # # This module exposes methods for querying a remote MSSQL service # ### module Exploit::Remote::MSSQL include Exploit::Remote::MSSQL_COMMANDS include Exploit::Remote::Udp include Exploit::Remote::Tcp include Exploit::Remote::NTLM::Client include Msf::Exploit::Remote::Kerberos::Ticket::Storage include Msf::Exploit::Remote::Kerberos::ServiceAuthenticator::Options attr_accessor :mssql_client # # Creates an instance of a MSSQL exploit module. # def initialize(info = {}) super # Register the options that all MSSQL exploits may make use of. register_options( [ Opt::RHOST, Opt::RPORT(1433), OptString.new('USERNAME', [ false, 'The username to authenticate as', 'sa']), OptString.new('PASSWORD', [ false, 'The password for the specified username', '']), OptBool.new('USE_WINDOWS_AUTHENT', [ true, 'Use windows authentication (requires DOMAIN option set)', false]), # OptBool.new('TDSENCRYPTION', [ true, 'Use TLS/SSL for TDS data "Force Encryption"', false]), - TODO: support TDS Encryption ], Msf::Exploit::Remote::MSSQL) register_advanced_options( [ OptPath.new('HEX2BINARY', [ false, "The path to the hex2binary script on the disk", File.join(Msf::Config.data_directory, "exploits", "mssql", "h2b") ]), OptString.new('DOMAIN', [ true, 'The domain to use for windows authentication', 'WORKSTATION'], aliases: ['MssqlDomain']), *kerberos_storage_options(protocol: 'Mssql'), *kerberos_auth_options(protocol: 'Mssql', auth_methods: Msf::Exploit::Remote::AuthOption::MSSQL_OPTIONS), ], Msf::Exploit::Remote::MSSQL) register_autofilter_ports([ 1433, 1434, 1435, 14330, 2533, 9152, 2638 ]) register_autofilter_services(%W{ ms-sql-s ms-sql2000 sybase }) end def set_session(client) print_status("Using existing session #{session.sid}") @mssql_client = client end # # This method sends a UDP query packet to the server and # parses out the reply packet into a hash # def mssql_ping(timeout=5) data = { } ping_sock = Rex::Socket::Udp.create( 'PeerHost' => rhost, 'PeerPort' => 1434, 'Context' => { 'Msf' => framework, 'MsfExploit' => self, }) ping_sock.put("\x02") resp, _saddr, _sport = ping_sock.recvfrom(65535, timeout) ping_sock.close return data if not resp return data if resp.length == 0 return mssql_ping_parse(resp) end # # Parse a 'ping' response and format as a hash # def mssql_ping_parse(data) res = [] var = nil idx = data.index('ServerName') return res if not idx sdata = data[idx, (data.length - 1)] instances = sdata.split(';;') instances.each do |instance| rinst = {} instance.split(';').each do |d| if (not var) var = d else if (var.length > 0) rinst[var] = d var = nil end end end res << rinst end return res end # # Execute a system command via xp_cmdshell # def mssql_parse_tds_reply(data, info) @mssql_client.mssql_parse_tds_reply(data, info) end def mssql_parse_reply(data, info) @mssql_client.mssql_parse_reply(data, info) end # # Parse a single row of a TDS reply # def mssql_parse_tds_row(data, info) @mssql_client.mssql_parse_tds_row(data, info) end # # Parse a "ret" TDS token # def mssql_parse_ret(data, info) @mssql_client.mssql_parse_ret(data, info) end # # Parse a "done" TDS token # def mssql_parse_done(data, info) @mssql_client.mssql_parse_done(data, info) end # # Parse an "error" TDS token # def mssql_parse_error(data, info) @mssql_client.mssql_parse_error(data, info) end # # Parse an "environment change" TDS token # def mssql_parse_env(data, info) @mssql_client.mssql_parse_env(data, info) end # # Parse an "information" TDS token # def mssql_parse_info(data, info) @mssql_client.mssql_parse_info(data, info) end def mssql_xpcmdshell(cmd, doprint=false, opts={}) @mssql_client.mssql_xpcmdshell(cmd, doprint, opts) end # # Upload and execute a Windows binary through MSSQL queries # def mssql_upload_exec(exe, debug=false) @mssql_client.mssql_upload_exec(exe, debug) end # # Upload and execute a Windows binary through MSSQL queries and Powershell # def powershell_upload_exec(exe, debug=false) @mssql_client.powershell_upload_exec(exe, debug) end # #this method send a prelogin packet and check if encryption is off # def mssql_prelogin(enc_error=false) @mssql_client.mssql_prelogin(enc_error) end # # This method connects to the server over TCP and attempts # to authenticate with the supplied username and password # The global socket is used and left connected after auth # def mssql_login(user='sa', pass='', db='', domain_name='') @mssql_client ||= Rex::Proto::MSSQL::Client.new(self, framework, datastore['RHOST'], datastore['RPORT']) result = @mssql_client.mssql_login(user, pass, db, domain_name) add_socket(@mssql_client.sock) if @mssql_client.sock && !sockets.include?(@mssql_client.sock) result end def mssql_login_datastore(db=nil) mssql_login(datastore['USERNAME'], datastore['PASSWORD'], db || datastore['DATABASE'] || '', datastore['MssqlDomain'] || '') end # # Issue a SQL query using the TDS protocol # def mssql_query(sqla, doprint=false, opts={}) @mssql_client.query(sqla, doprint, opts) end # # Nicely print the results of a SQL query # def mssql_print_reply(info) @mssql_client.mssql_print_reply(info) end def mssql_send_recv(req, timeout=15, check_status = true) @mssql_client.mssql_send_recv(req, timeout, check_status) end # # Encrypt a password according to the TDS protocol (encode) # def mssql_tds_encrypt(pass) # Convert to unicode, swap 4 bits both ways, xor with 0xa5 Rex::Text.to_unicode(pass).unpack('C*').map {|c| (((c & 0x0f) << 4) + ((c & 0xf0) >> 4)) ^ 0xa5 }.pack("C*") end end end