Path: blob/master/modules/auxiliary/scanner/discovery/udp_sweep.rb
19721 views
##1# This module requires Metasploit: https://metasploit.com/download2# Current source: https://github.com/rapid7/metasploit-framework3##45require 'openssl'67class MetasploitModule < Msf::Auxiliary8include Msf::Auxiliary::Report9include Msf::Auxiliary::UDPScanner1011def initialize12super(13'Name' => 'UDP Service Sweeper',14'Description' => 'Detect interesting UDP services.',15'Author' => 'hdm',16'License' => MSF_LICENSE,17'Notes' => {18'Stability' => [CRASH_SAFE],19'SideEffects' => [IOC_IN_LOGS],20'Reliability' => []21}22)2324register_advanced_options(25[26OptBool.new('RANDOMIZE_PORTS', [false, 'Randomize the order the ports are probed', true])27]28)2930# RPORT is required by UDPScanner but not used in this module since it31# works with multiple ports.32# TODO: update this module to simply use Scanner or update UDPScanner to support33# multiple ports.34deregister_options('RPORT')3536# Add the UDP probe method names37@probes = [38'probe_pkt_dns',39'probe_pkt_netbios',40'probe_pkt_portmap',41'probe_pkt_mssql',42'probe_pkt_ntp',43'probe_pkt_snmp1',44'probe_pkt_snmp2',45'probe_pkt_sentinel',46'probe_pkt_db2disco',47'probe_pkt_citrix',48'probe_pkt_pca_st',49'probe_pkt_pca_nq',50'probe_chargen',51]52end5354def setup55super5657if datastore['RANDOMIZE_PORTS']58@probes = @probes.sort_by { rand }59end60end6162def scanner_prescan(batch)63print_status("Sending #{@probes.length} probes to #{batch[0]}->#{batch[-1]} (#{batch.length} hosts)")64@results = {}65end6667def scan_host(ip)68@probes.each do |probe|69data, port = send(probe, ip)70scanner_send(data, ip, port)71end72end7374def scanner_postscan(_batch)75@results.each_key do |k|76next if !@results[k].respond_to?('keys')7778data = @results[k]7980conf = {81host: data[:host],82port: data[:port],83proto: 'udp',84name: data[:app],85info: data[:info]86}8788if data[:hname]89conf[:host_name] = data[:hname].downcase90end9192if data[:mac]93conf[:mac] = data[:mac].downcase94end9596report_service(conf)97print_good("Discovered #{data[:app]} on #{k} (#{data[:info]})")98end99end100101def scanner_process(data, shost, sport)102hkey = "#{shost}:#{sport}"103app = 'unknown'104inf = ''105maddr = nil106hname = nil107108# Work with protocols that return different data in different packets109# These are reported at the end of the scanning loop to build state110case sport111when 5632112113@results[hkey] ||= {}114data = @results[hkey]115data[:app] = 'pcAnywhere_stat'116data[:port] = sport117data[:host] = shost118119case data120121when /^NR(........................)(........)/122name = ::Regexp.last_match(1).dup123caps = ::Regexp.last_match(2).dup124name = name.gsub(/_+$/, '').gsub("\x00", '').strip125caps = caps.gsub(/_+$/, '').gsub("\x00", '').strip126data[:name] = name127data[:caps] = caps128129when /^ST(.+)/130buff = ::Regexp.last_match(1).dup131stat = 'Unknown'132133if buff[2, 1].unpack('C')[0] == 67134stat = 'Available'135end136137if buff[2, 1].unpack('C')[0] == 11138stat = 'Busy'139end140141data[:stat] = stat142end143144if data[:name]145inf << "Name: #{data[:name]} "146end147148if data[:stat]149inf << "- #{data[:stat]} "150end151152if data[:caps]153inf << "( #{data[:caps]} ) "154end155data[:info] = inf156end157158# Ignore duplicates159return if @results[hkey]160161case sport162163when 19164app = 'chargen'165return unless chargen_parse(data)166167@results[hkey] = true168169when 53170app = 'DNS'171ver = nil172173if !ver && data =~ %r{([6789]\.[\w.\-_:()\[\]/=+|{}]+)}i174ver = 'BIND ' + ::Regexp.last_match(1)175end176177ver = 'Microsoft DNS' if !ver && (data[2, 4] == "\x81\x04\x00\x01")178ver = 'TinyDNS' if !ver && (data[2, 4] == "\x81\x81\x00\x01")179180ver = data.unpack('H*')[0] if !ver181inf = ver if ver182183@results[hkey] = true184185when 137186app = 'NetBIOS'187188buff = data.dup189190head = buff.slice!(0, 12)191192_, _, quests, answers, = head.unpack('n6')193return if quests != 0194return if answers == 0195196buff.slice!(0, 34)197rtype, _, _, rlen = buff.slice!(0, 10).unpack('nnNn')198bits = buff.slice!(0, rlen)199200names = []201202case rtype203when 0x21204rcnt = bits.slice!(0, 1).unpack('C')[0]2051.upto(rcnt) do206tname = bits.slice!(0, 15).gsub(/\x00.*/, '').strip207ttype = bits.slice!(0, 1).unpack('C')[0]208tflag = bits.slice!(0, 2).unpack('n')[0]209names << [ tname, ttype, tflag ]210end211maddr = bits.slice!(0, 6).unpack('C*').map { |c| '%.2x' % c }.join(':')212213names.each do |n|214inf << n[0]215inf << ':<%.2x>' % n[1]216if (n[2] & 0x8000 == 0)217inf << ':U :'218else219inf << ':G :'220end221end222inf << maddr223224if !names.empty?225hname = names[0][0]226end227end228229@results[hkey] = true230231when 111232app = 'Portmap'233buf = data234inf = ''235buf.slice!(0, 24)236svc = []237while (buf.length >= 20)238rec = buf.slice!(0, 20).unpack('N5')239svc << "#{rec[1]} v#{rec[2]} #{rec[3] == 0x06 ? 'TCP' : 'UDP'}(#{rec[4]})"240report_service(241host: shost,242port: rec[4],243proto: (rec[3] == 0x06 ? 'tcp' : 'udp'),244name: 'sunrpc',245info: "#{rec[1]} v#{rec[2]}",246state: 'open'247)248end249inf = svc.join(', ')250251@results[hkey] = true252253when 123254app = 'NTP'255ver = data.unpack('H*')[0]256ver = 'NTP v3' if (ver =~ /^1c06|^1c05/)257ver = 'NTP v4' if (ver =~ /^240304/)258ver = 'NTP v4 (unsynchronized)' if (ver =~ /^e40/)259ver = 'Microsoft NTP' if (ver =~ /^dc00|^dc0f/)260inf = ver if ver261262@results[hkey] = true263264when 1434265app = 'MSSQL'266mssql_ping_parse(data).each_pair do |k, v|267inf += k + '=' + v + ' '268end269270@results[hkey] = true271272when 161273app = 'SNMP'274asn = begin275OpenSSL::ASN1.decode(data)276rescue StandardError277nil278end279return if !asn280281snmp_error = begin282asn.value[0].value283rescue StandardError284nil285end286snmp_comm = begin287asn.value[1].value288rescue StandardError289nil290end291snmp_data = begin292asn.value[2].value[3].value[0]293rescue StandardError294nil295end296snmp_oid = begin297snmp_data.value[0].value298rescue StandardError299nil300end301snmp_info = begin302snmp_data.value[1].value303rescue StandardError304nil305end306307return if !(snmp_error && snmp_comm && snmp_data && snmp_oid && snmp_info)308309snmp_info = snmp_info.to_s.gsub(/\s+/, ' ')310311inf = snmp_info312313@results[hkey] = true314315when 5093316app = 'Sentinel'317@results[hkey] = true318319when 523320app = 'ibm-db2'321inf = db2disco_parse(data)322@results[hkey] = true323324when 1604325app = 'citrix-ica'326return unless citrix_parse(data)327328@results[hkey] = true329330end331332report_service(333host: shost,334mac: (maddr && (maddr != '00:00:00:00:00:00')) ? maddr : nil,335host_name: hname ? hname.downcase : nil,336port: sport,337proto: 'udp',338name: app,339info: inf,340state: 'open'341)342343print_status("Discovered #{app} on #{shost}:#{sport} (#{inf})")344end345346#347# Validate a chargen packet.348#349def chargen_parse(data)350data =~ /ABCDEFGHIJKLMNOPQRSTUVWXYZ|0123456789/i351end352353#354# Parse a db2disco packet.355#356def db2disco_parse(data)357res = data.split("\x00")358"#{res[2]}_#{res[1]}"359end360361#362# Validate this is truly Citrix ICA; returns true or false.363#364def citrix_parse(data)365server_response = "\x30\x00\x02\x31\x02\xfd\xa8\xe3\x02\x00\x06\x44" # Server hello response366data =~ /^#{server_response}/367end368369#370# Parse a 'ping' response and format as a hash371#372def mssql_ping_parse(data)373res = {}374var = nil375idx = data.index('ServerName')376return res if !idx377378data[idx, data.length - idx].split(';').each do |d|379if !var380var = d381elsif !var.empty?382res[var] = d383var = nil384end385end386387return res388end389390#391# The probe definitions392#393394def probe_chargen(_ip)395pkt = Rex::Text.rand_text_alpha_lower(1)396return [pkt, 19]397end398399def probe_pkt_dns(_ip)400data = [rand(0xffff)].pack('n') +401"\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00" \402"\x07" + 'VERSION' \403"\x04" + 'BIND' \404"\x00\x00\x10\x00\x03"405406return [data, 53]407end408409def probe_pkt_netbios(_ip)410data =411[rand(0xffff)].pack('n') +412"\x00\x00\x00\x01\x00\x00\x00\x00" \413"\x00\x00\x20\x43\x4b\x41\x41\x41" \414"\x41\x41\x41\x41\x41\x41\x41\x41" \415"\x41\x41\x41\x41\x41\x41\x41\x41" \416"\x41\x41\x41\x41\x41\x41\x41\x41" \417"\x41\x41\x41\x00\x00\x21\x00\x01"418419return [data, 137]420end421422def probe_pkt_portmap(_ip)423data =424[425rand(0xffffffff), # XID4260, # Type4272, # RPC Version428100000, # Program ID4292, # Program Version4304, # Procedure4310, 0, # Credentials4320, 0, # Verifier433].pack('N*')434435return [data, 111]436end437438def probe_pkt_mssql(_ip)439return ["\x02", 1434]440end441442def probe_pkt_ntp(_ip)443data =444"\xe3\x00\x04\xfa\x00\x01\x00\x00\x00\x01\x00\x00\x00" \445"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" \446"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" \447"\x00\xc5\x4f\x23\x4b\x71\xb1\x52\xf3"448return [data, 123]449end450451def probe_pkt_sentinel(_ip)452return ["\x7a\x00\x00\x00\x00\x00", 5093]453end454455def probe_pkt_snmp1(_ip)456version = 1457data = OpenSSL::ASN1::Sequence([458OpenSSL::ASN1::Integer(version - 1),459OpenSSL::ASN1::OctetString('public'),460OpenSSL::ASN1::Set.new([461OpenSSL::ASN1::Integer(rand(0x80000000)),462OpenSSL::ASN1::Integer(0),463OpenSSL::ASN1::Integer(0),464OpenSSL::ASN1::Sequence([465OpenSSL::ASN1::Sequence([466OpenSSL::ASN1.ObjectId('1.3.6.1.2.1.1.1.0'),467OpenSSL::ASN1.Null(nil)468])469]),470], 0, :IMPLICIT)471]).to_der472[data, 161]473end474475def probe_pkt_snmp2(_ip)476version = 2477data = OpenSSL::ASN1::Sequence([478OpenSSL::ASN1::Integer(version - 1),479OpenSSL::ASN1::OctetString('public'),480OpenSSL::ASN1::Set.new([481OpenSSL::ASN1::Integer(rand(0x80000000)),482OpenSSL::ASN1::Integer(0),483OpenSSL::ASN1::Integer(0),484OpenSSL::ASN1::Sequence([485OpenSSL::ASN1::Sequence([486OpenSSL::ASN1.ObjectId('1.3.6.1.2.1.1.1.0'),487OpenSSL::ASN1.Null(nil)488])489]),490], 0, :IMPLICIT)491]).to_der492[data, 161]493end494495def probe_pkt_db2disco(_ip)496data = "DB2GETADDR\x00SQL05000\x00"497[data, 523]498end499500# Server hello packet from citrix_published_bruteforce501def probe_pkt_citrix(_ip)502data =503"\x1e\x00\x01\x30\x02\xfd\xa8\xe3\x00\x00\x00\x00\x00" \504"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" \505"\x00\x00\x00\x00"506return [data, 1604]507end508509def probe_pkt_pca_st(_ip)510return ['ST', 5632]511end512513def probe_pkt_pca_nq(_ip)514return ['NQ', 5632]515end516end517518519