First commit of a udp_mixin and modified scanners

This commit is contained in:
HD Moore 2012-11-04 01:13:38 -05:00
parent d4fc99e40c
commit 910a91a0f6
5 changed files with 176 additions and 486 deletions

View File

@ -11,6 +11,7 @@ require 'msf/core/auxiliary/dos'
require 'msf/core/auxiliary/fuzzer'
require 'msf/core/auxiliary/report'
require 'msf/core/auxiliary/scanner'
require 'msf/core/auxiliary/udp_scanner'
require 'msf/core/auxiliary/timed'
require 'msf/core/auxiliary/wmapmodule'
require 'msf/core/auxiliary/crawler'

View File

@ -16,23 +16,17 @@ require 'openssl'
class Metasploit3 < Msf::Auxiliary
include Msf::Auxiliary::Report
include Msf::Auxiliary::Scanner
include Msf::Auxiliary::UDPScanner
def initialize
super(
'Name' => 'UDP Service Sweeper',
'Version' => '$Revision$',
'Description' => 'Detect common UDP services',
'Description' => 'Detect interesting UDP services',
'Author' => 'hdm',
'License' => MSF_LICENSE
)
register_options(
[
Opt::CHOST,
OptInt.new('BATCHSIZE', [true, 'The number of hosts to probe in each set', 256]),
], self.class)
register_advanced_options(
[
OptBool.new('RANDOMIZE_PORTS', [false, 'Randomize the order the ports are probed', true])
@ -54,7 +48,6 @@ class Metasploit3 < Msf::Auxiliary
@probes << 'probe_pkt_citrix'
@probes << 'probe_pkt_pca_st'
@probes << 'probe_pkt_pca_nq'
end
def setup
@ -65,87 +58,23 @@ class Metasploit3 < Msf::Auxiliary
end
end
# Define our batch size
def run_batch_size
datastore['BATCHSIZE'].to_i
def scanner_prescan(batch)
print_status("Sending #{@probes.length} probes to #{batch[0]}->#{batch[-1]} (#{batch.length} hosts)")
@results = {}
end
# Fingerprint a single host
def run_batch(batch)
@results = {}
print_status("Sending #{@probes.length} probes to #{batch[0]}->#{batch[-1]} (#{batch.length} hosts)")
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)
# Send each probe to each host
@probes.each do |probe|
batch.each do |ip|
begin
data, port = self.send(probe, ip)
udp_sock.sendto(data, ip, port, 0)
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])
reply_addr = r[1].split(':').last
parse_reply(r) if batch.include? reply_addr
end
end
idx += 1
end
end
cnt = 0
del = 10
sts = Time.now.to_i
while (r = udp_sock.recvfrom(65535, del) and r[1])
reply_addr = r[1].split(':').last
parse_reply(r) if batch.include? reply_addr
# Prevent an indefinite loop if the targets keep replying
cnt += 1
break if cnt > run_batch_size
# Escape after 15 seconds regardless of batch size
break if ((sts + 15) < Time.now.to_i)
del = 1.0
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])
reply_addr = r[1].split(':').last
parse_reply(r) if batch.include? reply_addr
end
select(nil, nil, nil, 0.25)
retry
rescue ::Exception => e
print_error("Unknown error: #{e.class} #{e}")
def scan_host(ip)
@probes.each do |probe|
data, port = self.send(probe, ip)
scanner_send(data, ip, port)
end
end
def scanner_postscan(batch)
@results.each_key do |k|
next if not @results[k].respond_to?('keys')
data = @results[k]
next unless inside_workspace_boundary?(data[:host])
conf = {
:host => data[:host],
:port => data[:port],
@ -165,43 +94,29 @@ class Metasploit3 < Msf::Auxiliary
report_service(conf)
print_status("Discovered #{data[:app]} on #{k} (#{data[:info]})")
end
end
#
# The response parsers
#
def parse_reply(pkt)
def scanner_process(data, shost, sport)
# Ignore "empty" packets
return if not pkt[1]
if(pkt[1] =~ /^::ffff:/)
pkt[1] = pkt[1].sub(/^::ffff:/, '')
end
# Ignore duplicates
hkey = "#{pkt[1]}:#{pkt[2]}"
app = 'unknown'
inf = ''
hkey = "#{shost}:#{sport}"
app = 'unknown'
inf = ''
maddr = nil
hname = nil
# Work with protocols that return different data in different packets
# These are reported at the end of the scanning loop to build state
case pkt[2]
case sport
when 5632
@results[hkey] ||= {}
data = @results[hkey]
data[:app] = "pcAnywhere_stat"
data[:port] = pkt[2]
data[:host] = pkt[1]
data[:port] = sport
data[:host] = shost
case pkt[0]
case data
when /^NR(........................)(........)/
name = $1.dup
@ -243,20 +158,20 @@ class Metasploit3 < Msf::Auxiliary
# Ignore duplicates
return if @results[hkey]
case pkt[2]
case sport
when 53
app = 'DNS'
ver = nil
if (not ver and pkt[0] =~ /([6789]\.[\w\.\-_\:\(\)\[\]\/\=\+\|\{\}]+)/i)
if (not ver and data =~ /([6789]\.[\w\.\-_\:\(\)\[\]\/\=\+\|\{\}]+)/i)
ver = 'BIND ' + $1
end
ver = 'Microsoft DNS' if (not ver and pkt[0][2,4] == "\x81\x04\x00\x01")
ver = 'TinyDNS' if (not ver and pkt[0][2,4] == "\x81\x81\x00\x01")
ver = 'Microsoft DNS' if (not ver and data[2,4] == "\x81\x04\x00\x01")
ver = 'TinyDNS' if (not ver and data[2,4] == "\x81\x81\x00\x01")
ver = pkt[0].unpack('H*')[0] if not ver
ver = data.unpack('H*')[0] if not ver
inf = ver if ver
@results[hkey] = true
@ -264,30 +179,30 @@ class Metasploit3 < Msf::Auxiliary
when 137
app = 'NetBIOS'
data = pkt[0]
buff = data.dup
head = data.slice!(0,12)
head = buff.slice!(0,12)
xid, flags, quests, answers, auths, adds = head.unpack('n6')
return if quests != 0
return if answers == 0
qname = data.slice!(0,34)
rtype,rclass,rttl,rlen = data.slice!(0,10).unpack('nnNn')
buff = data.slice!(0,rlen)
qname = buff.slice!(0,34)
rtype,rclass,rttl,rlen = buff.slice!(0,10).unpack('nnNn')
bits = buff.slice!(0,rlen)
names = []
case rtype
when 0x21
rcnt = buff.slice!(0,1).unpack("C")[0]
rcnt = bits.slice!(0,1).unpack("C")[0]
1.upto(rcnt) do
tname = buff.slice!(0,15).gsub(/\x00.*/, '').strip
ttype = buff.slice!(0,1).unpack("C")[0]
tflag = buff.slice!(0,2).unpack('n')[0]
tname = bits.slice!(0,15).gsub(/\x00.*/, '').strip
ttype = bits.slice!(0,1).unpack("C")[0]
tflag = bits.slice!(0,2).unpack('n')[0]
names << [ tname, ttype, tflag ]
end
maddr = buff.slice!(0,6).unpack("C*").map{|c| "%.2x" % c }.join(":")
maddr = bits.slice!(0,6).unpack("C*").map{|c| "%.2x" % c }.join(":")
names.each do |name|
inf << name[0]
@ -309,7 +224,7 @@ class Metasploit3 < Msf::Auxiliary
when 111
app = 'Portmap'
buf = pkt[0]
buf = data
inf = ""
hed = buf.slice!(0,24)
svc = []
@ -317,7 +232,7 @@ class Metasploit3 < Msf::Auxiliary
rec = buf.slice!(0,20).unpack("N5")
svc << "#{rec[1]} v#{rec[2]} #{rec[3] == 0x06 ? "TCP" : "UDP"}(#{rec[4]})"
report_service(
:host => pkt[1],
:host => shost,
:port => rec[4],
:proto => (rec[3] == 0x06 ? "tcp" : "udp"),
:name => "sunrpc",
@ -332,7 +247,7 @@ class Metasploit3 < Msf::Auxiliary
when 123
app = 'NTP'
ver = nil
ver = pkt[0].unpack('H*')[0]
ver = data.unpack('H*')[0]
ver = 'NTP v3' if (ver =~ /^1c06|^1c05/)
ver = 'NTP v4' if (ver =~ /^240304/)
ver = 'NTP v4 (unsynchronized)' if (ver =~ /^e40/)
@ -343,7 +258,7 @@ class Metasploit3 < Msf::Auxiliary
when 1434
app = 'MSSQL'
mssql_ping_parse(pkt[0]).each_pair { |k,v|
mssql_ping_parse(data).each_pair { |k,v|
inf += k+'='+v+' '
}
@ -351,7 +266,7 @@ class Metasploit3 < Msf::Auxiliary
when 161
app = 'SNMP'
asn = OpenSSL::ASN1.decode(pkt[0]) rescue nil
asn = OpenSSL::ASN1.decode(data) rescue nil
return if not asn
snmp_error = asn.value[0].value rescue nil
@ -374,29 +289,28 @@ class Metasploit3 < Msf::Auxiliary
when 523
app = 'ibm-db2'
inf = db2disco_parse(pkt[0])
inf = db2disco_parse(data)
@results[hkey] = true
when 1604
app = 'citrix-ica'
return unless citrix_parse(pkt[0])
return unless citrix_parse(data)
@results[hkey] = true
end
return unless inside_workspace_boundary?(pkt[1])
report_service(
:host => pkt[1],
:host => shost,
:mac => (maddr and maddr != '00:00:00:00:00:00') ? maddr : nil,
:host_name => (hname) ? hname.downcase : nil,
:port => pkt[2],
:port => sport,
:proto => 'udp',
:name => app,
:info => inf,
:state => "open"
)
print_status("Discovered #{app} on #{pkt[1]}:#{pkt[2]} (#{inf})")
print_status("Discovered #{app} on #{shost}:#{sport} (#{inf})")
end
#

View File

@ -16,7 +16,7 @@ require 'msf/core'
class Metasploit3 < Msf::Auxiliary
include Msf::Auxiliary::Report
include Msf::Auxiliary::Scanner
include Msf::Auxiliary::UDPScanner
def initialize
super(
@ -29,124 +29,58 @@ class Metasploit3 < Msf::Auxiliary
register_options(
[
Opt::CHOST,
OptInt.new('BATCHSIZE', [true, 'The number of hosts to probe in each set', 256]),
Opt::RPORT(137)
], 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 NetBIOS status requests to #{batch[0]}->#{batch[-1]} (#{batch.length} hosts)")
def scanner_prescan(batch)
print_status("Sending NetBIOS requests to #{batch[0]}->#{batch[-1]} (#{batch.length} hosts)")
@results = {}
begin
udp_sock = nil
idx = 0
end
# 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)
def scan_host(ip)
scanner_send(create_netbios_status(ip), ip, datastore['RPORT'])
end
batch.each do |ip|
begin
data = create_netbios_status(ip)
udp_sock.sendto(data, ip, rport, 0)
rescue ::Interrupt
raise $!
rescue ::Rex::HostUnreachable, ::Rex::ConnectionTimeout, ::Rex::ConnectionRefused
nil
end
def scanner_postscan(batch)
if (idx % 30 == 0)
while (r = udp_sock.recvfrom(65535, 0.1) and r[1])
parse_reply(r)
end
end
cnt = 0
idx += 1
end
while (r = udp_sock.recvfrom(65535, 3) and r[1])
parse_reply(r)
end
# Second pass to find additional IPs per host name
@results.keys.each do |ip|
next if not @results[ip][:name]
begin
data = create_netbios_lookup(@results[ip][:name])
udp_sock.sendto(data, ip, rport, 0)
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}")
# Perform a second pass based on responsive hosts
@results.keys.each do |ip|
next if not @results[ip][:name]
scanner_send(create_netbios_lookup(@results[ip][:name]), ip, datastore['RPORT'])
cnt += 1
end
# Wait for the final replies to trickle in
scanner_recv(10) if cnt > 0
@results.keys.each do |ip|
next unless inside_workspace_boundary?(ip)
host = @results[ip]
user = ""
os = "Windows"
if(host[:user] and host[:mac] != "00:00:00:00:00:00")
if (host[:user] and host[:mac] != "00:00:00:00:00:00")
user = " User:#{host[:user]}"
end
if(host[:mac] == "00:00:00:00:00:00")
if (host[:mac] == "00:00:00:00:00:00")
os = "Unix"
end
names = ""
if(host[:names])
if (host[:names])
names = " Names:(" + host[:names].map{|n| n[0]}.uniq.join(", ") + ")"
end
addrs = ""
if(host[:addrs])
if (host[:addrs])
addrs = "Addresses:(" + host[:addrs].map{|n| n[0]}.uniq.join(", ") + ")"
end
if(host[:mac] != "00:00:00:00:00:00")
if (host[:mac] != "00:00:00:00:00:00")
report_host(:host => ip, :mac => host[:mac])
else
report_host(:host => ip)
@ -174,7 +108,7 @@ class Metasploit3 < Msf::Auxiliary
virtual = 'Virtual Computer Inc'
end
if(virtual)
if (virtual)
extra = "Virtual Machine:#{virtual}"
report_note(
:host => ip,
@ -183,7 +117,7 @@ class Metasploit3 < Msf::Auxiliary
)
end
if(host[:addrs])
if (host[:addrs])
aliases = []
host[:addrs].map{|n| n[0]}.uniq.each do |addr|
next if addr == ip
@ -206,16 +140,7 @@ class Metasploit3 < Msf::Auxiliary
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]
def scanner_process(data, shost, sport)
head = data.slice!(0,12)
@ -233,7 +158,7 @@ class Metasploit3 < Msf::Auxiliary
hname = nil
uname = nil
@results[addr] ||= {}
@results[shost] ||= {}
case rtype
when 0x21
@ -248,15 +173,15 @@ class Metasploit3 < Msf::Auxiliary
end
maddr = buff.slice!(0,6).unpack("C*").map{|c| "%.2x" % c }.join(":")
@results[addr][:names] = names
@results[addr][:mac] = maddr
@results[shost][:names] = names
@results[shost][:mac] = maddr
if (!hname and @results[addr][:names].length > 0)
@results[addr][:name] = @results[addr][:names][0][0]
if (!hname and @results[shost][:names].length > 0)
@results[shost][:name] = @results[shost][:names][0][0]
end
@results[addr][:name] = hname if hname
@results[addr][:user] = uname if uname
@results[shost][:name] = hname if hname
@results[shost][:user] = uname if uname
inf = ''
names.each do |name|
@ -269,24 +194,24 @@ class Metasploit3 < Msf::Auxiliary
end
end
inf << maddr
if inside_workspace_boundary?(addr)
report_service(
:host => addr,
:mac => (maddr and maddr != '00:00:00:00:00:00') ? maddr : nil,
:host_name => (hname) ? hname.downcase : nil,
:port => pkt[2],
:proto => 'udp',
:name => 'netbios',
:info => inf
)
end
report_service(
:host => shost,
:mac => (maddr and maddr != '00:00:00:00:00:00') ? maddr : nil,
:host_name => (hname) ? hname.downcase : nil,
:port => datastore['RPORT'],
:proto => 'udp',
:name => 'netbios',
:info => inf
)
when 0x20
1.upto(rlen / 6.0) do
tflag = buff.slice!(0,2).unpack('n')[0]
taddr = buff.slice!(0,4).unpack("C*").join(".")
names << [ taddr, tflag ]
end
@results[addr][:addrs] = names
@results[shost][:addrs] = names
end
end

View File

@ -16,7 +16,7 @@ require 'msf/core'
class Metasploit3 < Msf::Auxiliary
include Msf::Auxiliary::Report
include Msf::Auxiliary::Scanner
include Msf::Auxiliary::UDPScanner
def initialize
super(
@ -33,79 +33,23 @@ class Metasploit3 < Msf::Auxiliary
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)
def scanner_prescan(batch)
print_status("Sending pcAnywhere discovery requests to #{batch[0]}->#{batch[-1]} (#{batch.length} hosts)")
@results = {}
begin
udp_sock = nil
idx = 0
end
# 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)
# Send status query
udp_sock.sendto("ST", ip, rport, 0)
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
def scan_host(ip)
scanner_send("NQ", ip, datastore['RPORT'])
scanner_send("ST", ip, datastore['RPORT'])
end
def scanner_postscan(batch)
@results.keys.each do |ip|
next unless inside_workspace_boundary?(ip)
data = @results[ip]
info = ""
if data[:name]
@ -126,17 +70,7 @@ class Metasploit3 < Msf::Auxiliary
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]
def scanner_process(data, shost, sport)
case data
when /^NR(........................)(........)/
@ -146,12 +80,12 @@ class Metasploit3 < Msf::Auxiliary
name = name.gsub(/_+$/, '').gsub("\x00", '').strip
caps = caps.gsub(/_+$/, '').gsub("\x00", '').strip
@results[addr] ||= {}
@results[addr][:name] = name
@results[addr][:caps] = caps
@results[shost] ||= {}
@results[shost][:name] = name
@results[shost][:caps] = caps
when /^ST(.+)/
@results[addr] ||= {}
@results[shost] ||= {}
buff = $1.dup
stat = 'Unknown'
@ -163,9 +97,9 @@ class Metasploit3 < Msf::Auxiliary
stat = "Busy"
end
@results[addr][:stat] = stat
@results[shost][:stat] = stat
else
print_error("#{addr} Unknown: #{data.inspect}")
print_error("#{shost} Unknown: #{data.inspect}")
end
end

View File

@ -14,180 +14,96 @@ require 'msf/core'
class Metasploit3 < Msf::Auxiliary
include Msf::Auxiliary::Report
include Msf::Auxiliary::UDPScanner
def initialize
super(
'Name' => 'SSDP M-SEARCH Gateway Information Discovery',
'Name' => 'UPnP SSDP M-SEARCH Information Discovery',
'Version' => '$Revision$',
'Description' => 'Discover information about the local gateway via UPnP',
'Description' => 'Discover information from UPnP-enabled systems',
'Author' => 'todb',
'License' => MSF_LICENSE
)
register_options(
[
Opt::CHOST,
Opt::RPORT(1900),
Opt::RHOST("239.255.255.250"), # Generally don't change this.
OptPort.new('SRVPORT', [ false, "The source port to listen for replies.", 0]),
], self.class
)
@result = []
register_options( [
Opt::RPORT(1900),
OptBool.new('REPORT_LOCATION', [true, 'This determines whether to report the UPnP endpoint service advertised by SSDP', false ])
], self.class)
end
def upnp_client_listener()
sock = Rex::Socket::Udp.create(
'LocalHost' => datastore['CHOST'] || nil,
'LocalPort' => @sport,
'Context' => {'Msf' => framework, 'MsfExploit' => self}
)
add_socket(sock)
while (r = sock.recvfrom(65535, 5) and r[1])
@result << r
def setup
super
@msearch_probe =
"M-SEARCH * HTTP/1.1\r\n" +
"Host:239.255.255.250:1900\r\n" +
"ST:upnp:rootdevice\r\n" +
"Man:\"ssdp:discover\"\r\n" +
"MX:3\r\n" +
"\r\n\r\n" # Non-standard, but helps
end
def scanner_prescan(batch)
print_status("Sending UPnP SSDP probes to #{batch[0]}->#{batch[-1]} (#{batch.length} hosts)")
@results = {}
end
def scan_host(ip)
scanner_send(@msearch_probe, ip, datastore['RPORT'])
end
def scanner_process(data, shost, sport)
skey = "#{shost}:#{datastore['RPORT']}"
return if @results[skey]
info = []
if data =~ /^Server:[\s]*(.*)/
info << $1.strip
end
end
def set_server_port
if datastore['SRVPORT'].to_i.zero?
datastore['SRVPORT'] = rand(10_000) + 40_000
else
datastore['SRVPORT'].to_i
end
end
def rport
datastore['RPORT'].to_i
end
def rhost
datastore['RHOST']
end
def target
"%s:%d" % [rhost, rport]
end
# The problem is, the response comes from someplace we're not
# expecting, since we're sending out on the multicast address.
# This means we need to listen on our sending port, either with
# packet craftiness or by being able to set our sport.
def run
print_status("#{target}: Sending SSDP M-SEARCH Probe.")
@result = []
@sport = set_server_port
begin
udp_send_sock = nil
server_thread = framework.threads.spawn("Module(#{self.refname})-Listener", false) { upnp_client_listener }
# TODO: Test to see if this scheme will work when pivoted.
# 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_send_sock = Rex::Socket::Udp.create(
'LocalHost' => datastore['CHOST'] || nil,
'LocalPort' => @sport,
'Context' => {'Msf' => framework, 'MsfExploit' => self}
)
add_socket(udp_send_sock)
data = create_msearch_packet(rhost,rport)
begin
udp_send_sock.sendto(data, rhost, rport, 0)
rescue ::Interrupt
raise $!
rescue ::Rex::HostUnreachable, ::Rex::ConnectionTimeout, ::Rex::ConnectionRefused
nil
end
begin
Timeout.timeout(6) do
while @result.size.zero?
select(nil, nil, nil, 0.25)
parse_reply @result
end
end
rescue Timeout::Error
end
end
end
# Someday, take all these very similiar parse_reply functions
# and make them proper block consumers.
def parse_reply(pkts)
pkts.each do |pkt|
# Ignore "empty" packets
return if not pkt[1]
addr = pkt[1]
if(addr =~ /^::ffff:/)
addr = addr.sub(/^::ffff:/, '')
end
port = pkt[2]
data = pkt[0]
info = []
if data =~ /^Server:[\s]*(.*)/
server_string = $1
info << "\"#{server_string.to_s.strip}\""
end
ssdp_host = nil
ssdp_port = 80
if data =~ /^Location:[\s]*(.*)/
location_string = $1
info << location_string.to_s.strip
if location_string[/(https?):\x2f\x2f([^\x5c\x2f]+)/]
ssdp_host,ssdp_port = $2.split(":") if $2.respond_to?(:split)
if ssdp_port.nil?
ssdp_port = ($1 == "http" ? 80 : 443)
end
ssdp_host = nil
ssdp_port = 80
location_string = ''
if data =~ /^Location:[\s]*(.*)/
location_string = $1
info << location_string.to_s.strip
if location_string[/(https?):\x2f\x2f([^\x5c\x2f]+)/]
ssdp_host,ssdp_port = $2.split(":") if $2.respond_to?(:split)
if ssdp_port.nil?
ssdp_port = ($1 == "http" ? 80 : 443)
end
end
end
if data =~ /^USN:[\s]*(.*)/
usn_string = $1
info << usn_string.to_s.strip
end
if data =~ /^USN:[\s]*(.*)/
info << $1.strip
end
return unless info.length > 0
desc = info.join(" | ")
@results[skey] = {
:host => shost,
:port => datastore['RPORT'],
:proto => 'udp',
:name => 'ssdp',
:info => desc
}
print_status("#{shost}:#{sport} SSDP #{desc}")
report_service( @results[skey] )
if ssdp_host
report_service(
:host => addr,
:port => port,
:proto => 'udp',
:name => 'ssdp',
:info => info.join("|")
)
if info.first.nil? || info.first.empty?
print_status "#{addr}:#{port}: Got an incomplete response."
else
print_good "#{addr}:#{port}: Got an SSDP response from #{info.first}"
end
if ssdp_host
report_service(
:host => ssdp_host,
:port => ssdp_port,
:proto => 'tcp',
:name => 'upnp',
:info => location_string
)
print_good "#{ssdp_host}:#{ssdp_port}: UPnP services advertised at #{info.grep(/#{ssdp_host}/).first}"
end
:host => ssdp_host,
:port => ssdp_port,
:proto => 'tcp',
:name => 'upnp',
:info => location_string
) if datastore['REPORT_LOCATION']
end
end
# I'm sure this could be a million times cooler.
def create_msearch_packet(host,port)
data = "M-SEARCH * HTTP/1.1\r\n"
data << "Host:#{host}:#{port}\r\n"
data << "ST:urn:schemas-upnp-org:device:InternetGatewayDevice:1\r\n"
data << "Man:\"ssdp:discover\"\r\n"
data << "MX:3\r\n"
return data
end
end