@@ -13,8 +13,10 @@ class Metasploit3 < Msf::Auxiliary
13
13
include Msf ::Exploit ::Remote ::Udp
14
14
include Msf ::Auxiliary ::Scanner
15
15
16
- NTP_VERSIONS = ( 0 ..7 ) . to_a
17
- NTP_MODES = ( 0 ..7 ) . to_a
16
+ NTP_SUPPORTED_VERSIONS = ( 0 ..7 ) . to_a
17
+ NTP_SUPPORTED_MODES = ( 0 ..7 ) . to_a
18
+ NTP_SUPPORTED_MODE_7_IMPLEMENTATIONS = ( 0 ..255 ) . to_a
19
+ NTP_SUPPORTED_MODE_7_REQUEST_CODES = ( 0 ..255 ) . to_a
18
20
19
21
def initialize
20
22
super (
@@ -49,8 +51,10 @@ def initialize
49
51
register_options (
50
52
[
51
53
Opt ::RPORT ( 123 ) ,
52
- OptString . new ( 'VERSIONS' , [ true , 'Versions to fuzz' , NTP_VERSIONS . join ( ',' ) ] ) ,
53
- OptString . new ( 'MODES' , [ true , 'Versions to fuzz' , NTP_MODES . join ( ',' ) ] ) ,
54
+ OptString . new ( 'VERSIONS' , [ true , 'Versions to fuzz' , [ 3 , 2 , 4 ] ] ) ,
55
+ OptString . new ( 'MODES' , [ true , 'Modes to fuzz' , NTP_SUPPORTED_MODES ] ) ,
56
+ OptString . new ( 'MODE_7_IMPLEMENTATIONS' , [ true , 'Mode 7 implementations to fuzz' , [ 3 , 2 , 0 ] ] ) ,
57
+ OptString . new ( 'MODE_7_REQUEST_CODES' , [ true , 'Mode 7 request codes to fuzz' , ( 0 ..45 ) . to_a ] ) ,
54
58
OptInt . new ( 'SLEEP' , [ true , 'Sleep for this many ms between requests' , 0 ] ) ,
55
59
OptInt . new ( 'WAIT' , [ true , 'Wait this many ms for responses' , 500 ] )
56
60
] , self . class )
@@ -164,12 +168,20 @@ def sleep_time
164
168
def run_host ( ip )
165
169
# parse and sanity check versions
166
170
@versions = datastore [ 'VERSIONS' ] . split ( /[^\d ]/ ) . select { |v | !v . empty? } . map { |v | v . to_i }
167
- unsupported_versions = @versions - NTP_VERSIONS
171
+ unsupported_versions = @versions - NTP_SUPPORTED_VERSIONS
168
172
fail "Unsupported NTP versions: #{ unsupported_versions } " unless unsupported_versions . empty?
169
173
# parse and sanity check modes
170
174
@modes = datastore [ 'MODES' ] . split ( /[^\d ]/ ) . select { |m | !m . empty? } . map { |v | v . to_i }
171
- unsupported_modes = @modes - NTP_MODES
175
+ unsupported_modes = @modes - NTP_SUPPORTED_MODES
172
176
fail "Unsupported NTP modes: #{ unsupported_modes } " unless unsupported_modes . empty?
177
+ # parse and sanity check mode 7 implementations
178
+ @implementations = datastore [ 'MODE_7_IMPLEMENTATIONS' ] . split ( /[^\d ]/ ) . select { |m | !m . empty? } . map { |v | v . to_i }
179
+ unsupported_implementations = @implementations - NTP_SUPPORTED_MODE_7_IMPLEMENTATIONS
180
+ fail "Unsupported NTP mode 7implementations: #{ unsupported_implementations } " unless unsupported_implementations . empty?
181
+ # parse and sanity check mode 7 REQUEST_CODES
182
+ @request_codes = datastore [ 'MODE_7_REQUEST_CODES' ] . split ( /[^\d ]/ ) . select { |m | !m . empty? } . map { |v | v . to_i }
183
+ unsupported_request_codes = @request_codes - NTP_SUPPORTED_MODE_7_REQUEST_CODES
184
+ fail "Unsupported NTP mode 7 request codes: #{ unsupported_request_codes } " unless unsupported_request_codes . empty?
173
185
174
186
connect_udp
175
187
fuzz_version_mode ( ip )
@@ -183,32 +195,30 @@ def run_host(ip)
183
195
184
196
# Sends a series of NTP control messages
185
197
def fuzz_control ( host )
186
- @versions . map { | v | v . to_i } . each do |version |
198
+ @versions . each do |version |
187
199
print_status ( "#{ host } :#{ rport } fuzzing version #{ version } control messages (mode 6)" )
188
200
0 . upto ( 31 ) do |op |
189
201
request = build_ntp_control ( version , op )
190
202
what = "#{ request . size } -byte version #{ version } mode 6 op #{ op } message"
191
203
vprint_status ( "#{ host } :#{ rport } probing with #{ request . size } -byte #{ what } " )
192
- probe ( host , datastore [ 'RPORT' ] . to_i , request ) . each do |reply |
193
- handle_response ( host , request , reply , what )
194
- end
204
+ responses = probe ( host , datastore [ 'RPORT' ] . to_i , request )
205
+ handle_responses ( host , request , responses , what )
195
206
Rex . sleep ( sleep_time )
196
207
end
197
208
end
198
209
end
199
210
200
211
# Sends a series of NTP private messages
201
212
def fuzz_private ( host )
202
- @versions . map { | v | v . to_i } . each do |version |
213
+ @versions . each do |version |
203
214
print_status ( "#{ host } :#{ rport } fuzzing version #{ version } private messages (mode 7)" )
204
- 0 . upto ( 255 ) do |implementation |
205
- 0 . upto ( 255 ) do |request_code |
206
- request = build_ntp_private ( version , implementation , request_code )
215
+ @implementations . each do |implementation |
216
+ @request_codes . each do |request_code |
217
+ request = build_ntp_private ( version , implementation , request_code , " \x00 " * 188 )
207
218
what = "#{ request . size } -byte version #{ version } mode 7 imp #{ implementation } req #{ request_code } message"
208
219
vprint_status ( "#{ host } :#{ rport } probing with #{ request . size } -byte #{ what } " )
209
- probe ( host , datastore [ 'RPORT' ] . to_i , request ) . each do |reply |
210
- handle_response ( host , request , reply , what )
211
- end
220
+ responses = probe ( host , datastore [ 'RPORT' ] . to_i , request )
221
+ handle_responses ( host , request , responses , what )
212
222
Rex . sleep ( sleep_time )
213
223
end
214
224
end
@@ -220,11 +230,10 @@ def fuzz_short(host)
220
230
print_status ( "#{ host } :#{ rport } fuzzing short messages" )
221
231
0 . upto ( 4 ) do |size |
222
232
request = SecureRandom . random_bytes ( size )
223
- what = "Short #{ request . size } -byte random message"
233
+ what = "short #{ request . size } -byte random message"
224
234
vprint_status ( "#{ host } :#{ rport } probing with #{ what } " )
225
- probe ( host , datastore [ 'RPORT' ] . to_i , request ) . each do |reply |
226
- handle_response ( host , request , reply , what )
227
- end
235
+ responses = probe ( host , datastore [ 'RPORT' ] . to_i , request )
236
+ handle_responses ( host , request , responses , what )
228
237
Rex . sleep ( sleep_time )
229
238
end
230
239
end
@@ -236,25 +245,23 @@ def fuzz_random(host)
236
245
request = SecureRandom . random_bytes ( 48 )
237
246
what = "random #{ request . size } -byte message"
238
247
vprint_status ( "#{ host } :#{ rport } probing with #{ what } " )
239
- probe ( host , datastore [ 'RPORT' ] . to_i , request ) . each do |reply |
240
- handle_response ( host , request , reply , what )
241
- end
248
+ responses = probe ( host , datastore [ 'RPORT' ] . to_i , request )
249
+ handle_responses ( host , request , responses , what )
242
250
Rex . sleep ( sleep_time )
243
251
end
244
252
end
245
253
246
254
# Sends a series of different version + mode combinations
247
255
def fuzz_version_mode ( host , short = false )
248
256
print_status ( "#{ host } :#{ rport } fuzzing #{ short ? 'short ' : nil } version and mode combinations" )
249
- @versions . map { | v | v . to_i } . each do |version |
250
- @modes . map { | m | m . to_i } . each do |mode |
257
+ @versions . each do |version |
258
+ @modes . each do |mode |
251
259
request = build_ntp_generic ( version , mode )
252
260
request = request [ 0 , 4 ] if short
253
261
what = "#{ request . size } -byte #{ short ? 'short ' : nil } version #{ version } mode #{ mode } message"
254
262
vprint_status ( "#{ host } :#{ rport } probing with #{ what } " )
255
- probe ( host , datastore [ 'RPORT' ] . to_i , request ) . each do |reply |
256
- handle_response ( host , request , reply , what )
257
- end
263
+ responses = probe ( host , datastore [ 'RPORT' ] . to_i , request )
264
+ handle_responses ( host , request , responses , what )
258
265
Rex . sleep ( sleep_time )
259
266
end
260
267
end
@@ -276,19 +283,29 @@ def describe(message)
276
283
"#{ message . size } -byte version #{ ntp . version } mode #{ ntp . mode } reply"
277
284
end
278
285
279
- def handle_response ( host , request , response , what )
280
- return unless response [ 1 ]
281
- data = response [ 0 ]
286
+ def handle_responses ( host , request , responses , what )
282
287
problems = [ ]
283
- problems << 'large response' if request . size < data . size
284
- ntp_req = NTPGeneric . new ( request )
285
- ntp_resp = NTPGeneric . new ( data )
286
- problems << 'version mismatch' if ntp_req . version != ntp_resp . version
288
+ descriptions = [ ]
289
+ responses . select! { |r | r [ 1 ] }
290
+ return if responses . empty?
291
+ responses . each do |response |
292
+ data = response [ 0 ]
293
+ descriptions << describe ( data )
294
+ problems << 'large response' if request . size < data . size
295
+ ntp_req = NTPGeneric . new ( request )
296
+ ntp_resp = NTPGeneric . new ( data )
297
+ problems << 'version mismatch' if ntp_req . version != ntp_resp . version
298
+ end
299
+
300
+ problems << 'multiple responses' if responses . size > 1
301
+ problems . sort!
302
+ problems . uniq!
287
303
304
+ description = descriptions . join ( ',' )
288
305
if problems . empty?
289
- print_status ( "#{ host } :#{ rport } -- Received #{ describe ( data ) } to #{ what } " )
306
+ vprint_status ( "#{ host } :#{ rport } -- Received ' #{ description } ' to #{ what } " )
290
307
else
291
- print_good ( "#{ host } :#{ rport } -- Received #{ describe ( data ) } to #{ what } : #{ problems . join ( ',' ) } " )
308
+ print_good ( "#{ host } :#{ rport } -- Received ' #{ description } ' to #{ what } : #{ problems . join ( ',' ) } " )
292
309
end
293
310
end
294
311
end
0 commit comments