diff --git a/lib/metasploit/framework/login_scanner/base.rb b/lib/metasploit/framework/login_scanner/base.rb index 03af7e4b38..511260ffea 100644 --- a/lib/metasploit/framework/login_scanner/base.rb +++ b/lib/metasploit/framework/login_scanner/base.rb @@ -30,6 +30,9 @@ module Metasploit # @!attribute stop_on_success # @return [Boolean] Whether the scanner should stop when it has found one working Credential attr_accessor :stop_on_success + # @!attribute bruteforce_speed + # @return [Fixnum] The desired speed, with 5 being 'fast' and 0 being 'slow.' + attr_accessor :bruteforce_speed validates :connection_timeout, presence: true, @@ -53,6 +56,14 @@ module Metasploit validates :stop_on_success, inclusion: { in: [true, false] } + validates :bruteforce_speed, + presence: false, + numericality: { + only_integer: true, + greater_than_or_equal_to: 0, + less_than_or_equal_to: 5 + } + validate :host_address_must_be_valid validate :validate_cred_details @@ -86,6 +97,34 @@ module Metasploit false end + # @note Override this to set a timeout that makes more sense for + # your particular protocol. Telnet already usually takes a really + # long time, while MSSQL is often lickety-split quick. If + # overridden, the override should probably do something sensible + # with {#bruteforce_speed} + # + # @return [Fixnum] a number of seconds to sleep between attempts + def sleep_time + case bruteforce_speed + when 0; 60 * 5 + when 1; 15 + when 2; 1 + when 3; 0.5 + when 4; 0.1 + else; 0 + end + end + + # A threadsafe sleep method + # + # @param time [Fixnum] number of seconds (can be a Float), defaults + # to {#sleep_time} + # + # @return [void] + def sleep_between_attempts(time=self.sleep_time) + ::IO.select(nil,nil,nil,time) unless sleep_time.zero? + end + def each_credential cred_details.each do |raw_cred| @@ -148,6 +187,7 @@ module Metasploit total_error_count = 0 successful_users = Set.new + first_attempt = true each_credential do |credential| # Skip users for whom we've have already found a password @@ -161,6 +201,12 @@ module Metasploit next end + if first_attempt + first_attempt = false + else + sleep_between_attempts + end + result = attempt_login(credential) result.freeze diff --git a/lib/metasploit/framework/login_scanner/db2.rb b/lib/metasploit/framework/login_scanner/db2.rb index 93c7500805..31d9c7ec7f 100644 --- a/lib/metasploit/framework/login_scanner/db2.rb +++ b/lib/metasploit/framework/login_scanner/db2.rb @@ -106,7 +106,7 @@ module Metasploit self.max_send_size ||= 0 self.send_delay ||= 0 - self.ssl = false if self.ssl.nil? + self.ssl = false if self.ssl.nil? end # This method takes a response packet and checks to see diff --git a/modules/auxiliary/scanner/afp/afp_login.rb b/modules/auxiliary/scanner/afp/afp_login.rb index 0cc90634bb..e3e6204b89 100644 --- a/modules/auxiliary/scanner/afp/afp_login.rb +++ b/modules/auxiliary/scanner/afp/afp_login.rb @@ -63,6 +63,7 @@ class Metasploit3 < Msf::Auxiliary proxies: datastore['PROXIES'], cred_details: cred_collection, stop_on_success: datastore['STOP_ON_SUCCESS'], + bruteforce_speed: datastore['BRUTEFORCE_SPEED'], connection_timeout: 30, max_send_size: datastore['TCP::max_send_size'], send_delay: datastore['TCP::send_delay'], diff --git a/modules/auxiliary/scanner/db2/db2_auth.rb b/modules/auxiliary/scanner/db2/db2_auth.rb index 56a27fcea5..88bfa1950c 100644 --- a/modules/auxiliary/scanner/db2/db2_auth.rb +++ b/modules/auxiliary/scanner/db2/db2_auth.rb @@ -61,6 +61,7 @@ class Metasploit3 < Msf::Auxiliary proxies: datastore['PROXIES'], cred_details: cred_collection, stop_on_success: datastore['STOP_ON_SUCCESS'], + bruteforce_speed: datastore['BRUTEFORCE_SPEED'], connection_timeout: 30, max_send_size: datastore['TCP::max_send_size'], send_delay: datastore['TCP::send_delay'], diff --git a/modules/auxiliary/scanner/ftp/ftp_login.rb b/modules/auxiliary/scanner/ftp/ftp_login.rb index 04740c9f06..c3481d7fdd 100644 --- a/modules/auxiliary/scanner/ftp/ftp_login.rb +++ b/modules/auxiliary/scanner/ftp/ftp_login.rb @@ -75,6 +75,7 @@ class Metasploit3 < Msf::Auxiliary proxies: datastore['PROXIES'], cred_details: cred_collection, stop_on_success: datastore['STOP_ON_SUCCESS'], + bruteforce_speed: datastore['BRUTEFORCE_SPEED'], max_send_size: datastore['TCP::max_send_size'], send_delay: datastore['TCP::send_delay'], connection_timeout: 30 diff --git a/modules/auxiliary/scanner/http/appletv_login.rb b/modules/auxiliary/scanner/http/appletv_login.rb index 3919691ef0..0708abd0dc 100644 --- a/modules/auxiliary/scanner/http/appletv_login.rb +++ b/modules/auxiliary/scanner/http/appletv_login.rb @@ -5,6 +5,7 @@ require 'msf/core' require 'metasploit/framework/credential_collection' +require 'metasploit/framework/login_scanner/http' class Metasploit3 < Msf::Auxiliary @@ -80,6 +81,7 @@ class Metasploit3 < Msf::Auxiliary proxies: datastore["PROXIES"], cred_details: cred_collection, stop_on_success: datastore['STOP_ON_SUCCESS'], + bruteforce_speed: datastore['BRUTEFORCE_SPEED'], connection_timeout: 5, ) diff --git a/modules/auxiliary/scanner/http/axis_login.rb b/modules/auxiliary/scanner/http/axis_login.rb index 523dde423e..e12cc0d1a1 100644 --- a/modules/auxiliary/scanner/http/axis_login.rb +++ b/modules/auxiliary/scanner/http/axis_login.rb @@ -85,6 +85,7 @@ class Metasploit3 < Msf::Auxiliary proxies: proxies, cred_details: cred_collection, stop_on_success: datastore['STOP_ON_SUCCESS'], + bruteforce_speed: datastore['BRUTEFORCE_SPEED'], connection_timeout: 5, user_agent: datastore['UserAgent'], vhost: datastore['VHOST'] diff --git a/modules/auxiliary/scanner/http/buffalo_login.rb b/modules/auxiliary/scanner/http/buffalo_login.rb index 4b03fd0c4d..0b0b707596 100644 --- a/modules/auxiliary/scanner/http/buffalo_login.rb +++ b/modules/auxiliary/scanner/http/buffalo_login.rb @@ -49,6 +49,7 @@ class Metasploit3 < Msf::Auxiliary proxies: datastore['PROXIES'], cred_details: cred_collection, stop_on_success: datastore['STOP_ON_SUCCESS'], + bruteforce_speed: datastore['BRUTEFORCE_SPEED'], connection_timeout: 10, user_agent: datastore['UserAgent'], vhost: datastore['VHOST'] diff --git a/modules/auxiliary/scanner/http/glassfish_login.rb b/modules/auxiliary/scanner/http/glassfish_login.rb index 686ed7a31e..3cf30f4e18 100644 --- a/modules/auxiliary/scanner/http/glassfish_login.rb +++ b/modules/auxiliary/scanner/http/glassfish_login.rb @@ -100,6 +100,7 @@ class Metasploit3 < Msf::Auxiliary proxies: datastore["PROXIES"], cred_details: @cred_collection, stop_on_success: datastore['STOP_ON_SUCCESS'], + bruteforce_speed: datastore['BRUTEFORCE_SPEED'], connection_timeout: 5 ) diff --git a/modules/auxiliary/scanner/http/hp_sys_mgmt_login.rb b/modules/auxiliary/scanner/http/hp_sys_mgmt_login.rb index 6757477423..f412a6dce2 100644 --- a/modules/auxiliary/scanner/http/hp_sys_mgmt_login.rb +++ b/modules/auxiliary/scanner/http/hp_sys_mgmt_login.rb @@ -82,6 +82,7 @@ class Metasploit3 < Msf::Auxiliary proxies: datastore["PROXIES"], cred_details: @cred_collection, stop_on_success: datastore['STOP_ON_SUCCESS'], + bruteforce_speed: datastore['BRUTEFORCE_SPEED'], connection_timeout: 5 ) diff --git a/modules/auxiliary/scanner/http/http_login.rb b/modules/auxiliary/scanner/http/http_login.rb index eff3bb56a3..3cb7b2393b 100644 --- a/modules/auxiliary/scanner/http/http_login.rb +++ b/modules/auxiliary/scanner/http/http_login.rb @@ -160,6 +160,7 @@ class Metasploit3 < Msf::Auxiliary proxies: datastore["PROXIES"], cred_details: cred_collection, stop_on_success: datastore['STOP_ON_SUCCESS'], + bruteforce_speed: datastore['BRUTEFORCE_SPEED'], connection_timeout: 5, user_agent: datastore['UserAgent'], vhost: datastore['VHOST'] diff --git a/modules/auxiliary/scanner/http/ipboard_login.rb b/modules/auxiliary/scanner/http/ipboard_login.rb index 52f630c1dc..22b96e6a20 100644 --- a/modules/auxiliary/scanner/http/ipboard_login.rb +++ b/modules/auxiliary/scanner/http/ipboard_login.rb @@ -44,6 +44,7 @@ class Metasploit3 < Msf::Auxiliary proxies: datastore["PROXIES"], cred_details: cred_collection, stop_on_success: datastore['STOP_ON_SUCCESS'], + bruteforce_speed: datastore['BRUTEFORCE_SPEED'], connection_timeout: 5, user_agent: datastore['UserAgent'], vhost: datastore['VHOST'] diff --git a/modules/auxiliary/scanner/http/jenkins_login.rb b/modules/auxiliary/scanner/http/jenkins_login.rb index 1312c2d1b7..ebeb4109eb 100644 --- a/modules/auxiliary/scanner/http/jenkins_login.rb +++ b/modules/auxiliary/scanner/http/jenkins_login.rb @@ -48,6 +48,7 @@ class Metasploit3 < Msf::Auxiliary proxies: datastore['PROXIES'], cred_details: cred_collection, stop_on_success: datastore['STOP_ON_SUCCESS'], + bruteforce_speed: datastore['BRUTEFORCE_SPEED'], connection_timeout: 10, user_agent: datastore['UserAgent'], vhost: datastore['VHOST'] diff --git a/modules/auxiliary/scanner/http/mybook_live_login.rb b/modules/auxiliary/scanner/http/mybook_live_login.rb index 458f51dffb..636b39a89e 100644 --- a/modules/auxiliary/scanner/http/mybook_live_login.rb +++ b/modules/auxiliary/scanner/http/mybook_live_login.rb @@ -60,6 +60,7 @@ class Metasploit3 < Msf::Auxiliary proxies: datastore['PROXIES'], cred_details: cred_collection, stop_on_success: datastore['STOP_ON_SUCCESS'], + bruteforce_speed: datastore['BRUTEFORCE_SPEED'], connection_timeout: 10, user_agent: datastore['UserAgent'], vhost: datastore['VHOST'] diff --git a/modules/auxiliary/scanner/http/tomcat_mgr_login.rb b/modules/auxiliary/scanner/http/tomcat_mgr_login.rb index bea8a91f25..922fe9bc05 100644 --- a/modules/auxiliary/scanner/http/tomcat_mgr_login.rb +++ b/modules/auxiliary/scanner/http/tomcat_mgr_login.rb @@ -111,6 +111,7 @@ class Metasploit3 < Msf::Auxiliary proxies: datastore['PROXIES'], cred_details: cred_collection, stop_on_success: datastore['STOP_ON_SUCCESS'], + bruteforce_speed: datastore['BRUTEFORCE_SPEED'], connection_timeout: 10, user_agent: datastore['UserAgent'], vhost: datastore['VHOST'] diff --git a/modules/auxiliary/scanner/http/wordpress_xmlrpc_login.rb b/modules/auxiliary/scanner/http/wordpress_xmlrpc_login.rb index 99f3705e09..cc81b492bc 100644 --- a/modules/auxiliary/scanner/http/wordpress_xmlrpc_login.rb +++ b/modules/auxiliary/scanner/http/wordpress_xmlrpc_login.rb @@ -91,6 +91,7 @@ class Metasploit3 < Msf::Auxiliary proxies: datastore["PROXIES"], cred_details: cred_collection, stop_on_success: datastore['STOP_ON_SUCCESS'], + bruteforce_speed: datastore['BRUTEFORCE_SPEED'], connection_timeout: 5, ) diff --git a/modules/auxiliary/scanner/mssql/mssql_login.rb b/modules/auxiliary/scanner/mssql/mssql_login.rb index c8b67a72ee..4461f78e61 100644 --- a/modules/auxiliary/scanner/mssql/mssql_login.rb +++ b/modules/auxiliary/scanner/mssql/mssql_login.rb @@ -51,6 +51,7 @@ class Metasploit3 < Msf::Auxiliary proxies: datastore['PROXIES'], cred_details: cred_collection, stop_on_success: datastore['STOP_ON_SUCCESS'], + bruteforce_speed: datastore['BRUTEFORCE_SPEED'], connection_timeout: 30, max_send_size: datastore['TCP::max_send_size'], send_delay: datastore['TCP::send_delay'], diff --git a/modules/auxiliary/scanner/mysql/mysql_login.rb b/modules/auxiliary/scanner/mysql/mysql_login.rb index 8964a586d1..0314ea87d6 100644 --- a/modules/auxiliary/scanner/mysql/mysql_login.rb +++ b/modules/auxiliary/scanner/mysql/mysql_login.rb @@ -60,6 +60,7 @@ class Metasploit3 < Msf::Auxiliary proxies: datastore['PROXIES'], cred_details: cred_collection, stop_on_success: datastore['STOP_ON_SUCCESS'], + bruteforce_speed: datastore['BRUTEFORCE_SPEED'], connection_timeout: 30, max_send_size: datastore['TCP::max_send_size'], send_delay: datastore['TCP::send_delay'], diff --git a/modules/auxiliary/scanner/pop3/pop3_login.rb b/modules/auxiliary/scanner/pop3/pop3_login.rb index 173c1ab8b6..b19a89128c 100644 --- a/modules/auxiliary/scanner/pop3/pop3_login.rb +++ b/modules/auxiliary/scanner/pop3/pop3_login.rb @@ -71,6 +71,7 @@ class Metasploit3 < Msf::Auxiliary ssl: datastore['SSL'], cred_details: cred_collection, stop_on_success: datastore['STOP_ON_SUCCESS'], + bruteforce_speed: datastore['BRUTEFORCE_SPEED'], max_send_size: datastore['TCP::max_send_size'], send_delay: datastore['TCP::send_delay'], ) diff --git a/modules/auxiliary/scanner/postgres/postgres_login.rb b/modules/auxiliary/scanner/postgres/postgres_login.rb index 91c55da6cd..24e10c2915 100644 --- a/modules/auxiliary/scanner/postgres/postgres_login.rb +++ b/modules/auxiliary/scanner/postgres/postgres_login.rb @@ -69,6 +69,7 @@ class Metasploit3 < Msf::Auxiliary proxies: datastore['PROXIES'], cred_details: cred_collection, stop_on_success: datastore['STOP_ON_SUCCESS'], + bruteforce_speed: datastore['BRUTEFORCE_SPEED'], connection_timeout: 30 ) diff --git a/modules/auxiliary/scanner/smb/smb_login.rb b/modules/auxiliary/scanner/smb/smb_login.rb index c671ebf947..c4c380f354 100644 --- a/modules/auxiliary/scanner/smb/smb_login.rb +++ b/modules/auxiliary/scanner/smb/smb_login.rb @@ -73,6 +73,7 @@ class Metasploit3 < Msf::Auxiliary host: ip, port: rport, stop_on_success: datastore['STOP_ON_SUCCESS'], + bruteforce_speed: datastore['BRUTEFORCE_SPEED'], connection_timeout: 5, max_send_size: datastore['TCP::max_send_size'], send_delay: datastore['TCP::send_delay'], diff --git a/modules/auxiliary/scanner/snmp/snmp_login.rb b/modules/auxiliary/scanner/snmp/snmp_login.rb index 461fde351a..a9085437ed 100644 --- a/modules/auxiliary/scanner/snmp/snmp_login.rb +++ b/modules/auxiliary/scanner/snmp/snmp_login.rb @@ -60,6 +60,7 @@ class Metasploit3 < Msf::Auxiliary port: rport, cred_details: collection, stop_on_success: datastore['STOP_ON_SUCCESS'], + bruteforce_speed: datastore['BRUTEFORCE_SPEED'], connection_timeout: 2 ) diff --git a/modules/auxiliary/scanner/ssh/ssh_login.rb b/modules/auxiliary/scanner/ssh/ssh_login.rb index 51ed18c69e..b7ed878607 100644 --- a/modules/auxiliary/scanner/ssh/ssh_login.rb +++ b/modules/auxiliary/scanner/ssh/ssh_login.rb @@ -115,6 +115,7 @@ class Metasploit3 < Msf::Auxiliary cred_details: cred_collection, proxies: datastore['Proxies'], stop_on_success: datastore['STOP_ON_SUCCESS'], + bruteforce_speed: datastore['BRUTEFORCE_SPEED'], connection_timeout: datastore['SSH_TIMEOUT'], ) diff --git a/modules/auxiliary/scanner/ssh/ssh_login_pubkey.rb b/modules/auxiliary/scanner/ssh/ssh_login_pubkey.rb index 40a7b51688..872376b712 100644 --- a/modules/auxiliary/scanner/ssh/ssh_login_pubkey.rb +++ b/modules/auxiliary/scanner/ssh/ssh_login_pubkey.rb @@ -207,6 +207,7 @@ class Metasploit3 < Msf::Auxiliary port: rport, cred_details: keys, stop_on_success: datastore['STOP_ON_SUCCESS'], + bruteforce_speed: datastore['BRUTEFORCE_SPEED'], proxies: datastore['Proxies'], connection_timeout: datastore['SSH_TIMEOUT'], ) diff --git a/modules/auxiliary/scanner/telnet/telnet_login.rb b/modules/auxiliary/scanner/telnet/telnet_login.rb index 7f14dc0ee4..4f45f2c86b 100644 --- a/modules/auxiliary/scanner/telnet/telnet_login.rb +++ b/modules/auxiliary/scanner/telnet/telnet_login.rb @@ -64,6 +64,7 @@ class Metasploit3 < Msf::Auxiliary proxies: datastore['PROXIES'], cred_details: cred_collection, stop_on_success: datastore['STOP_ON_SUCCESS'], + bruteforce_speed: datastore['BRUTEFORCE_SPEED'], connection_timeout: datastore['Timeout'], max_send_size: datastore['TCP::max_send_size'], send_delay: datastore['TCP::send_delay'], diff --git a/modules/auxiliary/scanner/vmware/vmauthd_login.rb b/modules/auxiliary/scanner/vmware/vmauthd_login.rb index 418e0b808c..a7096d4fc6 100644 --- a/modules/auxiliary/scanner/vmware/vmauthd_login.rb +++ b/modules/auxiliary/scanner/vmware/vmauthd_login.rb @@ -72,6 +72,7 @@ class Metasploit3 < Msf::Auxiliary proxies: datastore['PROXIES'], cred_details: cred_collection, stop_on_success: datastore['STOP_ON_SUCCESS'], + bruteforce_speed: datastore['BRUTEFORCE_SPEED'], connection_timeout: 30, max_send_size: datastore['TCP::max_send_size'], send_delay: datastore['TCP::send_delay'], diff --git a/modules/auxiliary/scanner/vnc/vnc_login.rb b/modules/auxiliary/scanner/vnc/vnc_login.rb index a9c780c19d..312e02a4b2 100644 --- a/modules/auxiliary/scanner/vnc/vnc_login.rb +++ b/modules/auxiliary/scanner/vnc/vnc_login.rb @@ -77,6 +77,7 @@ class Metasploit3 < Msf::Auxiliary proxies: datastore['PROXIES'], cred_details: cred_collection, stop_on_success: datastore['STOP_ON_SUCCESS'], + bruteforce_speed: datastore['BRUTEFORCE_SPEED'], connection_timeout: datastore['ConnectTimeout'], max_send_size: datastore['TCP::max_send_size'], send_delay: datastore['TCP::send_delay'], diff --git a/modules/auxiliary/scanner/winrm/winrm_login.rb b/modules/auxiliary/scanner/winrm/winrm_login.rb index f3fe0d349e..da418946e6 100644 --- a/modules/auxiliary/scanner/winrm/winrm_login.rb +++ b/modules/auxiliary/scanner/winrm/winrm_login.rb @@ -59,6 +59,7 @@ class Metasploit3 < Msf::Auxiliary proxies: datastore["PROXIES"], cred_details: cred_collection, stop_on_success: datastore['STOP_ON_SUCCESS'], + bruteforce_speed: datastore['BRUTEFORCE_SPEED'], connection_timeout: 10, ) diff --git a/spec/lib/metasploit/framework/login_scanner/base_spec.rb b/spec/lib/metasploit/framework/login_scanner/base_spec.rb new file mode 100644 index 0000000000..522024f9e1 --- /dev/null +++ b/spec/lib/metasploit/framework/login_scanner/base_spec.rb @@ -0,0 +1,85 @@ +require 'spec_helper' +require 'metasploit/framework/login_scanner/base' + +describe Metasploit::Framework::LoginScanner::Base do + + let(:base_class) { + Class.new do + include Metasploit::Framework::LoginScanner::Base + def self.model_name + ActiveModel::Name.new(self, nil, 'base') + end + end + } + + subject(:login_scanner) { base_class.new } + + it { should respond_to :bruteforce_speed } + + context 'validations' do + context 'bruteforce_speed' do + it 'is not valid for a non-number' do + login_scanner.bruteforce_speed = "a" + expect(login_scanner).to_not be_valid + expect(login_scanner.errors[:bruteforce_speed]).to include "is not a number" + end + + it 'is not valid for a float' do + login_scanner.bruteforce_speed = "3.14" + expect(login_scanner).to_not be_valid + expect(login_scanner.errors[:bruteforce_speed]).to include "must be an integer" + end + + it 'is not negative' do + login_scanner.bruteforce_speed = "-1" + expect(login_scanner).to_not be_valid + expect(login_scanner.errors[:bruteforce_speed]).to include "must be greater than or equal to 0" + end + + it 'is not greater than five' do + login_scanner.bruteforce_speed = "6" + expect(login_scanner).to_not be_valid + expect(login_scanner.errors[:bruteforce_speed]).to include "must be less than or equal to 5" + end + end + + it { should respond_to :sleep_time } + + context '#sleep_time' do + + context 'default' do + subject(:sleep_time) { base_class.new.sleep_time } + it 'defaults to zero' do + expect(sleep_time).to eq(0) + end + end + + context 'set' do + subject(:sleep_time) { + klass = base_class.new + klass.bruteforce_speed = 0 + klass.sleep_time + } + it 'is five minutes when bruteforce_speed is set to 0' do + expect(sleep_time).to eq(60 * 5) + end + end + end + + it { should respond_to :sleep_between_attempts } + + context '#sleep_between_attempts' + context 'default' do + subject(:sleep_between_attempts) { base_class.new.sleep_between_attempts } + it 'returns nothing' do + expect(sleep_between_attempts).to be_nil + end + end + + context 'actually sleep a little' do + # I don't want to slow down the test, and I don't really know how + # to test a time interval anyway since rspec disables sleep. :( + end + end + +end diff --git a/spec/lib/metasploit/framework/login_scanner/telnet_spec.rb b/spec/lib/metasploit/framework/login_scanner/telnet_spec.rb index efbae0ff92..90f5d6924b 100644 --- a/spec/lib/metasploit/framework/login_scanner/telnet_spec.rb +++ b/spec/lib/metasploit/framework/login_scanner/telnet_spec.rb @@ -38,7 +38,7 @@ describe Metasploit::Framework::LoginScanner::Telnet do expect(login_scanner.errors[:banner_timeout]).to include "must be greater than or equal to 1" end - it 'is valid for a legitimate number' do + it 'is valid for a legitimate number' do login_scanner.port = rand(1000) + 1 expect(login_scanner.errors[:banner_timeout]).to be_empty end @@ -69,7 +69,7 @@ describe Metasploit::Framework::LoginScanner::Telnet do expect(login_scanner.errors[:telnet_timeout]).to include "must be greater than or equal to 1" end - it 'is valid for a legitimate number' do + it 'is valid for a legitimate number' do login_scanner.port = rand(1000) + 1 expect(login_scanner.errors[:telnet_timeout]).to be_empty end