Skip to content

Commit c7c0528

Browse files
committed
Fuzz NTP private messages too
1 parent 7ce9114 commit c7c0528

File tree

1 file changed

+66
-17
lines changed

1 file changed

+66
-17
lines changed

modules/auxiliary/fuzzers/ntp/ntp_protocol_fuzzer.rb

Lines changed: 66 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ def initialize
2929
* Short, invalid datagrams
3030
* Full-size, random datagrams
3131
* All possible NTP control messages
32+
* All possible NTP private messages
3233
3334
This findings of this fuzzer are not necessarily indicative of bugs,
3435
let alone vulnerabilities, rather they point out interesting things
@@ -80,7 +81,7 @@ class NTPGeneric < BitStruct
8081
char :payload, 352
8182
end
8283

83-
# An NTP control message. Control packets are only specified for NTP
84+
# An NTP control message. Control messages are only specified for NTP
8485
# versions 2-4, but this is a fuzzer so why not try them all...
8586
class NTPControl < BitStruct
8687
# 0 1 2 3
@@ -108,6 +109,26 @@ class NTPControl < BitStruct
108109
rest :payload
109110
end
110111

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+
111132
def build_ntp_control(version, operation, payload = nil)
112133
n = NTPControl.new
113134
n.version = version
@@ -120,6 +141,15 @@ def build_ntp_control(version, operation, payload = nil)
120141
n.to_s
121142
end
122143

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+
123153
def build_ntp_generic(version, mode)
124154
n = NTPGeneric.new
125155
n.version = version
@@ -142,11 +172,12 @@ def run
142172
fail "Unsupported NTP modes: #{unsupported_modes}" unless unsupported_modes.empty?
143173

144174
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)
150181
disconnect_udp
151182
end
152183

@@ -156,7 +187,7 @@ def fuzz_control(host)
156187
@versions.map { |v| v.to_i }.each do |version|
157188
0.upto(31) do |op|
158189
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"
160191
vprint_status("#{host}:#{rport} probing with #{request.size}-byte #{what}")
161192
probe(host, datastore['RPORT'].to_i, request).each do |reply|
162193
handle_response(host, request, reply, what)
@@ -166,12 +197,30 @@ def fuzz_control(host)
166197
end
167198
end
168199

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+
169218
# Sends a series of small, short datagrams, looking for a reply
170219
def fuzz_short(host)
171220
print_status("#{host}:#{rport} fuzzing short messages")
172221
0.upto(4) do |size|
173222
request = SecureRandom.random_bytes(size)
174-
what = "Short #{request.size}-byte random packet"
223+
what = "Short #{request.size}-byte random message"
175224
vprint_status("#{host}:#{rport} probing with #{what}")
176225
probe(host, datastore['RPORT'].to_i, request).each do |reply|
177226
handle_response(host, request, reply, what)
@@ -185,7 +234,7 @@ def fuzz_random(host)
185234
print_status("#{host}:#{rport} fuzzing random messages")
186235
0.upto(5) do
187236
request = SecureRandom.random_bytes(48)
188-
what = "random #{request.size}-byte packet"
237+
what = "random #{request.size}-byte message"
189238
vprint_status("#{host}:#{rport} probing with #{what}")
190239
probe(host, datastore['RPORT'].to_i, request).each do |reply|
191240
handle_response(host, request, reply, what)
@@ -201,7 +250,7 @@ def fuzz_version_mode(host, short=false)
201250
@modes.map { |m| m.to_i }.each do |mode|
202251
request = build_ntp_generic(version, mode)
203252
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"
205254
vprint_status("#{host}:#{rport} probing with #{what}")
206255
probe(host, datastore['RPORT'].to_i, request).each do |reply|
207256
handle_response(host, request, reply, what)
@@ -211,20 +260,20 @@ def fuzz_version_mode(host, short=false)
211260
end
212261
end
213262

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)
216265
replies = []
217-
udp_sock.sendto(packet, host, port, 0)
266+
udp_sock.sendto(message, host, port, 0)
218267
while (r = udp_sock.recvfrom(65535, datastore['WAIT'] / 1000.0) and r[1])
219268
replies << r
220269
end
221270
replies
222271
end
223272

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"
228277
end
229278

230279
def handle_response(host, request, response, what)

0 commit comments

Comments
 (0)