@@ -29,6 +29,7 @@ def initialize
29
29
* Short, invalid datagrams
30
30
* Full-size, random datagrams
31
31
* All possible NTP control messages
32
+ * All possible NTP private messages
32
33
33
34
This findings of this fuzzer are not necessarily indicative of bugs,
34
35
let alone vulnerabilities, rather they point out interesting things
@@ -80,7 +81,7 @@ class NTPGeneric < BitStruct
80
81
char :payload , 352
81
82
end
82
83
83
- # An NTP control message. Control packets are only specified for NTP
84
+ # An NTP control message. Control messages are only specified for NTP
84
85
# versions 2-4, but this is a fuzzer so why not try them all...
85
86
class NTPControl < BitStruct
86
87
# 0 1 2 3
@@ -108,6 +109,26 @@ class NTPControl < BitStruct
108
109
rest :payload
109
110
end
110
111
112
+ # An NTP "private" message. Private messages are only specified for NTP
113
+ # versions 2-4, but this is a fuzzer so why not try them all...
114
+ class NTPPrivate < BitStruct
115
+ # 0 1 2 3
116
+ # 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
117
+ # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
118
+ # |00 | VN | 7 |A| Sequence |
119
+ # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
120
+ # | Implementation| request code |
121
+ # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
122
+ unsigned :reserved , 2 , default : 0
123
+ unsigned :version , 3 , default : 0
124
+ unsigned :mode , 3 , default : 7
125
+ unsigned :auth , 1 , default : 0
126
+ unsigned :sequence , 7 , default : 0
127
+ unsigned :implementation , 8 , default : 0
128
+ unsigned :request_code , 8 , default : 0
129
+ rest :payload
130
+ end
131
+
111
132
def build_ntp_control ( version , operation , payload = nil )
112
133
n = NTPControl . new
113
134
n . version = version
@@ -120,6 +141,15 @@ def build_ntp_control(version, operation, payload = nil)
120
141
n . to_s
121
142
end
122
143
144
+ def build_ntp_private ( version , implementation , request_code , payload = nil )
145
+ n = NTPPrivate . new
146
+ n . version = version
147
+ n . implementation = implementation
148
+ n . request_code = request_code
149
+ n . payload = payload if payload
150
+ n . to_s
151
+ end
152
+
123
153
def build_ntp_generic ( version , mode )
124
154
n = NTPGeneric . new
125
155
n . version = version
@@ -142,11 +172,12 @@ def run
142
172
fail "Unsupported NTP modes: #{ unsupported_modes } " unless unsupported_modes . empty?
143
173
144
174
connect_udp
145
- fuzz_version_mode ( rhost )
146
- fuzz_version_mode ( rhost , true )
147
- fuzz_short ( rhost )
148
- fuzz_random ( rhost )
149
- fuzz_control ( rhost ) if @modes . include? ( 6 )
175
+ fuzz_version_mode ( host )
176
+ fuzz_version_mode ( host , true )
177
+ fuzz_short ( host )
178
+ fuzz_random ( host )
179
+ fuzz_control ( host ) if @modes . include? ( 6 )
180
+ fuzz_private ( host ) if @modes . include? ( 7 )
150
181
disconnect_udp
151
182
end
152
183
@@ -156,7 +187,7 @@ def fuzz_control(host)
156
187
@versions . map { |v | v . to_i } . each do |version |
157
188
0 . upto ( 31 ) do |op |
158
189
request = build_ntp_control ( version , op )
159
- what = "#{ request . size } -byte version #{ version } mode 6 op #{ op } packet "
190
+ what = "#{ request . size } -byte version #{ version } mode 6 op #{ op } message "
160
191
vprint_status ( "#{ host } :#{ rport } probing with #{ request . size } -byte #{ what } " )
161
192
probe ( host , datastore [ 'RPORT' ] . to_i , request ) . each do |reply |
162
193
handle_response ( host , request , reply , what )
@@ -166,12 +197,30 @@ def fuzz_control(host)
166
197
end
167
198
end
168
199
200
+ # Sends a series of NTP private messages
201
+ def fuzz_private ( host )
202
+ print_status ( "#{ host } :#{ rport } fuzzing private messages (mode 7)" )
203
+ @versions . map { |v | v . to_i } . each do |version |
204
+ 0 . upto ( 255 ) do |implementation |
205
+ 0 . upto ( 255 ) do |request_code |
206
+ request = build_ntp_private ( version , implementation , request_code )
207
+ what = "#{ request . size } -byte version #{ version } mode 7 imp #{ implementation } req #{ request_code } message"
208
+ 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
212
+ Rex . sleep ( sleep_time )
213
+ end
214
+ end
215
+ end
216
+ end
217
+
169
218
# Sends a series of small, short datagrams, looking for a reply
170
219
def fuzz_short ( host )
171
220
print_status ( "#{ host } :#{ rport } fuzzing short messages" )
172
221
0 . upto ( 4 ) do |size |
173
222
request = SecureRandom . random_bytes ( size )
174
- what = "Short #{ request . size } -byte random packet "
223
+ what = "Short #{ request . size } -byte random message "
175
224
vprint_status ( "#{ host } :#{ rport } probing with #{ what } " )
176
225
probe ( host , datastore [ 'RPORT' ] . to_i , request ) . each do |reply |
177
226
handle_response ( host , request , reply , what )
@@ -185,7 +234,7 @@ def fuzz_random(host)
185
234
print_status ( "#{ host } :#{ rport } fuzzing random messages" )
186
235
0 . upto ( 5 ) do
187
236
request = SecureRandom . random_bytes ( 48 )
188
- what = "random #{ request . size } -byte packet "
237
+ what = "random #{ request . size } -byte message "
189
238
vprint_status ( "#{ host } :#{ rport } probing with #{ what } " )
190
239
probe ( host , datastore [ 'RPORT' ] . to_i , request ) . each do |reply |
191
240
handle_response ( host , request , reply , what )
@@ -201,7 +250,7 @@ def fuzz_version_mode(host, short=false)
201
250
@modes . map { |m | m . to_i } . each do |mode |
202
251
request = build_ntp_generic ( version , mode )
203
252
request = request [ 0 , 4 ] if short
204
- what = "#{ request . size } -byte #{ short ? 'short ' : nil } version #{ version } mode #{ mode } packet "
253
+ what = "#{ request . size } -byte #{ short ? 'short ' : nil } version #{ version } mode #{ mode } message "
205
254
vprint_status ( "#{ host } :#{ rport } probing with #{ what } " )
206
255
probe ( host , datastore [ 'RPORT' ] . to_i , request ) . each do |reply |
207
256
handle_response ( host , request , reply , what )
@@ -211,20 +260,20 @@ def fuzz_version_mode(host, short=false)
211
260
end
212
261
end
213
262
214
- # Sends +packet + to +host+ on UDP port +port+, returning all replies
215
- def probe ( host , port , packet )
263
+ # Sends +message + to +host+ on UDP port +port+, returning all replies
264
+ def probe ( host , port , message )
216
265
replies = [ ]
217
- udp_sock . sendto ( packet , host , port , 0 )
266
+ udp_sock . sendto ( message , host , port , 0 )
218
267
while ( r = udp_sock . recvfrom ( 65535 , datastore [ 'WAIT' ] / 1000.0 ) and r [ 1 ] )
219
268
replies << r
220
269
end
221
270
replies
222
271
end
223
272
224
- # Parses the given packet and provides a description about the NTP message inside
225
- def describe ( packet )
226
- ntp = NTPGeneric . new ( packet )
227
- "#{ packet . size } -byte version #{ ntp . version } mode #{ ntp . mode } reply"
273
+ # Parses the given message and provides a description about the NTP message inside
274
+ def describe ( message )
275
+ ntp = NTPGeneric . new ( message )
276
+ "#{ message . size } -byte version #{ ntp . version } mode #{ ntp . mode } reply"
228
277
end
229
278
230
279
def handle_response ( host , request , response , what )
0 commit comments