Zwakheden in NTP-tijdsprotocol maken andere (beveiligings-)protocollen kwetsbaar
Oude NTP-protocol krijgt eindelijk een bruikbare cryptografische beveiliging
Oude NTP-protocol krijgt eindelijk een bruikbare cryptografische beveiliging
Met de publicatie van het NTS beveiligingsprotocol als RFC 8915 heeft het NTP tijdsprotocol eindelijk een bruikbare cryptografische beveiliging gekregen. En dat was hoog nodig ook: de afgelopen jaren verschenen meerdere publicaties over kwetsbaarheden in NTP, en de consequenties daarvan voor diverse internet-beveiligingsprotocollen (waaronder ook DNS en DNSSEC).
In dit artikel zetten we de problemen met NTP op een rijtje. Het nieuwe NTS-protocol bespreken we in een ander artikel. En meer over onze eigen NTP-dienst TimeNL – waaronder ook een experimentele NTS-server – vind je in een eerder gepubliceerd artikel.
De huidige versie 4 van NTP, vastgelegd in RFC 5905, bevat wel beveiligingsmogelijkheden, maar die worden (om goede redenen) maar weinig gebruikt. De eerste mogelijkheid is symmetrische encryptie, maar daarvoor moet je wel eerst een geheime sleutel tussen de betreffende systemen uitwisselen. Bovendien is deze beveiliging gebaseerd op het bewezen onveilige MD5-algoritme, zodat het gebruik ervan wordt afgeraden. Onofficieel alternatief voor MD5 hier is SHA-1, dat ook niet meer veilig is maar nog wel voor dit soort authenticatiemechanismen gebruikt kan worden. Daarnaast is er een asymmetrische authenticatiemethode beschikbaar: Autokey, gedefinieerd in RFC 5906. Maar dat is een problematisch/onveilig protocol dat je echt niet moet gebruiken. Dat betekent dat er tot nu toe eigenlijk geen goede opties waren om NTP te beveiligen. Vandaar dat we voor de veiligheid van NTPv4 nu meestal vertrouwen op een koppeling van de response aan het adres van de server waar de bijbehorende query heen werd gestuurd, tezamen met een timestamp (nonce) van de laatste synchronisatie met die server.
Omdat NTP net als DNS traditioneel over het stateless UDP-protocol loopt, kan elke aanvaller gespoofde NTP-responses naar een NTP-client sturen, in de hoop dat deze client de inhoud van die pakketten daadwerkelijk gebruikt om zijn systeemklok bij te werken. Een van de beschermingen die NTP-servers hiertegen bevatten, is dat zij maar beperkte wijzigingen (1000 seconden = 16 minuten) ten opzichte van de huidige systeemtijd toelaten. Of dat een client eerst 10 tot 100 NTP-responses (samples) van een server moet ontvangen, voordat deze zijn tijd daadwerkelijk zal aanpassen. Maar daarmee blijven deze systemen kwetsbaar bij het opstarten ― denk aan IoT-apparaten die geen eigen RTC-klokchip aan boord hebben ― alsook voor het aanpassen van de systeemklok via een heleboel kleine stapjes (time skimming).
Kijken we naar traditionele aanvallen op NTP-systemen en problemen met implementaties, dan blijkt misbruik van een (publieke) NTP-dienst (abuse) het meest voor te komen. Dat gebeurt bijvoorbeeld als een specifieke server (ongevraagd) wordt ingesteld door de fabrikant van een IoT-apparaat, of als een access provider dit doet in de modems die hij uitzet bij zijn klanten. Een ander bekend probleem zijn DDoS-amplificatie-aanvallen. Maar in 2015 publiceerden Malhotra e.a. van Boston University een paper waarin ze diverse zwakheden in het NTP-protocol zelf blootlegden. De auteurs lieten zien hoe ze door de systeemtijd te veranderen, konden rommelen met allerlei beveiligingsprotocollen die van de juiste tijd afhankelijk zijn.
Zo zitten de typische refresh rates voor de KSK/ZSK-sleutelparen van DNSSEC in de maanden, maar voor de digitale handtekeningen (RRSIG records) zijn dat slechts weken. Dat betekent dat je een DoS-aanval op een validerende resolver zou kunnen uitvoeren door de tijd vooruit te zetten, of de cache zou kunnen flushen door de TTL van de records kunstmatig te laten verlopen. Of andersom: dat je een replay-aanval zou kunnen uitvoeren door de tijd juist terug te zetten. Protocollen als RPKI en Bitcoin worden al kwetsbaar bij afwijkingen in de tijd van dagen of uren. Voor die eerste zou je eerst het RPKI manifest bij de routers (in de cache) kunnen legen, om deze vervolgens te vullen met verouderde (incomplete) informatie. En op een Bitcoin-node zou je geldige blokken kunnen laten verwerpen, of miners kunnen laten werken aan ongeldige blokken. Voor Kerberos-geauthentiseerde sessies en andere online authenticatie-diensten draait het zelfs om minuten: er zijn diverse replay-aanvallen mogelijk waarbij oude inloggegevens worden hergebruikt. Ben je in staat om een systeemklok jaren terug te zetten, dan zou je zelfs op TLS in kunnen breken door allang ingetrokken certificaten uit oude hacks alsnog te gebruiken.
In hun publicatie beschrijven Malhotra e.a. verschillende methoden om via NTP de systeemtijd daadwerkelijk te veranderen. Naast de hierboven al genoemde technieken (gespoofde NTP-responses en time skimming), kun je aanvullende trucjes gebruiken, zoals als het onbereikbaar maken van de upstream NTP-servers van een systeem. Daarvoor kun je natuurlijk een DoS-aanval uitvoeren op die NTP-servers, maar ook een (gespoofd) Kiss-of-Death (KoD)-pakket naar de client sturen. Deze laatste optie is bedoeld voor servers om te snel vurende clients te vragen hun queries even te pauzeren, maar deze kan ook misbruikt worden om te zorgen dat clients hun servers voor langere tijd niet kunnen (mogen) benaderen. De beveiliging tegen gespoofde valse NTP-responses is vergelijkbaar met de transaction ID gebruikt bij DNS. In dit geval wordt de origin timestamp gebruikt die aangeeft wanneer voor het laatst gesynchroniseerd werd met de betreffende server. Deze timestamp levert grofweg 32 bits aan entropie (randomness) op, en is dus niet makkelijk te raden door een aanvaller die een valse NTP-reponse naar een client wil sturen. Gek genoeg werd de terugkerende timestamp bij KoD-responses niet gecontroleerd door de ontvangende client om te verifiëren of een binnenkomend bericht wel echt bij een verzonden bericht hoorde. Dit probleem werd gefikst na de publicatie van dit onderzoek. Maar je kunt natuurlijk ook (andersom) namens de client een hoop gespoofde NTP-queries naar de echte server sturen, die daarop echte KoD-responses terug zal sturen naar de echte client. Heeft een client geen NTP-servers meer om mee te synchroniseren, dan zal zijn systeemtijd gaan driften.
Een tweede mogelijkheid is om de origin timestamp helemaal te omzeilen door valse segmenten te injecteren in een stroom van gefragmenteerde UDP-pakketten. Het idee is om de timestamp intact te laten en alleen de payload (de tijd) te veranderen. De aanvaller kan de correcte UDP checksum natuurlijk niet meegeven, maar dat probleem kan hij omzeilen omdat deze checksum voor IPv4 niet verplicht is (gewoon op 0 zetten). Met deze techniek verstuurt de aanvaller dus geen complete UDP-pakketten, maar alleen fragmenten daarvan, in de hoop dat die samen met eerdere fragmenten door de client naar binnen worden gehaald. Om dit te doen slagen, moet hij wel meerdere fragmenten sturen, met de juiste timing, en goed passen en meten om (overlappende) fragmenten op de goede plaats in het uiteindelijk weer geassembleerde UDP-pakket te krijgen.
De publicatie van dit onderzoek was de directe aanleiding voor de implementatie van NTS. En ook hier kunnen we weer parallellen trekken met DNS en DNSSEC: In 2008 liet Dan Kaminksy zien hoe je valse informatie in een caching resolver injecteert. Dat was de aanleiding om serieus met de implementatie van DNSSEC aan de slag te gaan. SIDN Labs en Nederlandse software-ontwikkelaars hebben daar een belangrijke voortrekkersrol in gespeeld. De publicatie van de 'SAD DNS'-aanval eerder deze maand toont nog eens het belang aan van deze structurele oplossing.
In de zomer van 2019 publiceerde dezelfde Aanchal Malhotra samen met de mensen van NLnet Labs (verantwoordelijk voor de ontwikkeling van NSD, Unbound, OpenDNSSEC, Routinator en verschillende andere netwerk-tools) een paper specifiek gericht op het belang van een correcte (veilige) systeemtijd voor DNS. Daarin bespreken zij verschillende scenario's:
Hoe lang records (nog) in de cache van een resolver opgeslagen blijven, wordt bepaald door de TTL. De TTL zelf is relatief (geeft alleen de resterende tijd in seconden aan), maar de implementaties van de cache vertalen dit naar absolute tijden op basis van de systeemtijd. Dit gegeven kan misbruikt worden om entries (records) eerder of later te laten flushen, en dat kan weer de basis vormen onder andersoortige aanvallen, zoals de eerder deze maand gepubliceerde 'SAD DNS'-aanval Zo kun je een geslaagde 'cache poisoning'-aanval verlengen door op deze manier de valse records voor langere tijd in de cache te houden. Je kunt ook fail-over configuraties frustreren door hetzelfde te doen voor de korte TTL's van DNS-gebaseerde load balancing systemen. Of je kunt de levensduur van NXDOMAIN replies (negative caching) verlengen. Het advies voor ontwikkelaars van caching resolvers luidt dan ook dat de administratieve velden in de cache alleen gebruik mogen maken van relatieve tijdsaanduidingen om de resterende levensduur aan te geven. Inmiddels is dat ook in Unbound geïmplementeerd.
De geldigheidsduur van de digitale handtekeningen (RRSIG records) van DNSSEC wordt weergegeven in absolute tijdsaanduidingen. Dit systeem is echt alleen robuuster te maken door de NTP-gebaseerde tijdsvoorziening van je validerende resolver van een cryptografische beveiliging te voorzien.
Bovendien zijn er hele andere DNS-gebaseerde beveiligingssystemen ― denk aan DNSBL's: DNS-based Blackhole Lists ― die op deze manier gemanipuleerd kunnen worden.
MITM-aanvallen op NTP zijn makkelijk uit te voeren. Daarvoor hoef je immers alleen de origin timestamp uit de NTP-query te kopiëren naar je gespoofde NTP-response. Voor de introductie van NTS was de authenticiteit en integriteit alleen te beveiligen tegen dit soort aanvallen middels een gedeelde (symmetrische) sleutel en een MAC (Message Authentication Code), enigszins vergelijkbaar met een digitale handtekening). Off-path-aanvallen zijn moeilijker vanwege de nonce (timestamp), maar zoals je hierboven hebt kunnen lezen, zijn er verschillende aanvalstechnieken bekend. Een belangrijk probleem met NTP is dat deze software op zowel grote als hele kleine internet-connected systemen draait, dat sommige daarvan heel lang draaien, en dat met name IoT-apparaten geen updates ontvangen. Specifiek voor validerende resolvers luidt het advies om NTP aan te zetten, maar dan wel met een goede configuratie. Dat betekent in ieder geval dat deze uitsluitend geconfigureerd mag worden als client, niet als server. Daarnaast zijn de NTP-clients op validerende resolvers de eerste om het hier beschreven NTS-protocol te gaan gebruiken.