Skip to content

Commit f475b9d

Browse files
authored
Merge pull request rapid7#19749 from zeroSteiner/fix/mod/ntp_nak_to_the_future
Fix ntp_nak_to_the_future
2 parents dac7c39 + e5e0657 commit f475b9d

File tree

3 files changed

+109
-40
lines changed

3 files changed

+109
-40
lines changed
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
## Vulnerable Application
2+
3+
## Verification Steps
4+
5+
1. Use the supplied Dockerfile to start a vulnerable instance of the application
6+
1. Build it with: `docker build -t ntpd:4.2.8p3 .`
7+
1. Run it with: `docker run --rm -it --name ntp-server -p 123:123/udp ntpd:4.2.8p3`
8+
1. Start `msfconsole` and use the module
9+
1. Set the `RHOSTS` value as necessary
10+
1. Run the module and see that the target is vulnerable
11+
12+
### Dockerfile
13+
Use this as `ntp.conf`:
14+
15+
```
16+
# Basic NTP configuration
17+
server 0.pool.ntp.org iburst
18+
server 1.pool.ntp.org iburst
19+
server 2.pool.ntp.org iburst
20+
server 3.pool.ntp.org iburst
21+
22+
driftfile /var/lib/ntp/ntp.drift
23+
24+
# Enable authentication for secure associations
25+
enable auth
26+
27+
# Define trusted keys
28+
trustedkey 1
29+
30+
# Open restrictions for all clients on the local network (example: 192.168.0.0/16)
31+
restrict default kod nomodify notrap
32+
restrict 127.0.0.1
33+
restrict ::1
34+
restrict 192.168.0.0 mask 255.255.0.0 autokey
35+
36+
# Uncomment to allow all clients (use cautiously)
37+
# restrict default kod nomodify notrap
38+
```
39+
40+
Use this as `Dockerfile`:
41+
42+
```
43+
ARG version=4.2.8p3
44+
FROM ubuntu:16.04
45+
ARG version
46+
47+
# Install dependencies
48+
RUN apt-get update && apt-get install -y \
49+
wget \
50+
build-essential \
51+
libcap-dev \
52+
libssl-dev && \
53+
apt-get clean
54+
55+
# Download and build NTPD
56+
WORKDIR /tmp
57+
RUN wget https://web.archive.org/web/20240608062853/https://www.eecis.udel.edu/~ntp/ntp_spool/ntp4/ntp-4.2/ntp-$version.tar.gz && \
58+
tar -xzf ntp-$version.tar.gz && \
59+
cd ntp-$version && \
60+
./configure --prefix=/usr/local --enable-linuxcaps && \
61+
make && \
62+
make install && \
63+
cd .. && \
64+
rm -rf ntp-$version*
65+
66+
# Add configuration file
67+
COPY ntp.conf /etc/ntp.conf
68+
69+
# Expose NTP port (123)
70+
EXPOSE 123/udp
71+
72+
# Run ntpd
73+
ENTRYPOINT ["/usr/local/bin/ntpd"]
74+
CMD ["-g", "-d", "-d"]
75+
```
76+
77+
## Options
78+
79+
## Scenarios
80+
81+
### Ubuntu 16.04 NTPd 4.2.8p3
82+
83+
```
84+
metasploit-framework (S:0 J:0) auxiliary(scanner/ntp/ntp_nak_to_the_future) > set RHOSTS 192.168.159.128, 192.168.159.10
85+
RHOSTS => 192.168.159.128, 192.168.159.10
86+
metasploit-framework (S:0 J:0) auxiliary(scanner/ntp/ntp_nak_to_the_future) > run
87+
[+] 192.168.159.128:123 - NTP - VULNERABLE: Accepted a NTP symmetric active association
88+
[*] Scanned 1 of 2 hosts (50% complete)
89+
[*] Scanned 1 of 2 hosts (50% complete)
90+
[*] Scanned 1 of 2 hosts (50% complete)
91+
[*] Scanned 1 of 2 hosts (50% complete)
92+
[*] Scanned 1 of 2 hosts (50% complete)
93+
[*] Scanned 2 of 2 hosts (100% complete)
94+
[*] Auxiliary module execution completed
95+
metasploit-framework (S:0 J:0) auxiliary(scanner/ntp/ntp_nak_to_the_future) >
96+
```

lib/rex/proto/ntp/modes.rb

Lines changed: 0 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -98,25 +98,6 @@ def records
9898
end
9999
end
100100

101-
class NTPSymmetric < BinData::Record
102-
alias size num_bytes
103-
endian :big
104-
bit2 :li
105-
bit3 :version, initial_value: 3
106-
bit3 :mode
107-
uint8 :stratum
108-
uint8 :poll
109-
uint8 :precision
110-
uint32 :root_delay
111-
uint32 :root_dispersion
112-
uint32 :reference_id
113-
uint64 :reference_timestamp
114-
uint64 :origin_timestamp
115-
uint64 :receive_timestamp
116-
uint64 :transmit_timestamp
117-
rest :payload
118-
end
119-
120101
def ntp_control(version, operation, payload = nil)
121102
n = NTPControl.new
122103
n.version = version

modules/auxiliary/scanner/ntp/ntp_nak_to_the_future.rb

Lines changed: 13 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@ class MetasploitModule < Msf::Auxiliary
77
include Msf::Auxiliary::Report
88
include Msf::Auxiliary::Scanner
99
include Msf::Exploit::Remote::Udp
10-
include Msf::Auxiliary::NTP
10+
11+
SYMMETRIC_ACTIVE_MODE = Rex::Proto::NTP::Constants::Mode::SYMMETRIC_ACTIVE
12+
SYMMETRIC_PASSIVE_MODE = Rex::Proto::NTP::Constants::Mode::SYMMETRIC_PASSIVE
1113

1214
def initialize(info = {})
1315
super(
@@ -39,26 +41,16 @@ def initialize(info = {})
3941
]
4042
)
4143
)
42-
43-
register_options(
44-
[
45-
OptInt.new('OFFSET', [true, "Offset from local time, in seconds", 300])
46-
])
4744
end
4845

4946
def build_crypto_nak(time)
50-
probe = Rex::Proto::NTP::NTPSymmetric.new
47+
probe = Rex::Proto::NTP::Header::NTPHeader.new
48+
probe.version_number = 3
5149
probe.stratum = 1
5250
probe.poll = 10
53-
probe.mode = 1
51+
probe.mode = SYMMETRIC_ACTIVE_MODE
5452
unless time
55-
now = Time.now
56-
# compute the timestamp. NTP stores a timestamp as 64-bit unsigned
57-
# integer, the high 32-bits representing the number of seconds since era
58-
# epoch and the low 32-bits representing the fraction of a second. The era
59-
# epoch in this case is Jan 1 1900, so we must add the number of seconds
60-
# between then and the ruby era epoch, Jan 1 1970, which is 2208988800
61-
time = ((now.to_i + 2208988800 + datastore['OFFSET']) << 32) + now.nsec
53+
time = Time.now
6254
end
6355

6456
# TODO: use different values for each?
@@ -67,24 +59,24 @@ def build_crypto_nak(time)
6759
probe.receive_timestamp = time
6860
probe.transmit_timestamp = time
6961
# key-id 0
70-
probe.payload = "\x00\x00\x00\x00"
62+
probe.key_identifier = 0
7163
probe
7264
end
7365

7466
def check
7567
connect_udp
7668

7769
# pick a random 64-bit timestamp
78-
canary_timestamp = rand((2**32)..((2**64) - 1))
70+
canary_timestamp = Time.now.utc - (60 * 5)
7971
probe = build_crypto_nak(canary_timestamp)
80-
udp_sock.put(probe)
72+
udp_sock.put(probe.to_binary_s)
8173

82-
expected_length = probe.to_binary_s.length - probe.payload.length
74+
expected_length = probe.offset_of(probe.key_identifier)
8375
response = udp_sock.timed_read(expected_length)
8476
disconnect_udp
8577
if response.length == expected_length
86-
ntp_symmetric = Rex::Proto::NTP::NTPSymmetric.new.read(response)
87-
if ntp_symmetric.mode == 2 && ntp_symmetric.origin_timestamp == canary_timestamp
78+
ntp_symmetric = Rex::Proto::NTP::Header::NTPHeader.read(response)
79+
if ntp_symmetric.mode == SYMMETRIC_PASSIVE_MODE && ntp_symmetric.origin_timestamp == nil
8880
vprint_good("#{rhost}:#{rport} - NTP - VULNERABLE: Accepted a NTP symmetric active association")
8981
report_vuln(
9082
host: rhost,

0 commit comments

Comments
 (0)