Created
June 30, 2025 13:01
-
-
Save mlichvar/7ef4ccce214c60122d945cc9562a88c3 to your computer and use it in GitHub Desktop.
Authenticate NTP packets in pcap file with a specified symmetric key
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/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