Meltdown & Spectre in detail
Meltdown & Spectre-aanvallen in detail Welke aanvalsplannen schuilen er nu precies achter Meltdown en Spectre? Waar zitten de lekken en hoe kunnen die in de praktijk worden benut? Een analyse.
Rond de jaarwisseling gingen er geruchten rond over een kritiek veiligheidslek in Intel-cpu's. Opvallend was vooral het rappe tempo waarmee Windows- en Linux-kernel-ontwikkelaars werkten aan de implementatie van een veiligheidsmechanisme met de naam Kernel Page-Table Isolation (KPTI).
De concrete aanvalstechnieken die afgeweerd moesten worden bleven geheim, pas op 3 januari maakten de ontdekkers van Meltdown en Spectre de details openbaar. Microsoft reageerde nog dezelfde dag met haastig opgetrommelde veiligheidsupdates, die eigenlijk stonden gepland voor de patchdag op 9 januari. Later kwam boven water dat de onderzoekers de getroffen hard- en softwarefabrikanten al in juni 2017 hadden geïnformeerd om ze zo voldoende tijd te bieden om aan het repareren te slaan. Vanaf die tijd tot het moment dat de lekken bekend werden, verkochten AMD, Intel, Microsoft, Apple en talloze andere bedrijven chips en apparaten waarvan ze wisten dat er qua veiligheid iets aan schortte.
Meerdere onafhankelijke projectgroepen hebben de twee veiligheidslekken ontdekt en op blogs en in whitepapers gedetailleerd beschreven: Googles Project Zero, medewerkers van Cyberus Technology en twee teams met medewerkers van meerdere universiteiten. Daarbij zaten ook onder- zoekers van de TU Graz die twee jaar eerder al met het opvallende nieuws naar buiten kwamen over foutief RAM (Rowhammer).
Meltdown:
Rogue Data Cache Load
Meltdown (CVE-2017-5754), door Google ook wel Rogue Data Cache Load genoemd, doorbreekt de strikte scheidingslijn tussen user- en kernel-mode. Hierdoor wordt het mogelijk voor aanvallers die niet de benodigde rechten hebben, om toegang te krijgen tot het werkgeheugen van de meeste besturingssystemen. Voor deze aanval is het wel noodzakelijk dat processen in de user-mode werken met een page table die niet alleen hun eigen maar ook alle virtuele geheugenadressen van de kernel vermeldt.
Dit had tot voor kort impact op alle gangbare besturingssystemen. Pas sinds de patches tegen Meltdown voor Windows en Linux is een beschermingsmechanisme met de naam Page Table Isolation (PTI) in de kernel aanwezig (ook Kernel PTI/KPTI genoemd). De techniek waarop dit is gebaseerd heette oorspronkelijk KAISER (Kernel Addres Isolation to have Side-channels Efficiently Removed). Die zorgt voor een scheiding van de page tables voor kernelen user-mode. Dit wordt ook wel page table splitting genoemd. Hiervoor wordt
een shadow page table gebruikt waardoor een draaiend proces alleen zijn eigen geheugenbereik ziet en een klein deel van het geheugenbereik van de kernel: alleen datgene wat nodig is voor system calls en interrupts. De originele page table en mapping van het volledige adresbereik is alleen nog maar via de kernel-mode te bereiken.
Het open en bloot zichtbaar zijn van kernel-adressen maakt niet direct toegang tot het geheugen mogelijk, dit wordt normaliter door de supervisor-bit van de processor voorkomen.
Meltdown breekt deze bescherming af via speculative execution. Dit is een mechanisme in moderne cpu's dat speculative execution van reeksen commando's met interne cpu-registers mogelijk maakt om de performance te verhogen. Als de speculaties niet van toepassing zijn, worden de resultaten in de interne registers simpelweg weer verworpen en niet naar de normale architectuurregisters overgezet. De speculative executed commando's laten echter sporen achter in de cpu-cache, in de Translation-Lookaside-Buffers (TLB) en de history- en/of target-buffers, die via trucs door aanvallers uitgelezen kunnen worden.
In theorie is voor een Meltdownaanval een enkel proces nodig dat aan de ene kant de speculatief uit te voeren reeks commando's bevat en aan de andere kant de code voor het uitlezen van de cache. Aangezien volgens onderzoekers het uitvoeren ook via een parent- en een child-proces of twee aparte threads van een proces werkt, gebruiken we die route voor een wat duidelijker voorbeeldscenario.
Byte voor byte
Aan het begin van dit vereenvoudigde Meltdown-scenario hebben we een aanvaller die het geheugen wil uitlezen. Hij heeft in de user-mode niet de daarvoor benodigde toegangsrechten. Hij 'ziet' echter dankzij de nog niet geïmplementeerde KPTI de virtuele geheugenadressen van vreemde processen inclusief die binnen de kerneladresruimte. Om via omwegen die inhoud uit te lezen zijn 256 geheugenadressen van 4096 byte in user-mode als 'probe array' voldoende.
De aanvaller start hiervoor twee processen of een enkel proces met twee threads. Proces 1 speelt zender en proces 2 ontvanger. Ze gebruiken allebei de cache van de cpu als maskering voor de communicatie. In het Engels wordt dit covert channel of side channel genoemd. Proces 2 leegt de cpu-cache. Dan breekt proces 1 zonder de nodige rechten in op een geheugenlocatie in de virtuele adresruimte van de kernel, die in ons voorbeeld de waarde 2 bevat. Dit triggert een exception.
Dankzij out-of-order execution (OoOE) draait de exception handling parallel aan de speculative execution van de verdere commando's van proces 1. Deze commando's vermenigvuldigen de zonet in een intern register uitgelezen waarde met 4096, oftewel de grootte van een page. In het voorbeeld wordt het resultaat 8192.
De volgende speculative execution grijpt terug op het bijbehorende adres in de probe array. Zo belandt de page, die het adres bevat (in ons geval de tweede) in de cache van de cpu.
Nu voert proces 2 een als flush & reload bekende side-channel aanval uit op de cache. Hij zapt door alle 256 pagina's in de probe array en voert tijdmetingen uit om te bepalen of een daarvan al in de cache staat. In dit geval is de toegangstijd op de derde page duidelijk korter. De aanvaller kan daaruit waarde 2 bepalen (de eerste page staat gelijk aan 0).
Voor het uitlezen van de volgende byte leegt proces 2 opnieuw de cache en de procedure begint weer van voren af aan. Volgens de Meltdown-whitepaper haalden de onderzoekers bij het byte voor byte uitlezen van data via het zonet beschreven schema een snelheid tot aan 503 kB per seconde. In de whitepaper gaan ze kort in op Kernel Address Space Layout Randomization (KASLR). Dit beschrijft een toevallige toewijzing van RAM-adressen in de kernel, die aanvallen lastiger moet maken en al jaren onderdeel is van de gangbare besturingssystemen. Aangezien dat echter kwetsbaar is voor side-channel-aanvallen, kan het de Meltdown-aanvallen niet stoppen maar in het beste geval de totale duur van de aanval afremmen door te zorgen voor extra voorbereidingstijd.
Nadat de whitepaper bekend werd gemaakt en de proof-of-concept-code op GitHub belandde, gaven een aantal personen aan dat de beschrijving van de aanval sterk vereenvoudigd leek ten opzichte van oudere openbaar gemaakte informatie. Micheal Schwarz, een van de schrijvers van de paper, bevestigde via GitHub dat de openbaar gemaakte proof-of-conceptcode nog niet geoptimaliseerd is en mogelijk niet op alle systemen werkt. Er is wel een geoptimaliseerde versie beschikbaar, maar die wil hij pas openbaar maken zodra de Meltdown-patches grootschalig zijn uitgerold.
Spectre
Achter het tweede lek met de naam Spectre zitten twee verschillende aanvalsscenario's verscholen. Variant 1 (CVE-20175753) wordt door Google ook wel Bounds Check Bypass genoemd, variant 2 (CVE2017-5715) heeft de naam Branch Target Injection (BTI) gekregen.
De openbaar gemaakte proof-of-concept-code voor de Spectre-aanvalstechnieken richtte zich tot nu toe echter alleen op programma's in de user-mode. Maar het is niet uitgesloten dat ook de kernel-code gevaar loopt. Linux-ontwikkelaars werken al een tijdje aan patches om het besturingssysteem dicht te timmeren.
Anders dan bij Meltdown zorgt de Spectre-code niet voor een exception. In plaats daarvan 'traint' hij de branch prediction, een andere feature van moderne cpu's met out-of-order-execution. Die helpt de address prediction van branch instructions op basis van empirische waarden.
Een aanval die Spectre wil misbruiken, traint de brach predictor via een voldoende aantal herhalingen op zo'n manier dat de betreffende code bij bepaalde programmavertakkingen een voor hem gunstige branch kiest. Het doel is, vergelijkbaar met hoe dat bij Meltdown verloopt, om via speculative execution toegang te krijgen tot vreemde geheugencontent en daardoor sporen in de cache achter te laten.