From 4b2a2d4dea5eb59ad459543809aa9deb2d2c78c6 Mon Sep 17 00:00:00 2001 From: Matteo Cantoni Date: Fri, 21 Mar 2014 16:39:53 +0100 Subject: [PATCH 1/2] Improve NTP monlist auxiliary module --- modules/auxiliary/scanner/ntp/ntp_monlist.rb | 135 ++++++++++++------- 1 file changed, 87 insertions(+), 48 deletions(-) diff --git a/modules/auxiliary/scanner/ntp/ntp_monlist.rb b/modules/auxiliary/scanner/ntp/ntp_monlist.rb index f03a77a518..da61e4426d 100644 --- a/modules/auxiliary/scanner/ntp/ntp_monlist.rb +++ b/modules/auxiliary/scanner/ntp/ntp_monlist.rb @@ -6,7 +6,6 @@ require 'msf/core' - class Metasploit3 < Msf::Auxiliary include Msf::Auxiliary::Report @@ -15,7 +14,20 @@ class Metasploit3 < Msf::Auxiliary def initialize super( 'Name' => 'NTP Monitor List Scanner', - 'Description' => 'Obtain the list of recent clients from an NTP server', + 'Description' => %q{ + This module identifies NTP server which permit "monlist" queries + and obtains the recents clients list. The monlist feature allows + remote attackers to cause a denial of service (traffic amplification) + via forged (spoofed) requests. More clients in the list, the greater + is the amplification. + }, + 'References' => + [ + ['CVE', '2013-5211'], + ['URL', 'https://www.us-cert.gov/ncas/alerts/TA14-013A'], + ['URL', 'http://support.ntp.org/bin/view/Main/SecurityNotice'], + ['URL', 'http://nmap.org/nsedoc/scripts/ntp-monlist.html'], + ], 'Author' => 'hdm', 'License' => MSF_LICENSE ) @@ -24,17 +36,17 @@ class Metasploit3 < Msf::Auxiliary [ Opt::RPORT(123), Opt::CHOST, - OptInt.new('BATCHSIZE', [true, 'The number of hosts to probe in each set', 256]) + OptInt.new('RETRY', [ false, "Number of tries to query the alleged NTP server", 3]), + OptInt.new('BATCHSIZE', [true, 'The number of hosts to probe in each set', 256]), + OptBool.new('SHOW_LIST', [false, 'Show the recents clients list', 'false']) ], self.class) - register_advanced_options( [ OptBool.new('StoreNTPClients', [true, 'Store NTP clients as host records in the database', 'false']) ], self.class) end - # Define our batch size def run_batch_size datastore['BATCHSIZE'].to_i @@ -46,7 +58,7 @@ class Metasploit3 < Msf::Auxiliary @results = {} @aliases = {} - print_status("Sending probes to #{batch[0]}->#{batch[-1]} (#{batch.length} hosts)") + vprint_status("Sending probes to #{batch[0]}->#{batch[-1]} (#{batch.length} hosts)") begin udp_sock = nil @@ -54,31 +66,34 @@ class Metasploit3 < Msf::Auxiliary # 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} }) + udp_sock = Rex::Socket::Udp.create({ + 'LocalHost' => datastore['CHOST'] || nil, + 'Context' => {'Msf' => framework, 'MsfExploit' => self} + }) add_socket(udp_sock) - # Try three times since NTP servers can be a bit busy - 1.upto(3) do - batch.each do |ip| - next if @results[ip] + # Try more times since NTP servers can be a bit busy + 1.upto(datastore['RETRY'].to_i) do + batch.each do |ip| + next if @results[ip] - begin - data = probe_pkt_ntp(ip) - udp_sock.sendto(data, ip, datastore['RPORT'].to_i, 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) + begin + data = probe_pkt_ntp(ip) + udp_sock.sendto(data, ip, datastore['RPORT'].to_i, 0) + rescue ::Interrupt + raise $! + rescue ::Rex::HostUnreachable, ::Rex::ConnectionTimeout, ::Rex::ConnectionRefused + nil end - end - idx += 1 - end + if (idx % 30 == 0) + while (r = udp_sock.recvfrom(65535, 0.1) and r[1]) + parse_reply(r) + end + end + + idx += 1 + end end while (r = udp_sock.recvfrom(65535, 10) and r[1]) @@ -109,6 +124,7 @@ class Metasploit3 < Msf::Auxiliary ) if (@aliases[k] and @aliases[k].keys[0] != k) + print_good("#{k}:#{datastore['RPORT'].to_i} NTP monlist request permitted (#{@results[k].length} entries)") report_note( :host => k, :proto => 'udp', @@ -122,7 +138,15 @@ class Metasploit3 < Msf::Auxiliary print_status("#{k} Storing #{@results[k].length} NTP client hosts in the database...") @results[k].each do |r| maddr,mport,mserv = r - report_note(:host => maddr, :type => 'ntp.client.history', :data => {:address => maddr, :port => mport, :server => mserv}) + report_note( + :host => maddr, + :type => 'ntp.client.history', + :data => { + :address => maddr, + :port => mport, + :server => mserv + } + ) end end end @@ -143,39 +167,54 @@ class Metasploit3 < Msf::Auxiliary port = pkt[2] return if pkt[0].length < (72 + 16) + + # NTP headers 8 bytes ntp_flags, ntp_auth, ntp_vers, ntp_code = data.slice!(0,4).unpack('C*') - pcnt, plen, hlen, tmp = data.slice!(0,12).unpack('nnNN') + vprint_status("#{host}:#{port} - ntp_auth: #{ntp_auth}, ntp_vers: #{ntp_vers}") + pcnt, plen = data.slice!(0,4).unpack('nn') return if plen != 72 idx = 0 1.upto(pcnt) do - tmp1,mcnt,madd,sadd,tmp3,tmp4,mport = data[idx, plen].unpack("NNNNn3") + + #u_int32 firsttime; /* first time we received a packet */ + #u_int32 lasttime; /* last packet from this host */ + #u_int32 restr; /* restrict bits (was named lastdrop) */ + #u_int32 count; /* count of packets received */ + #u_int32 addr; /* host address V4 style */ + #u_int32 daddr; /* destination host address */ + #u_int32 flags; /* flags about destination */ + #u_short port; /* port number of last reception */ + + firsttime,lasttime,restr,count,saddr,daddr,flags,dport = data[idx, 30].unpack("NNNNNNNn") + #print_status("#{firsttime},#{lasttime},#{restr},#{count},#{saddr},#{daddr},#{flags},#{dport}") + @results[host] ||= [] @aliases[host] ||= {} - @results[host] << [ Rex::Socket.addr_itoa(madd), mport, Rex::Socket.addr_itoa(sadd) ] - @aliases[host][Rex::Socket.addr_itoa(sadd)] = true - print_status("#{host}:#{port} #{Rex::Socket.addr_itoa(madd)}:#{mport} (#{Rex::Socket.addr_itoa(sadd)})") + @results[host] << [ Rex::Socket.addr_itoa(daddr), dport, Rex::Socket.addr_itoa(saddr) ] + @aliases[host][Rex::Socket.addr_itoa(saddr)] = true + if datastore['SHOW_LIST'] + print_status("#{host}:#{port} #{Rex::Socket.addr_itoa(saddr)} (lst: #{lasttime}sec., cnt: #{count}]") + end idx += plen end end - def probe_pkt_ntp(ip) - data = - "\x17\x00\x03\x2a\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + data = "\x17\x00\x03\x2a\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + data << "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + data << "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + data << "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + data << "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + data << "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + data << "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + data << "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + data << "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + data << "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + data << "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + data << "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + data << "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + data << "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" return data end From 1c4797337fd2e2210e7ee891fa0fcf60b1b64f4f Mon Sep 17 00:00:00 2001 From: William Vu Date: Tue, 25 Mar 2014 14:03:25 -0500 Subject: [PATCH 2/2] Clean up rapid7/metasploit-framework#3132 --- modules/auxiliary/scanner/ntp/ntp_monlist.rb | 39 ++++++-------------- 1 file changed, 11 insertions(+), 28 deletions(-) diff --git a/modules/auxiliary/scanner/ntp/ntp_monlist.rb b/modules/auxiliary/scanner/ntp/ntp_monlist.rb index da61e4426d..c8b5675377 100644 --- a/modules/auxiliary/scanner/ntp/ntp_monlist.rb +++ b/modules/auxiliary/scanner/ntp/ntp_monlist.rb @@ -3,7 +3,6 @@ # Current source: https://github.com/rapid7/metasploit-framework ## - require 'msf/core' class Metasploit3 < Msf::Auxiliary @@ -15,11 +14,11 @@ class Metasploit3 < Msf::Auxiliary super( 'Name' => 'NTP Monitor List Scanner', 'Description' => %q{ - This module identifies NTP server which permit "monlist" queries - and obtains the recents clients list. The monlist feature allows - remote attackers to cause a denial of service (traffic amplification) - via forged (spoofed) requests. More clients in the list, the greater - is the amplification. + This module identifies NTP servers which permit "monlist" queries and + obtains the recent clients list. The monlist feature allows remote + attackers to cause a denial of service (traffic amplification) + via spoofed requests. The more clients there are in the list, the + greater the amplification. }, 'References' => [ @@ -36,9 +35,9 @@ class Metasploit3 < Msf::Auxiliary [ Opt::RPORT(123), Opt::CHOST, - OptInt.new('RETRY', [ false, "Number of tries to query the alleged NTP server", 3]), + OptInt.new('RETRY', [false, "Number of tries to query the NTP server", 3]), OptInt.new('BATCHSIZE', [true, 'The number of hosts to probe in each set', 256]), - OptBool.new('SHOW_LIST', [false, 'Show the recents clients list', 'false']) + OptBool.new('SHOW_LIST', [false, 'Show the recent clients list', 'false']) ], self.class) register_advanced_options( @@ -78,7 +77,7 @@ class Metasploit3 < Msf::Auxiliary next if @results[ip] begin - data = probe_pkt_ntp(ip) + data = probe_pkt_ntp udp_sock.sendto(data, ip, datastore['RPORT'].to_i, 0) rescue ::Interrupt raise $! @@ -176,7 +175,6 @@ class Metasploit3 < Msf::Auxiliary idx = 0 1.upto(pcnt) do - #u_int32 firsttime; /* first time we received a packet */ #u_int32 lasttime; /* last packet from this host */ #u_int32 restr; /* restrict bits (was named lastdrop) */ @@ -187,35 +185,20 @@ class Metasploit3 < Msf::Auxiliary #u_short port; /* port number of last reception */ firsttime,lasttime,restr,count,saddr,daddr,flags,dport = data[idx, 30].unpack("NNNNNNNn") - #print_status("#{firsttime},#{lasttime},#{restr},#{count},#{saddr},#{daddr},#{flags},#{dport}") @results[host] ||= [] @aliases[host] ||= {} @results[host] << [ Rex::Socket.addr_itoa(daddr), dport, Rex::Socket.addr_itoa(saddr) ] @aliases[host][Rex::Socket.addr_itoa(saddr)] = true if datastore['SHOW_LIST'] - print_status("#{host}:#{port} #{Rex::Socket.addr_itoa(saddr)} (lst: #{lasttime}sec., cnt: #{count}]") + print_status("#{host}:#{port} #{Rex::Socket.addr_itoa(saddr)} (lst: #{lasttime}sec., cnt: #{count})") end idx += plen end end - def probe_pkt_ntp(ip) - data = "\x17\x00\x03\x2a\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" - data << "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" - data << "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" - data << "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" - data << "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" - data << "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" - data << "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" - data << "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" - data << "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" - data << "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" - data << "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" - data << "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" - data << "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" - data << "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" - return data + def probe_pkt_ntp + "\x17\x00\x03\x2a" + "\x00" * 188 end end