2012-01-27 18:35:39 +00:00
|
|
|
##
|
|
|
|
# $Id$
|
|
|
|
##
|
|
|
|
|
|
|
|
##
|
|
|
|
# This file is part of the Metasploit Framework and may be subject to
|
|
|
|
# redistribution and commercial restrictions. Please see the Metasploit
|
2012-02-21 01:40:50 +00:00
|
|
|
# web site for more information on licensing and terms of use.
|
|
|
|
# http://metasploit.com/
|
2012-01-27 18:35:39 +00:00
|
|
|
##
|
|
|
|
|
|
|
|
|
|
|
|
require 'msf/core'
|
|
|
|
|
|
|
|
|
|
|
|
class Metasploit3 < Msf::Auxiliary
|
|
|
|
|
|
|
|
include Msf::Auxiliary::Report
|
|
|
|
include Msf::Auxiliary::Scanner
|
|
|
|
|
|
|
|
def initialize
|
|
|
|
super(
|
|
|
|
'Name' => 'pcAnywhere UDP Service Discovery',
|
|
|
|
'Version' => '$Revision$',
|
|
|
|
'Description' => 'Discover active pcAnywhere services through UDP',
|
|
|
|
'Author' => 'hdm',
|
|
|
|
'License' => MSF_LICENSE,
|
2012-02-01 16:59:58 +00:00
|
|
|
'References' =>
|
2012-01-27 18:35:39 +00:00
|
|
|
[
|
2012-02-01 16:59:58 +00:00
|
|
|
['URL', 'http://www.unixwiz.net/tools/pcascan.txt']
|
2012-01-27 18:35:39 +00:00
|
|
|
]
|
|
|
|
)
|
|
|
|
|
|
|
|
register_options(
|
|
|
|
[
|
|
|
|
Opt::CHOST,
|
|
|
|
OptInt.new('BATCHSIZE', [true, 'The number of hosts to probe in each set', 256]),
|
|
|
|
Opt::RPORT(5632)
|
|
|
|
], self.class)
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
# Define our batch size
|
|
|
|
def run_batch_size
|
|
|
|
datastore['BATCHSIZE'].to_i
|
|
|
|
end
|
|
|
|
|
|
|
|
def rport
|
|
|
|
datastore['RPORT'].to_i
|
|
|
|
end
|
|
|
|
|
|
|
|
# Fingerprint a single host
|
|
|
|
def run_batch(batch)
|
|
|
|
|
|
|
|
print_status("Sending pcAnywhere discovery requests to #{batch[0]}->#{batch[-1]} (#{batch.length} hosts)")
|
|
|
|
|
|
|
|
@results = {}
|
|
|
|
begin
|
|
|
|
udp_sock = nil
|
|
|
|
idx = 0
|
|
|
|
|
|
|
|
# Create an unbound UDP socket if no CHOST is specified, otherwise
|
|
|
|
# create a UDP socket bound to CHOST (in order to avail of pivoting)
|
|
|
|
udp_sock = Rex::Socket::Udp.create( { 'LocalHost' => datastore['CHOST'] || nil, 'Context' => {'Msf' => framework, 'MsfExploit' => self} })
|
|
|
|
add_socket(udp_sock)
|
|
|
|
|
|
|
|
batch.each do |ip|
|
|
|
|
begin
|
|
|
|
# Send network query
|
|
|
|
udp_sock.sendto("NQ", ip, rport, 0)
|
2012-02-01 16:59:58 +00:00
|
|
|
|
2012-01-27 18:35:39 +00:00
|
|
|
# Send status query
|
2012-02-01 16:59:58 +00:00
|
|
|
udp_sock.sendto("ST", ip, rport, 0)
|
2012-01-27 18:35:39 +00:00
|
|
|
rescue ::Interrupt
|
|
|
|
raise $!
|
|
|
|
rescue ::Rex::HostUnreachable, ::Rex::ConnectionTimeout, ::Rex::ConnectionRefused
|
|
|
|
nil
|
|
|
|
end
|
|
|
|
|
|
|
|
if (idx % 30 == 0)
|
|
|
|
while (r = udp_sock.recvfrom(65535, 0.1) and r[1])
|
|
|
|
parse_reply(r)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
idx += 1
|
|
|
|
end
|
|
|
|
|
|
|
|
while (r = udp_sock.recvfrom(65535, 3) and r[1])
|
|
|
|
parse_reply(r)
|
|
|
|
end
|
|
|
|
|
|
|
|
rescue ::Interrupt
|
|
|
|
raise $!
|
|
|
|
rescue ::Errno::ENOBUFS
|
|
|
|
print_status("Socket buffers are full, waiting for them to flush...")
|
|
|
|
while (r = udp_sock.recvfrom(65535, 0.1) and r[1])
|
|
|
|
parse_reply(r)
|
|
|
|
end
|
|
|
|
select(nil, nil, nil, 0.25)
|
|
|
|
rescue ::Exception => e
|
|
|
|
print_error("Unknown error: #{e.class} #{e} #{e.backtrace}")
|
|
|
|
end
|
|
|
|
|
|
|
|
@results.keys.each do |ip|
|
|
|
|
next unless inside_workspace_boundary?(ip)
|
|
|
|
data = @results[ip]
|
2012-02-01 16:59:58 +00:00
|
|
|
|
2012-01-27 18:35:39 +00:00
|
|
|
info = ""
|
2012-02-01 16:59:58 +00:00
|
|
|
|
2012-01-27 18:35:39 +00:00
|
|
|
if data[:name]
|
|
|
|
info << "Name: #{data[:name]} "
|
|
|
|
end
|
2012-02-01 16:59:58 +00:00
|
|
|
|
2012-01-28 19:05:05 +00:00
|
|
|
if data[:stat]
|
|
|
|
info << "- #{data[:stat]} "
|
2012-01-27 18:35:39 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
if data[:caps]
|
2012-01-28 19:05:05 +00:00
|
|
|
info << "( #{data[:caps]} ) "
|
2012-02-01 16:59:58 +00:00
|
|
|
end
|
2012-01-27 18:35:39 +00:00
|
|
|
|
2012-01-28 19:05:05 +00:00
|
|
|
report_service(:host => ip, :port => rport, :proto => 'udp', :name => "pcanywhere", :info => info)
|
|
|
|
report_note(:host => ip, :port => rport, :proto => 'udp', :name => "pcanywhere", :update => :unique, :ntype => "pcanywhere.status", :data => data )
|
2012-01-27 18:35:39 +00:00
|
|
|
print_status("#{ip}:#{rport} #{info}")
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def parse_reply(pkt)
|
|
|
|
# Ignore "empty" packets
|
|
|
|
return if not pkt[1]
|
|
|
|
|
|
|
|
addr = pkt[1]
|
|
|
|
if(addr =~ /^::ffff:/)
|
|
|
|
addr = addr.sub(/^::ffff:/, '')
|
|
|
|
end
|
|
|
|
|
|
|
|
data = pkt[0]
|
2012-02-01 16:59:58 +00:00
|
|
|
|
2012-01-27 18:35:39 +00:00
|
|
|
case data
|
|
|
|
when /^NR(........................)(........)/
|
2012-02-01 16:59:58 +00:00
|
|
|
|
2012-01-27 18:35:39 +00:00
|
|
|
name = $1.dup
|
|
|
|
caps = $2.dup
|
2012-02-01 16:59:58 +00:00
|
|
|
|
2012-01-29 05:33:26 +00:00
|
|
|
name = name.gsub(/_+$/, '').gsub("\x00", '').strip
|
2012-02-01 16:59:58 +00:00
|
|
|
caps = caps.gsub(/_+$/, '').gsub("\x00", '').strip
|
|
|
|
|
2012-01-27 18:35:39 +00:00
|
|
|
@results[addr] ||= {}
|
|
|
|
@results[addr][:name] = name
|
|
|
|
@results[addr][:caps] = caps
|
2012-02-01 16:59:58 +00:00
|
|
|
|
2012-01-27 18:35:39 +00:00
|
|
|
when /^ST(.+)/
|
|
|
|
@results[addr] ||= {}
|
|
|
|
buff = $1.dup
|
|
|
|
stat = 'Unknown'
|
2012-02-01 16:59:58 +00:00
|
|
|
|
2012-01-27 18:35:39 +00:00
|
|
|
if buff[2,1].unpack("C")[0] == 67
|
|
|
|
stat = "Available"
|
|
|
|
end
|
2012-02-01 16:59:58 +00:00
|
|
|
|
2012-01-27 18:35:39 +00:00
|
|
|
if buff[2,1].unpack("C")[0] == 11
|
|
|
|
stat = "Busy"
|
|
|
|
end
|
2012-02-01 16:59:58 +00:00
|
|
|
|
2012-01-27 18:35:39 +00:00
|
|
|
@results[addr][:stat] = stat
|
|
|
|
else
|
|
|
|
print_error("#{addr} Unknown: #{data.inspect}")
|
|
|
|
end
|
2012-02-01 16:59:58 +00:00
|
|
|
|
2012-01-27 18:35:39 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
end
|