scapy icon indicating copy to clipboard operation
scapy copied to clipboard

NTP Authenticator still broken

Open Olegandr opened this issue 4 years ago • 12 comments

Brief description

Issue #2731 does not seem to be fixed.

Environment

  • Scapy version: 2.4.4.
  • Python version: 3.8.0
  • Operating System: Windows

How to reproduce

Use NTP packet capture with NTP Version 4 packets using SHA1, SHA256, SHA512 message authentication (produced by chrony or ntpd NTP server.) Use show() command to parse packet.

Actual result

###[ UDP ]###
        sport= ntp
        dport= ntp
        len= 80
        chksum= 0xdb3b
###[ NTPHeader ]###
           leap= unknown (clock unsynchronized)
           version= 4
           mode= client
           stratum= 0
           poll= 6
           precision= 232
           delay= 0.0
           dispersion= 0.0
           ref_id= b'INIT'
           ref= 0.0
           orig= 0.0
           recv= 0.0
           sent= Tue, 02 Jun 2020 23:37:12 +0000
###[ Authenticator ]###
              padding= '\x00\x00\x00\x0c'
              key_id= 3523582088
              dgst= b4a2adc11d2dbd1420a43560bf92c6f9

Expected result

###[ UDP ]###
        sport= ntp
        dport= ntp
        len= 80
        chksum= 0xdb3b
###[ NTPHeader ]###
           leap= unknown (clock unsynchronized)
           version= 4
           mode= client
           stratum= 0
           poll= 6
           precision= 232
           delay= 0.0
           dispersion= 0.0
           ref_id= b'INIT'
           ref= 0.0
           orig= 0.0
           recv= 0.0
           sent= Tue, 02 Jun 2020 23:37:12 +0000
###[ Authenticator ]###
              padding= '\x00\x00\x00'
              key_id= 12
              dgst= d2059888b4a2adc11d2dbd1420a43560bf92c6f9

key ID was included into the padding and portion of digest was incorrectly identified as key id.

Related resources

Olegandr avatar Dec 05 '20 06:12 Olegandr

Capture uploaded issues2999.pcap.gz

Olegandr avatar Dec 05 '20 06:12 Olegandr

I'm pretty sure this is AutoKey (https://tools.ietf.org/html/rfc5906#section-10) and that we don't support it. Feel free to contribute

gpotter2 avatar Dec 05 '20 15:12 gpotter2

No it is definitely not autokey. See linked issue.

Olegandr avatar Dec 06 '20 06:12 Olegandr

Alright, could you find an NTP RFC that mentions a Message Digest that is not 128bits, therefore matches this issue ? Thanks

gpotter2 avatar Dec 06 '20 11:12 gpotter2

Maybe this one https://www.rfc-editor.org/errata/eid3627 ? I mean those packets are created by widely-used NTP server/client implementations, are being parsed correctly by Wireshark. And no, this is not Autokey.

Olegandr avatar Dec 07 '20 05:12 Olegandr

I ran into the same problem as @Olegandr and hacked it like this:

diff --git a/scapy/layers/ntp.py b/scapy/layers/ntp.py
index 53743f9a..68cacafb 100644
--- a/scapy/layers/ntp.py
+++ b/scapy/layers/ntp.py
@@ -239,9 +239,9 @@ class NTPAuthenticator(Packet):
 
     name = "Authenticator"
     fields_desc = [
-        _NTPAuthenticatorPaddingField("padding", ""),
+        #_NTPAuthenticatorPaddingField("padding", ""),
         IntField("key_id", 0),
-        XStrFixedLenField("dgst", "", length_from=lambda x: 16)
+        XStrFixedLenField("dgst", "", length_from=lambda x: 20)
     ]
 
     def extract_padding(self, s):

The root cause is that Scapy doesn't handle properly anything other than MD5 (128 bits/16 bytes). The RFC uses MD5 as an example, but other algorithms are allowed. In my case, I was using SHA-1 which results in a 20-byte message digest.

Here's what Scapy's representation of my packet looked like without the hack (incorrect from NTPAuthenticator onwards):

<Ether  dst=xx:xx:xx:xx:xx:xx src=xx:xx:xx:xx:xx:xx type=IPv4 |<IP  version=4 ihl=5 tos=0xb8 len=100 id=16730 flags=DF frag=0 ttl=64 proto=udp chksum=0x65b1 src=192.168.8.228 dst=192.168.8.145 |<UDP  sport=ntp dport=ntp len=80 chksum=0xc545 |<NTPHeader  leap=unknown (clock unsynchronized) version=4 mode=client stratum=0 poll=6 precision=237 delay=0 dispersion=0 ref_id='INIT' ref=0 orig=0 recv=0 sent=Wed, 12 Jan 2022 03:12:22 +0000 |<NTPAuthenticator  padding='\x00\x00\x00\x0b' key_id=1285752564 dgst=16dca620855921cffc11cee2da29a3ae |>>>>>

And here's what it looked like with the hack (correct):

<Ether  dst=xx:xx:xx:xx:xx:xx src=xx:xx:xx:xx:xx:xx type=IPv4 |<IP  version=4 ihl=5 tos=0xb8 len=100 id=16730 flags=DF frag=0 ttl=64 proto=udp chksum=0x65b1 src=192.168.8.228 dst=192.168.8.145 |<UDP  sport=ntp dport=ntp len=80 chksum=0xc545 |<NTPHeader  leap=unknown (clock unsynchronized) version=4 mode=client stratum=0 poll=6 precision=237 delay=0 dispersion=0 ref_id=b'INIT' ref=0 orig=0 recv=0 sent=Wed, 12 Jan 2022 03:12:22 +0000 |<NTPAuthenticator  key_id=11 dgst=4ca306f416dca620855921cffc11cee2da29a3ae |>>>>>

Wireshark doesn't have this problem. The attached image, for example, shows Wireshark's representation of my packet above. The key_id that I'm using is indeed 11 (0xb), and the rest (20 bytes) is the message digest.

Unfortunately, I don't have time to look into this any further. I hacked the code just to be able to generate a correct graphical representation (with pdfdump()) for a university report. I hope that the Scapy developers find this information helpful and fix this properly. I know there was a previous commit claiming to fix it (https://github.com/secdev/scapy/pull/2732/files#diff-585cfb5dec6341defbe2f6643ec31bd8f41b596acb295c9213ba8181fd18fc06). However, as @Olegandr has mentioned, the problem still exists.

Furthermore, I think that there needs to be some separation in Scapy between NTPv3 and NTPv4 packets. While both protocols are compatible with each other, they differ in the stuff at the end i.e. after the transmit timestamp. For example, in NTPv4 there is no padding before the key_id unless there is an Extension Field -- if I'm not mistaken. Thus, for NTPv4 packets, as soon as the code has read the transmit timestamp, the next 4 bytes are always the key_id, and the remaining bytes are the message digest. The Scapy code seems to start at the end and work backwards, hence throwing away useful bytes into what it thinks is padding.

Screenshot from 2022-10-15 01-00-00

ghost avatar Oct 15 '22 00:10 ghost

FTR here are some information to help implementing this https://github.com/wireshark/wireshark/blob/eff7cd15b007bce10763253a131fa09d0e465206/epan/dissectors/packet-ntp.c#L1302

guedou avatar Dec 07 '22 15:12 guedou

US government standard requires that NTP be authenticated using " SHA1, SHA256, SHA384, SHA512, AES-CBC-128, AES-CBC-256 as the message digest algorithm(s)". That is why support for those fields are requested.

Olegandr avatar Dec 10 '22 06:12 Olegandr

@Olegandr do you have PCAP containing authenticators other than MD5 our SHA256?

guedou avatar Dec 10 '22 11:12 guedou

@guedou, Here is a capture with all possible digests: alldigests2.zip Packets 1,2 SHA1 Packets 3,4 SHA256 Packets 5,6 SHA512 Packets7,8 SHA384, Packet 9 AES-CMAC 128 Packet 10 AES_CMAC 256 Sorry it took so long.

Olegandr avatar Jan 11 '23 03:01 Olegandr

Thanks a lot. Sorry, I fail to provide an answer earlier while I was experimenting with these pcaps. #3821 parses them correctly yet introduces other issues. Can you tell me which NTP servers you used to generate them? All SHA* MAC seem to be 20 bytes long.

guedou avatar Mar 08 '23 20:03 guedou

I was using chrony 4. afaik ntpd does not support AES-MAC.

Olegandr avatar Mar 09 '23 02:03 Olegandr