Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Path: blob/master/modules/auxiliary/scanner/discovery/udp_probe.rb
Views: 11784
##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::Scanner1011def initialize12super(13'Name' => 'UDP Service Prober',14'Description' => 'Detect common UDP services using sequential probes',15'Author' => 'hdm',16'License' => MSF_LICENSE17)1819register_options(20[21Opt::CHOST,22])2324register_advanced_options(25[26OptBool.new('RANDOMIZE_PORTS', [false, 'Randomize the order the ports are probed', true])27])2829# Initialize the probes array30@probes = []3132# Add the UDP probe method names33@probes << 'probe_pkt_dns'34@probes << 'probe_pkt_netbios'35@probes << 'probe_pkt_portmap'36@probes << 'probe_pkt_mssql'37@probes << 'probe_pkt_ntp'38@probes << 'probe_pkt_snmp1'39@probes << 'probe_pkt_snmp2'40@probes << 'probe_pkt_sentinel'41@probes << 'probe_pkt_db2disco'42@probes << 'probe_pkt_citrix'43@probes << 'probe_pkt_pca_st'44@probes << 'probe_pkt_pca_nq'45@probes << 'probe_chargen'4647end4849def setup50super5152if datastore['RANDOMIZE_PORTS']53@probes = @probes.sort_by { rand }54end55end5657# Fingerprint a single host58def run_host(ip)59@results = {}60@thost = ip6162begin63udp_sock = nil6465@probes.each do |probe|6667# Send each probe to each host6869begin70data, port = self.send(probe, ip)71@tport = port7273# Create an unbound UDP socket if no CHOST is specified, otherwise74# create a UDP socket bound to CHOST (in order to avail of pivoting)75udp_sock = Rex::Socket::Udp.create( {76'LocalHost' => datastore['CHOST'] || nil,77'PeerHost' => ip, 'PeerPort' => port,78'Context' => {'Msf' => framework, 'MsfExploit' => self}79})8081udp_sock.put(data)8283r = udp_sock.recvfrom(65535, 0.1) and r[1]84parse_reply(r) if r8586rescue ::Interrupt87raise $!88rescue ::Rex::HostUnreachable, ::Rex::ConnectionTimeout, ::Rex::ConnectionRefused, ::IOError89nil90ensure91udp_sock.close if udp_sock92end9394end95rescue ::Interrupt96raise $!97rescue ::Exception => e98print_error("Unknown error: #{@thost}:#{@tport} #{e.class} #{e} #{e.backtrace}")99end100101@results.each_key do |k|102next if not @results[k].respond_to?('keys')103data = @results[k]104105next unless inside_workspace_boundary?(data[:host])106107conf = {108:host => data[:host],109:port => data[:port],110:proto => 'udp',111:name => data[:app],112:info => data[:info]113}114115if data[:hname]116conf[:host_name] = data[:hname].downcase117end118119if data[:mac]120conf[:mac] = data[:mac].downcase121end122123report_service(conf)124print_good("Discovered #{data[:app]} on #{k} (#{data[:info]})")125end126end127128129#130# The response parsers131#132def parse_reply(pkt)133134# Ignore "empty" packets135return if not pkt[1]136137if(pkt[1] =~ /^::ffff:/)138pkt[1] = pkt[1].sub(/^::ffff:/, '')139end140141app = 'unknown'142inf = ''143maddr = nil144hname = nil145146hkey = "#{pkt[1]}:#{pkt[2]}"147148# Work with protocols that return different data in different packets149# These are reported at the end of the scanning loop to build state150case pkt[2]151when 5632152153@results[hkey] ||= {}154data = @results[hkey]155data[:app] = "pcAnywhere_stat"156data[:port] = pkt[2]157data[:host] = pkt[1]158159case pkt[0]160161when /^NR(........................)(........)/162name = $1.dup163caps = $2.dup164name = name.gsub(/_+$/, '').gsub("\x00", '').strip165caps = caps.gsub(/_+$/, '').gsub("\x00", '').strip166data[:name] = name167data[:caps] = caps168169when /^ST(.+)/170buff = $1.dup171stat = 'Unknown'172173if buff[2,1].unpack("C")[0] == 67174stat = "Available"175end176177if buff[2,1].unpack("C")[0] == 11178stat = "Busy"179end180181data[:stat] = stat182end183184if data[:name]185inf << "Name: #{data[:name]} "186end187188if data[:stat]189inf << "- #{data[:stat]} "190end191192if data[:caps]193inf << "( #{data[:caps]} ) "194end195data[:info] = inf196end197198199# Ignore duplicates for the protocols below200return if @results[hkey]201202case pkt[2]203204when 19205app = 'chargen'206return unless chargen_parse(pkt[0])207@results[hkey] = true208209when 53210app = 'DNS'211ver = nil212213if (not ver and pkt[0] =~ /([6789]\.[\w\.\-_\:\(\)\[\]\/\=\+\|\{\}]+)/i)214ver = 'BIND ' + $1215end216217ver = 'Microsoft DNS' if (not ver and pkt[0][2,4] == "\x81\x04\x00\x01")218ver = 'TinyDNS' if (not ver and pkt[0][2,4] == "\x81\x81\x00\x01")219220ver = pkt[0].unpack('H*')[0] if not ver221inf = ver if ver222@results[hkey] = true223224when 137225app = 'NetBIOS'226227data = pkt[0]228229head = data.slice!(0,12)230231xid, flags, quests, answers, auths, adds = head.unpack('n6')232return if quests != 0233return if answers == 0234235qname = data.slice!(0,34)236rtype,rclass,rttl,rlen = data.slice!(0,10).unpack('nnNn')237buff = data.slice!(0,rlen)238239names = []240241case rtype242when 0x21243rcnt = buff.slice!(0,1).unpack("C")[0]2441.upto(rcnt) do245tname = buff.slice!(0,15).gsub(/\x00.*/, '').strip246ttype = buff.slice!(0,1).unpack("C")[0]247tflag = buff.slice!(0,2).unpack('n')[0]248names << [ tname, ttype, tflag ]249end250maddr = buff.slice!(0,6).unpack("C*").map{|c| "%.2x" % c }.join(":")251252names.each do |name|253inf << name[0]254inf << ":<%.2x>" % name[1]255if (name[2] & 0x8000 == 0)256inf << ":U :"257else258inf << ":G :"259end260end261inf << maddr262263if(names.length > 0)264hname = names[0][0]265end266end267268@results[hkey] = true269270when 111271app = 'Portmap'272buf = pkt[0]273inf = ""274hed = buf.slice!(0,24)275svc = []276while(buf.length >= 20)277rec = buf.slice!(0,20).unpack("N5")278svc << "#{rec[1]} v#{rec[2]} #{rec[3] == 0x06 ? "TCP" : "UDP"}(#{rec[4]})"279report_service(280:host => pkt[1],281:port => rec[4],282:proto => (rec[3] == 0x06 ? "tcp" : "udp"),283:name => "sunrpc",284:info => "#{rec[1]} v#{rec[2]}"285)286end287inf = svc.join(", ")288@results[hkey] = true289290when 123291app = 'NTP'292ver = nil293ver = pkt[0].unpack('H*')[0]294ver = 'NTP v3' if (ver =~ /^1c06|^1c05/)295ver = 'NTP v4' if (ver =~ /^240304/)296ver = 'NTP v4 (unsynchronized)' if (ver =~ /^e40/)297ver = 'Microsoft NTP' if (ver =~ /^dc00|^dc0f/)298inf = ver if ver299@results[hkey] = true300301when 1434302app = 'MSSQL'303mssql_ping_parse(pkt[0]).each_pair { |k,v|304inf += k+'='+v+' '305}306@results[hkey] = true307308when 161309app = 'SNMP'310311asn = OpenSSL::ASN1.decode(pkt[0]) rescue nil312return if not asn313314snmp_error = asn.value[0].value rescue nil315snmp_comm = asn.value[1].value rescue nil316snmp_data = asn.value[2].value[3].value[0] rescue nil317snmp_oid = snmp_data.value[0].value rescue nil318snmp_info = snmp_data.value[1].value rescue nil319320return if not (snmp_error and snmp_comm and snmp_data and snmp_oid and snmp_info)321snmp_info = snmp_info.to_s.gsub(/\s+/, ' ')322323inf = snmp_info324com = snmp_comm325@results[hkey] = true326327when 5093328app = 'Sentinel'329@results[hkey] = true330331when 523332app = 'ibm-db2'333inf = db2disco_parse(pkt[0])334@results[hkey] = true335336when 1604337app = 'citrix-ica'338return unless citrix_parse(pkt[0])339@results[hkey] = true340341end342343return unless inside_workspace_boundary?(pkt[1])344report_service(345:host => pkt[1],346:mac => (maddr and maddr != '00:00:00:00:00:00') ? maddr : nil,347:host_name => (hname) ? hname.downcase : nil,348:port => pkt[2],349:proto => 'udp',350:name => app,351:info => inf352)353354print_good("Discovered #{app} on #{pkt[1]}:#{pkt[2]} (#{inf})")355356end357358359#360# Parse a db2disco packet.361#362def db2disco_parse(data)363res = data.split("\x00")364"#{res[2]}_#{res[1]}"365end366367#368# Validate a chargen packet.369#370def chargen_parse(data)371data =~ /ABCDEFGHIJKLMNOPQRSTUVWXYZ|0123456789/i372end373374#375# Validate this is truly Citrix ICA; returns true or false.376#377def citrix_parse(data)378server_response = "\x30\x00\x02\x31\x02\xfd\xa8\xe3\x02\x00\x06\x44" # Server hello response379data =~ /^#{server_response}/380end381382#383# Parse a 'ping' response and format as a hash384#385def mssql_ping_parse(data)386res = {}387var = nil388idx = data.index('ServerName')389return res if not idx390391data[idx, data.length-idx].split(';').each do |d|392if (not var)393var = d394else395if (var.length > 0)396res[var] = d397var = nil398end399end400end401402return res403end404405#406# The probe definitions407#408409def probe_chargen(ip)410pkt = Rex::Text.rand_text_alpha_lower(1)411return [pkt, 19]412end413414def probe_pkt_dns(ip)415data = [rand(0xffff)].pack('n') +416"\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00"+417"\x07"+ "VERSION"+418"\x04"+ "BIND"+419"\x00\x00\x10\x00\x03"420421return [data, 53]422end423424def probe_pkt_netbios(ip)425data =426[rand(0xffff)].pack('n')+427"\x00\x00\x00\x01\x00\x00\x00\x00"+428"\x00\x00\x20\x43\x4b\x41\x41\x41"+429"\x41\x41\x41\x41\x41\x41\x41\x41"+430"\x41\x41\x41\x41\x41\x41\x41\x41"+431"\x41\x41\x41\x41\x41\x41\x41\x41"+432"\x41\x41\x41\x00\x00\x21\x00\x01"433434return [data, 137]435end436437def probe_pkt_portmap(ip)438data =439[440rand(0xffffffff), # XID4410, # Type4422, # RPC Version443100000, # Program ID4442, # Program Version4454, # Procedure4460, 0, # Credentials4470, 0, # Verifier448].pack('N*')449450return [data, 111]451end452453def probe_pkt_mssql(ip)454return ["\x02", 1434]455end456457def probe_pkt_ntp(ip)458data =459"\xe3\x00\x04\xfa\x00\x01\x00\x00\x00\x01\x00\x00\x00" +460"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +461"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +462"\x00\xc5\x4f\x23\x4b\x71\xb1\x52\xf3"463return [data, 123]464end465466467def probe_pkt_sentinel(ip)468return ["\x7a\x00\x00\x00\x00\x00", 5093]469end470471def probe_pkt_snmp1(ip)472version = 1473data = OpenSSL::ASN1::Sequence([474OpenSSL::ASN1::Integer(version - 1),475OpenSSL::ASN1::OctetString("public"),476OpenSSL::ASN1::Set.new([477OpenSSL::ASN1::Integer(rand(0x80000000)),478OpenSSL::ASN1::Integer(0),479OpenSSL::ASN1::Integer(0),480OpenSSL::ASN1::Sequence([481OpenSSL::ASN1::Sequence([482OpenSSL::ASN1.ObjectId("1.3.6.1.2.1.1.1.0"),483OpenSSL::ASN1.Null(nil)484])485]),486], 0, :IMPLICIT)487]).to_der488[data, 161]489end490491def probe_pkt_snmp2(ip)492version = 2493data = OpenSSL::ASN1::Sequence([494OpenSSL::ASN1::Integer(version - 1),495OpenSSL::ASN1::OctetString("public"),496OpenSSL::ASN1::Set.new([497OpenSSL::ASN1::Integer(rand(0x80000000)),498OpenSSL::ASN1::Integer(0),499OpenSSL::ASN1::Integer(0),500OpenSSL::ASN1::Sequence([501OpenSSL::ASN1::Sequence([502OpenSSL::ASN1.ObjectId("1.3.6.1.2.1.1.1.0"),503OpenSSL::ASN1.Null(nil)504])505]),506], 0, :IMPLICIT)507]).to_der508[data, 161]509end510511def probe_pkt_db2disco(ip)512data = "DB2GETADDR\x00SQL05000\x00"513[data, 523]514end515516def probe_pkt_citrix(ip) # Server hello packet from citrix_published_bruteforce517data =518"\x1e\x00\x01\x30\x02\xfd\xa8\xe3\x00\x00\x00\x00\x00" +519"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +520"\x00\x00\x00\x00"521return [data, 1604]522end523524def probe_pkt_pca_st(ip)525return ["ST", 5632]526end527528def probe_pkt_pca_nq(ip)529return ["NQ", 5632]530end531end532533534