Naar de inhoud

Blog ·

Waarom een kwetsbaarhedenscanner nul bytes hoort te sturen

De grens tussen een controle en een aanval is wat je op de lijn zet. Hoe je van die belofte een getal maakt waarop je tests kunnen falen, met de metingen.

Door Provibr

Beheer je infrastructuur voor anderen, dan wil je vroeg of laat weten of poort 3306 op een van de machines van je klanten het hele internet staat te beantwoorden. Het is een redelijke vraag, de klant zou het antwoord willen hebben, en op het moment dat je gaat kijken ben je een partij geworden die ongevraagd verkeer stuurt naar een machine onder het contract van iemand anders.

Wat dat van een inbraak onderscheidt is niet je bedoeling, en het is geen vinkje in je algemene voorwaarden. Het is wat er werkelijk uit de socket vertrokken is. Al het andere is een verhaal over wat er uit de socket vertrokken is.

We hebben onlangs een blootstellingscontrole in onze agent gebouwd, met een regel die begon als een zin in een ontwerpdocument: stuur geen wijzigende query, geen inlogpoging, geen standaardwachtwoord, geen exploitlading, geen brede banneroogst. Zo werd die zin iets waarop een test kan falen, en dit is wat de peilingen die eruit voortkwamen in bytes kosten.

Een belofte in een commentaarregel heeft een houdbaarheidsdatum

Het probleem met de zin hierboven is niet dat hij moeilijk te volgen is. Het is dat hij ongeveer een half jaar makkelijk te volgen is.

Iemand voegt een EHLO toe aan de SMTP-peiling zodat die kan vaststellen of STARTTLS wordt aangeboden, wat werkelijk nuttige informatie is. De bestaande tests blijven slagen, want ze toetsen op wat de peiling vond en niet op wat ze zei. Vanaf die dag stuurt de software protocolcommando's naar machines van klanten, en het ontwerpdocument zegt nog steeds dat ze dat niet doet. Er werd niets rood. Er wordt ook nooit iets rood.

Dus hebben we er een getal van gemaakt waar een getal mogelijk is. Elke socket die de controle opent zit verpakt in een stroom die geschreven bytes telt, en het totaal reist met het resultaat mee terug. Bij vier van de zes soorten peiling toetsen de tests op een exact aantal: nul voor de twee die niets sturen, tweeëndertig voor SSH, acht voor PostgreSQL. De andere twee kunnen dat niet, want een client hello en een SMB-negotiate wisselen in lengte met wat er binnenin wordt aangeboden; in plaats daarvan is hun grens een vorm, namelijk één bericht en niets erna. Een EHLO toevoegen aan een peiling die op nul getoetst wordt, is nu een rode test met een getal erin.

Twee details in die teller bleken ertoe te doen.

Hij telt schrijfacties en geen leesacties. Wat de andere kant uit zichzelf prijsgeeft is niet iets wat wij veroorzaakten; wat wij sturen wel. En het is een gedeelde teller en geen veld op ons eigen stroomtype, want de TLS-peiling geeft haar socket aan een bibliotheek die er vervolgens haar eigen handshakerecords overheen schrijft. Zonder een gedeelde teller zou juist de ene peiling die de code van iemand anders gebruikt de ene ongetelde peiling zijn, en dat is precies waar een bytebudget het lastigst over na te denken is.

Wat elke peiling kost

PeilingWat wij sturenWat er gelezen wordt
Alleen openenniets, getoetst op 0 bytesniets
Banner: FTP, Telnet, SMTP, MySQLniets, getoetst op 0 byteséén begrensde begroeting
SSHéén identificatieregel, getoetst op 32 bytesde identificatieregel van de server, daarna zijn algoritmelijst als hij die uit zichzelf geeft
TLSéén client hello, wisselende lengteserver hello en certificaat
SMBéén negotiate, wisselende lengtehet antwoord op de negotiate
PostgreSQLhet SSL-verzoek, getoetst op 8 byteséén byte

Op de opening volgt niets. Geen tweede bericht, geen keepalive, geen afronding van een handshake. Zodra de andere kant gesproken heeft, gaat de socket dicht.

SSH: tweeëndertig bytes, en waarom het niet verder kan gaan

In het SSH-protocol sturen beide kanten eerst een identificatieregel. De onze is één regel van tweeëndertig bytes, en het is het enige dat de peiling schrijft. Daarna leest ze: de identificatieregel van de server, en vervolgens, als de server er ongevraagd een stuurt, zijn sleuteluitwisselingsbericht met de aangeboden algoritmen erin.

Het belangrijke deel is wat er ontbreekt. De peiling stuurt nooit haar eigen sleuteluitwisselingsbericht. Dat is geen beleid, het is een structurele grens: zonder dat bericht kan de sleuteluitwisseling niet doorgaan, wordt er nooit een sessie opgezet, en is er dus geen toestand in het protocol waarin een authenticatieverzoek verstuurd zou kunnen worden. "Geen inlogpoging" is daarmee geen regel meer die iemand moet onthouden.

De identificatieregel noemt het product in plaats van een client na te doen. Een beheerder die zijn authenticatielog leest hoort te kunnen zien wie er verbond. Een neutrale of vervalste tekst zou een legitieme controle op verkenning laten lijken, en dat is een slechte ruil voor de twee seconden onduidelijkheid die hij oplevert.

Er is een prijs, en die is het benoemen waard omdat hij zich voordoet als een kwaliteitsprobleem in de gegevens en niet als een fout. Sommige servers wachten op het sleuteluitwisselingsbericht van de client voordat ze het hunne sturen, en het protocol staat beide volgordes toe. Gebeurt dat, dan krijgt de peiling een versietekst en geen algoritmelijst. De dekking voor die poort is dan gedeeltelijk, en dat moet ook zo vastgelegd worden. Werd het vastgelegd als volledig met een lege lijst, dan zou de regelmachine concluderen dat er geen verouderde algoritmen worden aangeboden, en dat is de slechtst denkbare uitkomst: een zelfverzekerd "veilig" dat is afgeleid uit een afwezigheid van gegevens.

De protocollen die eerst spreken, en de verleiding om te antwoorden

FTP, Telnet, SMTP en MySQL begroeten je allemaal. De peiling voor die protocollen stuurt helemaal niets: verbinden, een begrensd aantal bytes lezen, sluiten. Dat is geen banneroogst, want er is geen manier om zo'n poort te openen zonder de begroeting te ontvangen, en er is niets gevraagd.

De verleiding van EHLO is echt en we hebben er tegen besloten. Weten of een mailserver STARTTLS aanbiedt is werkelijk waardevol. Het vereist ook het sturen van een protocolcommando, en de regel ging over feiten die de andere kant ongevraagd prijsgeeft. Het gevolg is dat een SMTP-bevinding op een lagere ernst en een lagere betrouwbaarheid uitkomt dan anders het geval zou zijn. Dat is de eerlijke prijs en hij hoort zichtbaar te zijn in de bevinding in plaats van weggeredeneerd te worden.

PostgreSQL is het interessante geval ertussenin. Acht bytes is het volledige SSL-verzoekpakket: een lengte en een magisch getal, wat het eerste is dat elke client stuurt en wat geen query, geen wachtwoord en geen inlogpoging is. De server antwoordt met één enkele byte die zegt of hij TLS zal spreken. Eén byte antwoord is genoeg om een database die versleuteling eist te onderscheiden van een database die dat niet doet, en er is geen versie van deze controle die minder stuurt.

Een open poort is geen kwetsbaarheid

Een poort 445 die antwoordt is niet hetzelfde als SMB1 dat aanstaat. Dat zijn twee losse feiten en ze hebben twee losse velden nodig.

Onze peiling legt een toestand voor de poort vast, wat neerkomt op de vraag of er iemand is, en hooguit één stuk bewijs, wat neerkomt op wat die iemand zei. Een negotiate die terugkomt zonder een oud dialect laat je achter met een open poort en verder niets, en dat is een volledig resultaat en geen gedeeltelijk. De verleiding om die twee tot één ernst samen te vouwen is groot, want het levert een veel spannender dashboard op.

Zorgvuldigheid met bewijs hoort in dezelfde alinea, want een begroeting is geen onschuldige tekst. Banners van FTP en SMTP dragen in de praktijk de hostnaam van de klant, soms de hostnaam van diens klant, en af en toe een pad op schijf. Sla je ze rauw op, dan wordt je bevindingentabel een plek waar persoonlijke en structurele informatie over de netwerken van anderen zich ophoopt, bewaard zolang bevindingen bewaard blijven, zichtbaar voor iedereen die een bevinding mag lezen.

Wat de peiling dus verlaat is een genormaliseerd protocolwoord, een versiefragment dat volgens een vastgelegde regel is uitgeknipt, het aantal gelezen bytes en een hash van de rauwe bytes. De hash maakt twee scans vergelijkbaar zonder dat iemand de tekst bewaart. Die laatste eigenschap is degene die je mist als je haar overslaat: zonder haar vereist "is dit sinds vorige maand veranderd" precies het ding dat je besloot niet te bewaren.

De sensor heeft geen mening

Nog één scheiding, en het is degene die de rest onderhoudbaar maakt.

De agent meet. Hij zoekt geen hostnamen op, houdt geen eigen poortlijst bij en kiest geen doelen: het adres en de poorten worden uitgeschreven door het platform, dat vóór het versturen controleerde dat het adres op dit moment aan die organisatie en die server is toegewezen. Zonder doelen meet de agent niets.

Het oordeel valt op het platform, vanuit een regelverzameling die gelezen en geversioneerd kan worden. Dat is van belang omdat een bevinding de code overleeft die haar voortbracht. Over een half jaar zal iemand vragen waarom een scan in september als hoog beoordeeld werd, en de enige manier om dat te beantwoorden is de regels en de metingen als aparte, afzonderlijk opvraagbare dingen te hebben. Zat de ernst ingebakken in de sensor, dan zou het antwoord luiden "omdat die versie van de agent dat vond", en dat is geen antwoord.

Dezelfde redenering is de reden dat de agent uitgaand verbindt en nooit luistert, en dat inloggegevens op de host blijven in plaats van naar ons toe te reizen. Een onderdeel dat uitsluitend meldt wat het gemeten heeft is een onderdeel waarvan je de schadestraal in één zin kunt beschrijven.

Wat we zouden opschrijven

  1. Maak van de belofte een getal. Gaat je regel over wat je stuurt, tel dan wat je stuurt en toets op het aantal. Een commentaarregel kan niet falen.
  2. Kies grenzen die structureel zijn. Geen sleuteluitwisselingsbericht sturen betekent dat authenticatie onbereikbaar is, en dat is sterker dan een regel die zegt niet te authenticeren.
  3. Laat een peiling gedeeltelijk zijn. Ontbrekende gegevens moeten zichtbaar zijn als ontbrekend, want het alternatief is een zelfverzekerd nee dat niemand kan controleren.
  4. Houd "open" en "kwetsbaar" in verschillende velden, en laat de regelmachine ze ergens combineren waar je het kunt nalezen.
  5. Bewaar geen rauwe banners. Normaliseren, hashen, en de hash bewaren om te vergelijken.
  6. Leg de mening bij het platform, zodat een oordeel van vorig kwartaal dit kwartaal nog uit te leggen is.

Voor de duidelijkheid over ons eigen product: dit is één detector, hij zit achter een schakelaar op organisatieniveau die op dit moment uit staat, en het is niet iets wat je vandaag kunt kopen. De metingen hierboven komen uit de uitvoering en haar tests, niet van een marketingpagina.

Gebouwd voor Provibr, waar de agent binnen het eigen netwerk van de klant draait. De verificatiekant van hetzelfde ontwerp, bewijzen dat een hostnaam toebehoort aan wie erom vraagt, staat in hoe je bewijst dat een hostnaam van jou is.

Begin met één host en één agent

Maak een account aan, installeer de agent met één commando en koppel je eerste Proxmox-host. De proefperiode loopt 14 dagen en vraagt geen betaalgegevens.