Skip to content

Instantly share code, notes, and snippets.

@mlichvar
Created June 30, 2025 13:01
Show Gist options
  • Select an option

  • Save mlichvar/7ef4ccce214c60122d945cc9562a88c3 to your computer and use it in GitHub Desktop.

Select an option

Save mlichvar/7ef4ccce214c60122d945cc9562a88c3 to your computer and use it in GitHub Desktop.
Authenticate NTP packets in pcap file with a specified symmetric key
#!/usr/bin/python3
# Try to authenticate NTP packets in a pcap file with a symmetric
# MD5, SHA1, or SHA256 key specified in the chrony ASCII or HEX syntax
from scapy.all import *
import datetime
import hashlib
import sys
if len(sys.argv) != 3:
print(f"Usage: {sys.argv[0]} PCAP [ASCII:|HEX:]KEY")
sys.exit(1)
pcap_file = sys.argv[1]
key = sys.argv[2]
if key.startswith('ASCII:'):
key = bytes(key[6:], encoding='ASCII')
elif key.startswith('HEX:'):
key = bytes.fromhex(key[4:])
else:
key = bytes(key, encoding='ASCII')
for p in PcapReader(sys.argv[1]):
if UDP not in p or (p[UDP].sport != 123 and p[UDP].dport != 123):
continue
print(datetime.datetime.fromtimestamp(int(p.time)), end=': ')
ntp_msg = bytes(p[UDP].payload)
if IP in p:
print(f"{p[IP].src:15} -> {p[IP].dst:15}", end=' ')
elif IPv6 in p:
print(f"{p[IPv6].src:30} -> {p[IPv6].dst:30}", end=' ')
else:
print()
continue
if len(ntp_msg) < 48:
print("Invalid NTP message")
continue
ntp_ver = (ntp_msg[0] >> 3) & 7
auth_len = len(ntp_msg) - 48
print(f"NTPv{ntp_ver}", end=' ')
if ntp_ver > 4:
print("Not supported")
continue
if auth_len == 0:
print("No MAC")
continue
elif auth_len == 4:
print("Crypto NAK")
continue
elif ntp_ver == 4 and auth_len not in (20, 24):
print("extension field or non-RFC7822 MAC")
continue
elif ntp_ver < 4 and auth_len not in (20, 24, 36):
print("Unknown MAC type")
continue
key_id = struct.unpack("!I", ntp_msg[48:52])[0]
print(f"MAC len={auth_len} id={key_id}", end=' ')
data = key + ntp_msg[:48]
for key_type, digest in \
(('MD5', hashlib.md5(data).digest()),
('SHA1', hashlib.sha1(data).digest()),
('full-length SHA256', hashlib.sha256(data).digest()),
('truncated SHA256', hashlib.sha256(data).digest()[:20])):
if digest == ntp_msg[52:]:
print(f"Auth passed with {key_type}")
break
else:
print("Auth failed")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment