Statistics
| Branch: | Tag: | Revision:

root / modules / auxiliary / scanner / ntp / ntp_monlist.rb @ master

History | View | Annotate | Download (4.8 kB)

1
##
2
# $Id$
3
##
4

    
5
##
6
# This file is part of the Metasploit Framework and may be subject to
7
# redistribution and commercial restrictions. Please see the Metasploit
8
# Framework web site for more information on licensing and terms of use.
9
# http://metasploit.com/framework/
10
##
11

    
12

    
13
require 'msf/core'
14

    
15

    
16
class Metasploit3 < Msf::Auxiliary
17

    
18
        include Msf::Auxiliary::Report
19
        include Msf::Auxiliary::Scanner
20

    
21
        def initialize
22
                super(
23
                        'Name'        => 'NTP Monitor List Scanner',
24
                        'Version'     => '$Revision$',
25
                        'Description' => 'Obtain the list of recent clients from an NTP server',
26
                        'Author'      => 'hdm',
27
                        'License'     => MSF_LICENSE
28
                )
29

    
30
                register_options(
31
                [
32
                        Opt::RPORT(123),
33
                        Opt::CHOST,
34
                        OptInt.new('BATCHSIZE', [true, 'The number of hosts to probe in each set', 256])
35
                ], self.class)
36

    
37

    
38
                register_advanced_options(
39
                [
40
                        OptBool.new('StoreNTPClients', [true, 'Store NTP clients as host records in the database', 'false'])
41
                ], self.class)
42
        end
43

    
44

    
45
        # Define our batch size
46
        def run_batch_size
47
                datastore['BATCHSIZE'].to_i
48
        end
49

    
50
        # Fingerprint a single host
51
        def run_batch(batch)
52

    
53
                @results = {}
54
                @aliases = {}
55

    
56
                print_status("Sending probes to #{batch[0]}->#{batch[-1]} (#{batch.length} hosts)")
57

    
58
                begin
59
                        udp_sock = nil
60
                        idx = 0
61

    
62
                        # Create an unbound UDP socket if no CHOST is specified, otherwise
63
                        # create a UDP socket bound to CHOST (in order to avail of pivoting)
64
                        udp_sock = Rex::Socket::Udp.create( { 'LocalHost' => datastore['CHOST'] || nil, 'Context' => {'Msf' => framework, 'MsfExploit' => self} })
65
                        add_socket(udp_sock)
66

    
67
                        # Try three times since NTP servers can be a bit busy
68
                        1.upto(3)  do
69
                        batch.each do |ip|
70
                                next if @results[ip]
71

    
72
                                begin
73
                                        data = probe_pkt_ntp(ip)
74
                                        udp_sock.sendto(data, ip, datastore['RPORT'].to_i, 0)
75
                                rescue ::Interrupt
76
                                        raise $!
77
                                rescue ::Rex::HostUnreachable, ::Rex::ConnectionTimeout, ::Rex::ConnectionRefused
78
                                        nil
79
                                end
80

    
81
                                if (idx % 30 == 0)
82
                                        while (r = udp_sock.recvfrom(65535, 0.1) and r[1])
83
                                                parse_reply(r)
84
                                        end
85
                                end
86

    
87
                                idx += 1
88
                        end
89
                        end
90

    
91
                        while (r = udp_sock.recvfrom(65535, 10) and r[1])
92
                                parse_reply(r)
93
                        end
94

    
95
                rescue ::Interrupt
96
                        raise $!
97
                rescue ::Exception => e
98
                        print_error("Unknown error: #{e.class} #{e}")
99
                end
100

    
101
                @results.keys.each do |k|
102

    
103
                        report_service(
104
                                :host  => k,
105
                                :proto => 'udp',
106
                                :port  => datastore['RPORT'].to_i,
107
                                :name  => 'NTP'
108
                        )
109

    
110
                        report_note(
111
                                :host  => k,
112
                                :proto => 'udp',
113
                                :port  => datastore['RPORT'].to_i,
114
                                :type  => 'ntp.monlist',
115
                                :data  => {:monlist => @results[k]}
116
                        )
117

    
118
                        if (@aliases[k] and @aliases[k].keys[0] != k)
119
                                report_note(
120
                                        :host  => k,
121
                                        :proto => 'udp',
122
                                        :port  => datastore['RPORT'].to_i,
123
                                        :type  => 'ntp.addresses',
124
                                        :data  => {:addresses => @aliases[k].keys}
125
                                )
126
                        end
127

    
128
                        if (datastore['StoreNTPClients'])
129
                                print_status("#{k} Storing #{@results[k].length} NTP client hosts in the database...")
130
                                @results[k].each do |r|
131
                                        maddr,mport,mserv = r
132
                                        report_note(:host => maddr, :type => 'ntp.client.history', :data => {:address => maddr, :port => mport, :server => mserv})
133
                                end
134
                        end
135
                end
136

    
137
        end
138

    
139
        def parse_reply(pkt)
140

    
141
                # Ignore "empty" packets
142
                return if not pkt[1]
143

    
144
                if(pkt[1] =~ /^::ffff:/)
145
                        pkt[1] = pkt[1].sub(/^::ffff:/, '')
146
                end
147

    
148
                data = pkt[0]
149
                host = pkt[1]
150
                port = pkt[2]
151

    
152
                return if pkt[0].length < (72 + 16)
153
                ntp_flags, ntp_auth, ntp_vers, ntp_code = data.slice!(0,4).unpack('C*')
154
                pcnt, plen, hlen, tmp = data.slice!(0,12).unpack('nnNN')
155
                return if plen != 72
156

    
157
                idx = 0
158
                1.upto(pcnt) do
159
                        tmp1,mcnt,madd,sadd,tmp3,tmp4,mport = data[idx, plen].unpack("NNNNn3")
160
                        @results[host] ||= []
161
                        @aliases[host] ||= {}
162
                        @results[host] << [ Rex::Socket.addr_itoa(madd), mport, Rex::Socket.addr_itoa(sadd) ]
163
                        @aliases[host][Rex::Socket.addr_itoa(sadd)] = true
164
                        print_status("#{host}:#{port} #{Rex::Socket.addr_itoa(madd)}:#{mport} (#{Rex::Socket.addr_itoa(sadd)})")
165
                        idx += plen
166
                end
167
        end
168

    
169

    
170
        def probe_pkt_ntp(ip)
171
                data =
172
                        "\x17\x00\x03\x2a\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +
173
                        "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +
174
                        "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +
175
                        "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +
176
                        "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +
177
                        "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +
178
                        "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +
179
                        "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +
180
                        "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +
181
                        "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +
182
                        "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +
183
                        "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +
184
                        "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +
185
                        "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
186
                return data
187
        end
188

    
189
end
190