Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix ntp_nak_to_the_future #19749

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
## Vulnerable Application

## Verification Steps

1. Use the supplied Dockerfile to start a vulnerable instance of the application
1. Build it with: `docker build -t ntpd:4.2.8p3 .`
1. Run it with: `docker run --rm -it --name ntp-server -p 123:123/udp ntpd:4.2.8p3`
1. Start `msfconsole` and use the module
1. Set the `RHOSTS` value as necessary
1. Run the module and see that the target is vulnerable

### Dockerfile
Use this as `ntp.conf`:
```
# Basic NTP configuration
server 0.pool.ntp.org iburst
server 1.pool.ntp.org iburst
server 2.pool.ntp.org iburst
server 3.pool.ntp.org iburst

driftfile /var/lib/ntp/ntp.drift

# Enable authentication for secure associations
enable auth

# Define trusted keys
trustedkey 1

# Open restrictions for all clients on the local network (example: 192.168.0.0/16)
restrict default kod nomodify notrap
restrict 127.0.0.1
restrict ::1
restrict 192.168.0.0 mask 255.255.0.0 autokey

# Uncomment to allow all clients (use cautiously)
# restrict default kod nomodify notrap
```

Use this as `Dockerfile`:
```
ARG version=4.2.8p3
FROM ubuntu:16.04
ARG version

# Install dependencies
RUN apt-get update && apt-get install -y \
wget \
build-essential \
libcap-dev \
libssl-dev && \
apt-get clean

# Download and build NTPD
WORKDIR /tmp
RUN wget https://www.eecis.udel.edu/~ntp/ntp_spool/ntp4/ntp-4.2/ntp-$version.tar.gz && \
tar -xzf ntp-$version.tar.gz && \
cd ntp-$version && \
./configure --prefix=/usr/local --enable-linuxcaps && \
make && \
make install && \
cd .. && \
rm -rf ntp-$version*

# Add configuration file
COPY ntp.conf /etc/ntp.conf

# Expose NTP port (123)
EXPOSE 123/udp

# Run ntpd
ENTRYPOINT ["/usr/local/bin/ntpd"]
CMD ["-g", "-d", "-d"]
```

## Options

## Scenarios

### Ubuntu 16.04 NTPd 4.2.8p3

```
metasploit-framework (S:0 J:0) auxiliary(scanner/ntp/ntp_nak_to_the_future) > set RHOSTS 192.168.159.128, 192.168.159.10
RHOSTS => 192.168.159.128, 192.168.159.10
metasploit-framework (S:0 J:0) auxiliary(scanner/ntp/ntp_nak_to_the_future) > run
[+] 192.168.159.128:123 - NTP - VULNERABLE: Accepted a NTP symmetric active association
[*] Scanned 1 of 2 hosts (50% complete)
[*] Scanned 1 of 2 hosts (50% complete)
[*] Scanned 1 of 2 hosts (50% complete)
[*] Scanned 1 of 2 hosts (50% complete)
[*] Scanned 1 of 2 hosts (50% complete)
[*] Scanned 2 of 2 hosts (100% complete)
[*] Auxiliary module execution completed
metasploit-framework (S:0 J:0) auxiliary(scanner/ntp/ntp_nak_to_the_future) >
```
19 changes: 0 additions & 19 deletions lib/rex/proto/ntp/modes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -98,25 +98,6 @@ def records
end
end

class NTPSymmetric < BinData::Record
alias size num_bytes
endian :big
bit2 :li
bit3 :version, initial_value: 3
bit3 :mode
uint8 :stratum
uint8 :poll
uint8 :precision
uint32 :root_delay
uint32 :root_dispersion
uint32 :reference_id
uint64 :reference_timestamp
uint64 :origin_timestamp
uint64 :receive_timestamp
uint64 :transmit_timestamp
rest :payload
end

def ntp_control(version, operation, payload = nil)
n = NTPControl.new
n.version = version
Expand Down
34 changes: 13 additions & 21 deletions modules/auxiliary/scanner/ntp/ntp_nak_to_the_future.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ class MetasploitModule < Msf::Auxiliary
include Msf::Auxiliary::Report
include Msf::Auxiliary::Scanner
include Msf::Exploit::Remote::Udp
include Msf::Auxiliary::NTP

SYMMETRIC_ACTIVE_MODE = Rex::Proto::NTP::Constants::Mode::SYMMETRIC_ACTIVE
SYMMETRIC_PASSIVE_MODE = Rex::Proto::NTP::Constants::Mode::SYMMETRIC_PASSIVE

def initialize(info = {})
super(
Expand Down Expand Up @@ -39,26 +41,16 @@ def initialize(info = {})
]
)
)

register_options(
[
OptInt.new('OFFSET', [true, "Offset from local time, in seconds", 300])
])
end

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

# TODO: use different values for each?
Expand All @@ -67,24 +59,24 @@ def build_crypto_nak(time)
probe.receive_timestamp = time
probe.transmit_timestamp = time
# key-id 0
probe.payload = "\x00\x00\x00\x00"
probe.key_identifier = 0
probe
end

def check
connect_udp

# pick a random 64-bit timestamp
canary_timestamp = rand((2**32)..((2**64) - 1))
canary_timestamp = Time.now.utc - (60 * 5)
probe = build_crypto_nak(canary_timestamp)
udp_sock.put(probe)
udp_sock.put(probe.to_binary_s)

expected_length = probe.to_binary_s.length - probe.payload.length
expected_length = probe.offset_of(probe.key_identifier)
response = udp_sock.timed_read(expected_length)
disconnect_udp
if response.length == expected_length
ntp_symmetric = Rex::Proto::NTP::NTPSymmetric.new.read(response)
if ntp_symmetric.mode == 2 && ntp_symmetric.origin_timestamp == canary_timestamp
ntp_symmetric = Rex::Proto::NTP::Header::NTPHeader.read(response)
if ntp_symmetric.mode == SYMMETRIC_PASSIVE_MODE && ntp_symmetric.origin_timestamp == nil
vprint_good("#{rhost}:#{rport} - NTP - VULNERABLE: Accepted a NTP symmetric active association")
report_vuln(
host: rhost,
Expand Down
Loading