Skip to content

Commit 5f88971

Browse files
busterbBrent Cook
authored andcommitted
convert NTP modules to bindata
1 parent 98ffa4d commit 5f88971

File tree

11 files changed

+79
-73
lines changed

11 files changed

+79
-73
lines changed

lib/rex/proto/ntp/modes.rb

Lines changed: 58 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# -*- coding: binary -*-
22

3-
require 'bit-struct'
3+
require 'bindata'
44

55
module Rex
66
module Proto
@@ -16,24 +16,25 @@ module NTP
1616
# pages 45/48 of http://tools.ietf.org/pdf/rfc1119.pdf
1717
# http://tools.ietf.org/html/rfc1305#appendix-D
1818
# http://tools.ietf.org/html/rfc5905#page-19
19-
class NTPGeneric < BitStruct
19+
class NTPGeneric < BinData::Record
2020
# 0 1 2 3
2121
# 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
2222
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
2323
# |LI | VN | mode| Stratum | Poll | Precision |
2424
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
25-
unsigned :li, 2, default: 0
26-
unsigned :version, 3, default: 0
27-
unsigned :mode, 3, default: 0
28-
unsigned :stratum, 8, default: 0
29-
unsigned :poll, 8, default: 0
30-
unsigned :precision, 8, default: 0
31-
rest :payload
25+
endian :big
26+
bit2 :li
27+
bit3 :version
28+
bit3 :mode
29+
uint8 :stratum
30+
uint8 :poll
31+
uint8 :precision
32+
rest :payload
3233
end
3334

3435
# An NTP control message. Control messages are only specified for NTP
3536
# versions 2-4, but this is a fuzzer so why not try them all...
36-
class NTPControl < BitStruct
37+
class NTPControl < BinData::Record
3738
# 0 1 2 3
3839
# 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
3940
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
@@ -43,70 +44,73 @@ class NTPControl < BitStruct
4344
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
4445
# | offset | count |
4546
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
46-
unsigned :reserved, 2, default: 0
47-
unsigned :version, 3, default: 0
48-
unsigned :mode, 3, default: 6
49-
unsigned :response, 1, default: 0
50-
unsigned :error, 1, default: 0
51-
unsigned :more, 1, default: 0
52-
unsigned :operation, 5, default: 0
53-
unsigned :sequence, 16, default: 0
54-
unsigned :status, 16, default: 0
55-
unsigned :association_id, 16, default: 0
47+
endian :big
48+
bit2 :reserved
49+
bit3 :version
50+
bit3 :mode, initial_value: 6
51+
bit1 :response
52+
bit1 :error
53+
bit1 :more
54+
bit5 :operation
55+
uint16 :sequence
56+
uint16 :status
57+
uint16 :association_id
5658
# TODO: there *must* be bugs in the handling of these next two fields!
57-
unsigned :payload_offset, 16, default: 0
58-
unsigned :payload_size, 16, default: 0
59-
rest :payload
59+
uint16 :payload_offset
60+
uint16 :payload_size
61+
rest :payload
6062
end
6163

6264
# An NTP "private" message. Private messages are only specified for NTP
6365
# versions 2-4, but this is a fuzzer so why not try them all...
64-
class NTPPrivate < BitStruct
66+
class NTPPrivate < BinData::Record
6567
# 0 1 2 3
6668
# 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
6769
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
6870
# |R M| VN | 7 |A| Sequence | Implementation| Req code |
6971
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
7072
# | err | Number of data items | MBZ | Size of data item |
7173
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
72-
unsigned :response, 1, default: 0
73-
unsigned :more, 1, default: 0
74-
unsigned :version, 3, default: 0
75-
unsigned :mode, 3, default: 7
76-
unsigned :auth, 1, default: 0
77-
unsigned :sequence, 7, default: 0
78-
unsigned :implementation, 8, default: 0
79-
unsigned :request_code, 8, default: 0
80-
unsigned :error, 4, default: 0
81-
unsigned :record_count, 12, default: 0
82-
unsigned :mbz, 4, default: 0
83-
unsigned :record_size, 12, default: 0
84-
rest :payload
74+
endian :big
75+
bit1 :response
76+
bit1 :more
77+
bit3 :version
78+
bit3 :mode, initial_value: 7
79+
bit1 :auth
80+
bit7 :sequence
81+
uint8 :implementation
82+
uint8 :request_code
83+
bit4 :error
84+
bit12 :record_count
85+
bit4 :mbz
86+
bit12 :record_size
87+
rest :payload
8588

8689
def records
8790
records = []
8891
1.upto(record_count) do |record_num|
89-
records << payload[record_size*(record_num-1), record_size]
92+
records << payload[record_size * (record_num - 1), record_size]
9093
end
9194
records
9295
end
9396
end
9497

95-
class NTPSymmetric < BitStruct
96-
unsigned :li, 2, default: 0
97-
unsigned :version, 3, default: 3
98-
unsigned :mode, 3, default: 0
99-
unsigned :stratum, 8, default: 0
100-
unsigned :poll, 8, default: 0
101-
unsigned :precision, 8, default: 0
102-
unsigned :root_delay, 32, default: 0
103-
unsigned :root_dispersion, 32, default: 0
104-
unsigned :reference_id, 32, default: 0
105-
unsigned :reference_timestamp, 64, default: 0
106-
unsigned :origin_timestamp, 64, default: 0
107-
unsigned :receive_timestamp, 64, default: 0
108-
unsigned :transmit_timestamp, 64, default: 0
109-
rest :payload
98+
class NTPSymmetric < BinData::Record
99+
endian :big
100+
bit2 :li
101+
bit3 :version, initial_value: 3
102+
bit3 :mode
103+
uint8 :stratum
104+
uint8 :poll
105+
uint8 :precision
106+
uint32 :root_delay
107+
uint32 :root_dispersion
108+
uint32 :reference_id
109+
uint64 :reference_timestamp
110+
uint64 :origin_timestamp
111+
uint64 :receive_timestamp
112+
uint64 :transmit_timestamp
113+
rest :payload
110114
end
111115

112116
def self.ntp_control(version, operation, payload = nil)
@@ -139,7 +143,7 @@ def self.ntp_generic(version, mode)
139143

140144
# Parses the given message and provides a description about the NTP message inside
141145
def self.describe(message)
142-
ntp = NTPGeneric.new(message)
146+
ntp = NTPGeneric.new.read(message)
143147
"#{message.size}-byte version #{ntp.version} mode #{ntp.mode} reply"
144148
end
145149
end

modules/auxiliary/fuzzers/ntp/ntp_protocol_fuzzer.rb

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ def fuzz_private(host)
116116
print_status("#{host}:#{rport} fuzzing version #{version} private messages (mode 7)")
117117
@mode_7_implementations.each do |implementation|
118118
@mode_7_request_codes.each do |request_code|
119-
request = Rex::Proto::NTP.ntp_private(version, implementation, request_code, "\x00" * 188)
119+
request = Rex::Proto::NTP.ntp_private(version, implementation, request_code, "\0" * 188)
120120
what = "#{request.size}-byte version #{version} mode 7 imp #{implementation} req #{request_code} message"
121121
vprint_status("#{host}:#{rport} probing with #{request.size}-byte #{what}")
122122
responses = probe(host, datastore['RPORT'].to_i, request)
@@ -177,6 +177,7 @@ def fuzz_version_mode(host, short)
177177

178178
# Sends +message+ to +host+ on UDP port +port+, returning all replies
179179
def probe(host, port, message)
180+
message = message.to_binary_s if message.respond_to?('to_binary_s')
180181
replies = []
181182
begin
182183
udp_sock.sendto(message, host, port, 0)
@@ -194,14 +195,15 @@ def probe(host, port, message)
194195
def handle_responses(host, request, responses, what)
195196
problems = []
196197
descriptions = []
198+
request = request.to_binary_s if request.respond_to?('to_binary_s')
197199
responses.select! { |r| r[1] }
198200
return if responses.empty?
199201
responses.each do |response|
200202
data = response[0]
201203
descriptions << Rex::Proto::NTP.describe(data)
202204
problems << 'large response' if request.size < data.size
203-
ntp_req = Rex::Proto::NTP::NTPGeneric.new(request)
204-
ntp_resp = Rex::Proto::NTP::NTPGeneric.new(data)
205+
ntp_req = Rex::Proto::NTP::NTPGeneric.new.read(request)
206+
ntp_resp = Rex::Proto::NTP::NTPGeneric.new.read(data)
205207
problems << 'version mismatch' if ntp_req.version != ntp_resp.version
206208
end
207209

modules/auxiliary/scanner/ntp/ntp_monlist.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,15 +49,15 @@ def initialize
4949
# Called for each response packet
5050
def scanner_process(data, shost, sport)
5151
@results[shost] ||= { messages: [], peers: [] }
52-
@results[shost][:messages] << Rex::Proto::NTP::NTPPrivate.new(data)
52+
@results[shost][:messages] << Rex::Proto::NTP::NTPPrivate.new.read(data)
5353
@results[shost][:peers] << extract_peer_tuples(data)
5454
end
5555

5656
# Called before the scan block
5757
def scanner_prescan(batch)
5858
@results = {}
5959
@aliases = {}
60-
@probe = Rex::Proto::NTP.ntp_private(datastore['VERSION'], datastore['IMPLEMENTATION'], 42)
60+
@probe = Rex::Proto::NTP.ntp_private(datastore['VERSION'], datastore['IMPLEMENTATION'], 42, "\0" * 40)
6161
end
6262

6363
# Called after the scan block

modules/auxiliary/scanner/ntp/ntp_nak_to_the_future.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,11 +83,11 @@ def check
8383
probe = build_crypto_nak(canary_timestamp)
8484
udp_sock.put(probe)
8585

86-
expected_length = probe.length - probe.payload.length
86+
expected_length = probe.to_binary_s.length - probe.payload.length
8787
response = udp_sock.timed_read(expected_length)
8888
disconnect_udp
8989
if response.length == expected_length
90-
ntp_symmetric = Rex::Proto::NTP::NTPSymmetric.new(response)
90+
ntp_symmetric = Rex::Proto::NTP::NTPSymmetric.new.read(response)
9191
if ntp_symmetric.mode == 2 && ntp_symmetric.origin_timestamp == canary_timestamp
9292
vprint_good("#{rhost}:#{rport} - NTP - VULNERABLE: Accepted a NTP symmetric active association")
9393
report_vuln(

modules/auxiliary/scanner/ntp/ntp_peer_list_dos.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ def scanner_prescan(batch)
4343
# Called for each response packet
4444
def scanner_process(data, shost, sport)
4545
@results[shost] ||= []
46-
@results[shost] << Rex::Proto::NTP::NTPPrivate.new(data)
46+
@results[shost] << Rex::Proto::NTP::NTPPrivate.new.read(data)
4747
end
4848

4949
# Called after the scan block

modules/auxiliary/scanner/ntp/ntp_peer_list_sum_dos.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ def initialize
3737
# Called for each response packet
3838
def scanner_process(data, shost, sport)
3939
@results[shost] ||= []
40-
@results[shost] << Rex::Proto::NTP::NTPPrivate.new(data)
40+
@results[shost] << Rex::Proto::NTP::NTPPrivate.new.read(data)
4141
end
4242

4343
# Called before the scan block

modules/auxiliary/scanner/ntp/ntp_readvar.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ def initialize(info = {})
3636

3737
def scanner_process(data, shost, _sport)
3838
@results[shost] ||= []
39-
@results[shost] << Rex::Proto::NTP::NTPControl.new(data)
39+
@results[shost] << Rex::Proto::NTP::NTPControl.new.read(data)
4040
end
4141

4242
def scan_host(ip)

modules/auxiliary/scanner/ntp/ntp_req_nonce_dos.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ def initialize
3838
# Called for each response packet
3939
def scanner_process(data, shost, sport)
4040
@results[shost] ||= []
41-
@results[shost] << Rex::Proto::NTP::NTPControl.new(data)
41+
@results[shost] << Rex::Proto::NTP::NTPControl.new.read(data)
4242
end
4343

4444
# Called before the scan block

modules/auxiliary/scanner/ntp/ntp_reslist_dos.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ def initialize
3939
# Called for each response packet
4040
def scanner_process(data, shost, sport)
4141
@results[shost] ||= []
42-
@results[shost] << Rex::Proto::NTP::NTPPrivate.new(data)
42+
@results[shost] << Rex::Proto::NTP::NTPPrivate.new.read(data)
4343
end
4444

4545
# Called before the scan block

modules/auxiliary/scanner/ntp/ntp_unsettrap_dos.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ def initialize
3737
# Called for each response packet
3838
def scanner_process(data, shost, sport)
3939
@results[shost] ||= []
40-
@results[shost] << Rex::Proto::NTP::NTPControl.new(data)
40+
@results[shost] << Rex::Proto::NTP::NTPControl.new.read(data)
4141
end
4242

4343
# Called before the scan block

0 commit comments

Comments
 (0)