Door Tijs Hofmans

Nieuwscoördinator

Daan Keuper en Thijs Alkemade

Pwn2Own winnen met een Zoom-exploit

26-08-2021 • 06:00

14

Pwn2Own

De Nederlandse hackers Daan Keuper en Thijs Alkemade wonnen in april van dit jaar de hackwedstrijd Pwn2Own nadat ze een kwetsbaarheid hadden gevonden in Zoom. De hackers, die werken voor securitybedrijf Computest, bouwden een exploit waarmee ze van een afstand code konden uitvoeren op het Windows-systeem van een Zoom-gebruiker. Omdat het lek nog niet was gedicht, mocht het duo geen details vrijgeven over de kwetsbaarheid. Inmiddels mogen ze dat wel. De hackers hebben een technische analyse online gezet waarin ze hun exploit uitleggen. Tweakers sprak met hen over Pwn2Own en hoe je een effectieve exploit bouwt.

Vanaf nul beginnen

Pwn2Own is een wedstrijd waarbij onderzoekers veel soorten software kunnen hacken. Waarom kozen jullie specifiek voor Zoom?

Daan Keuper: "De wedstrijd vindt jaarlijks plaats. Ieder jaar doen ook bijvoorbeeld grote browsermakers mee, dus je kunt Chrome of Edge hacken. Het probleem is dat je weet dat die ieder jaar opnieuw mee gaan doen. Als onderzoeker kun je dus al lang van tevoren beginnen met het zoeken naar kwetsbaarheden. Bovendien doet Google zelf al heel veel aan beveiliging en er zijn veel externe onderzoekers bezig met Chrome. Zoom is echter een relatief nieuwe app. Daarom begint iedere onderzoeker eigenlijk vanaf nul. Dat maakt de wedstrijd eerlijker."

Pwn2Own

Thijs Alkemade: "Apps hebben ook allemaal hun eigen framework met hun eigen kwetsbaarheden. We zagen dat je naast Zoom bijvoorbeeld ook aan de slag kon met Microsoft Teams. Daar hebben we even over getwijfeld. Teams maakt gebruik van Electron. Dat betekent dat er dezelfde soorten kwetsbaarheden in kunnen zitten als in een web-app, dat zijn er al veel. Zoom is daarentegen gebouwd op C++. Daarin vind je sneller een buffer overflow en we vonden het persoonlijk toch wat leuker om daar onderzoek naar te doen."

Waar kijk je het eerst naar als je een kwetsbaarheid wilt vinden?

TA: "Je bekijkt eerst wat voor data de software verzendt. Wat gebeurt er op het netwerk als gebruiker A iets naar gebruiker B stuurt en wat gebeurt er als dat een plaatje is of juist een chatbericht? Er zijn allerlei tools voor en die gebruiken we om dat verkeer te analyseren."

DK: "Welke informatie je onderschept, hangt af van welk protocol er wordt gebruikt. We zagen dat de meeste configuratieberichten van Zoom over een gewone HTTP-verbinding liepen, maar chatberichten gingen via XMPP. Wat videogesprekken betreft, daar konden we geen wijs uit. We denken dat ze een of ander eigen formaat hebben ontwikkeld. Daar hebben we niet heel lang naar gekeken."

Zijn er doorgaans niet meer bugs te vinden in zelfgeschreven, ingewikkelde code van een bedrijf?

DK: "Dat denk ik wel. Een video decoderen is vaak erg foutgevoelig, dus daar zitten ongetwijfeld kwetsbaarheden in, maar we vonden eerder een andere kwetsbaarheid in een andere component waar we naar konden kijken."

TA: "Het is ook makkelijker om onderzoek te doen naar de chatfunctionaliteit. Als je een videobelverbinding wilt onderzoeken, moet je eerst veel meer stappen uitvoeren voordat je snapt hoe het werkt. Dat is vrij moeilijk en kost veel tijd."

Buffer overflow

De kwetsbaarheid zat in de manier waarop Zoom berichten naar het geheugen schrijftWelke kwetsbaarheid kwamen jullie uiteindelijk wel tegen?

DK: "Die zit in de manier waarop Zoom XMPP-berichten afhandelt. Daar gebruikt Zoom de Gloox-library voor. Je kunt elkaar op Zoom versleutelde berichten versturen. In het afhandelen van zo'n bericht wijst het programma een buffer van 1024 bytes toe en als je de applicatie normaal gebruikt, hoort dat element nooit méér ruimte te bevatten. Wij zijn natuurlijk geen gewone gebruikers, dus we hebben geprobeerd om manieren te vinden om meer data te versturen. Zoom vergeet te controleren of de data die binnenkomt, precies binnen die 1024 bytes past. Daardoor kun je een buffer overflow veroorzaken. De OpenSSL-library die Zoom gebruikt voor versleuteling doet wat hij moet doen en schrijft versleutelde data naar de buffer weg, maar in ons geval, als we een datapakketje van meer dan 1024 bytes hebben, past dat niet. Dan veroorzaak je een buffer overflow of een memory corruption."

Een kwetsbaarheid vinden is één ding, maar die uitbuiten is een volgende stap. Hoe schrijf je daar een exploit voor?

DK: "Dat doen we in een paar stappen. De eerste is dat we achter het buffergeheugen moeten kunnen schrijven, dus dat we over iets heen schrijven dat ons een bepaalde mate van controle over het geheugen geeft. In het begin heb je die controle niet. Dan schrijf je gewoon over data heen die toevallig achter de buffer ligt. Wat er dan gebeurt, is afhankelijk van datgene dat daar ligt, achter die buffer. Als dat een plaatje is, gebeurt er misschien niet zo heel veel, maar in veel andere gevallen is er een kans dat de applicatie crasht. De eerste stap is dus zorgen dat je een aanname kunt doen over het geheugen waar je overheen schrijft. We moeten precies het juiste schrijven, anders crasht de applicatie. Pas als je dat consistent kunt maken, kunnen we ervoor zorgen dat onze code ook echt wordt uitgevoerd en de software blijft werken."

"Daar gaat uiteindelijk de meeste tijd in zitten. Het vinden van die kwetsbaarheid heeft ons ongeveer anderhalve week gekost. Het schrijven van de exploit zelf, iets bouwen dat we ook daadwerkelijk kunnen gebruiken om een pc over te nemen, kostte echter nog eens anderhalve maand."

Trial and error

Je hebt het over 'de juiste code die je moet schrijven' richting de buffer. Hoe kom je erachter wat voor code dat is? Is dat een trial-and-errorproces of kun je zoiets automatiseren?

DK: "De belangrijkste stap is dat we heel precies aannames moeten kunnen doen over hoe het geheugen er aan de kant van het slachtoffer uit ziet. Zeker als de applicatie al een tijdje draait, is dat geheugen best een rommeltje. Dat proces van zoeken naar aannames noemen we heap grooming. Dat doen we door heel veel berichtjes te sturen naar de client, waardoor we steeds meer van het geheugen volschrijven. Vervolgens gaan we heel precies die berichtjes weggooien en gaatjes prikken in het geheugen. Als je dat nauwkeurig doet, kun je specifiek bepalen waar de gaatjes zitten en kun je die aannames doen."

"Dat is het saaiste deel. Je moet eindeloos dingetjes proberen en observeren wat er gebeurt. Het is heel veel trial and error. Het is bovendien moeilijk te automatiseren, maar uiteindelijk is het wel de betrouwbaarste manier van werken. Tegelijk is dat het belangrijkste deel. Hoe meer tijd je eraan besteedt, hoe betrouwbaarder je exploit wordt."

Daan Keuper Thijs Alkemade

Moet je de beveiliging van het besturingssysteem ook nog omzeilen?

DK: "Er zijn twee beschermlagen in het OS waar je omheen moet. De eerste is een functie waarbij een programma geen nieuwe code kan inladen als die eenmaal draait. Onze exploit werkt voor Windows, maar je zou dit ook voor macOS kunnen uitvoeren. In Windows heet die functie DEP, of Data Execution Prevention. We kunnen dus niet zomaar een chatbericht sturen met daarin wat code en vervolgens tegen de applicatie zeggen dat die de code moet uitvoeren. Dat betekent dat we stukjes code die al in het programma aanwezig zijn, aan elkaar moeten rijgen. We moeten dus weten waar de code van Zoom in het geheugen staat."

"Het tweede probleem is address space layout randomization of aslr. Dat zet een programma bij het starten op een willekeurige plek in het geheugen neer. Dus we moeten eerst heel expliciet weten waar Windows Zoom in het geheugen zet."

TA: "We hebben onze bufferoverflowkwetsbaarheid uiteindelijk zo weten in te zetten dat we hiermee een geheugenadres van het slachtoffer konden laten uitlekken. Dit noem je een information leak. Dat is uiteindelijk nodig om beschermingsmethodes zoals DEP en aslr te kunnen omzeilen."

Return oriented programming

Hoe voer je die exploit vervolgens concreet uit?

DK: "Daarvoor moeten wij als aanvallers kunnen bepalen welke code Zoom gaat uitvoeren. Normaal volgt Zoom zijn eigen programmapad, maar daar willen wij invloed op kunnen uitoefenen. We moeten dat zien te kapen. We kwamen erachter dat Zoom een audiobestand afspeelt als je iemand belt. Dat is zo'n geluidje van een telefoon die overgaat. Dat gebeurt via een adres waarin staat hoe dat bestand moet worden afgespeeld. Dat adres verwijst naar het stukje code om die ringtone af te spelen. We hebben dat adres kunnen aanpassen, zodat Zoom vanaf een ander adres code kon uitvoeren."

"Door dat te manipuleren gaat Zoom het adres in het geheugen zetten op precies de plek waar wij weten dat het gebeurt. De eerstvolgende keer dat de ringtone dan afgaat, kunnen wij bepalen vanaf waar Zoom de code uitvoert. Door kleine stukjes code aan elkaar te rijgen, een proces dat return oriented programming wordt genoemd, kunnen we Zoom uiteindelijk een commandprompt laten starten met een internetverbinding naar ons."

Pwn2own vond al in april plaats. Is deze kwetsbaarheid inmiddels gerepareerd?

DK: "Gelijk nadat we de exploit presenteerden op Pwn2Own, hebben we meer uitleg gegeven aan Zoom. Zo kan Zoom kijken of deze kwetsbaarheid niet al bekend was in zijn eigen bugtracker. Dan zou je de wedstrijd verliezen. Al vrij snel na de competitie heeft Zoom een mitigatie doorgevoerd aan de serverkant. Dat was mogelijk doordat berichten altijd via de servers van Zoom gingen. Dat, plus het feit dat het lek niet actief werd misbruikt, maakte het risico voor eindgebruikers laag."

Reacties (14)

14
14
9
1
0
2
Wijzig sortering
Als je een library gebruikt van een third party, hoe moet je dan weten dat er een standaard buffer wordt aangemaakt van 1024 bytes ? Is er niet ergens een best practice beschreven of komt dit gewoon neer op ervaring/deskundigheid van een ontwikkelaar ? Het is interessant om te lezen hoe 1 extra controle het hele zooitje in elkaar doet storten, maar hoe kan je dit als ontwikkelaar voorkomen? Je kan onmogelijk alles weten over elke library.
Je kan onmogelijk alles weten over elke library.
Als je leunt op de services van anderen, dan is dat wel zaak omdat te weten. Je hoeft het niet meer zelf te schrijven, maar je zou het wel volledig moeten doorgronden op hoe het werkt. Maar dat is dan ook het verschil tussen een berg aan libs aan elkaar te knopen zonder goed te kijken, of dat je daadwerkelijk doorneemt wat er allemaal achter zit. En ja dat kost veel (tijd en resources) vandaar dat het vaak niet gedaan wordt.
Ja, maar hoe realistisch is dat in de praktijk? Iedereen is afhankelijk van versleuteling. Om ECDSA of zelfs RSA volledig te doorgronden is maar voor weinigen weggelegd (laat staan de code die er voor nodig is om het te bewerkstelligen). Een GUI is voor heel veel software ook handig. Grafische libraries kunnen ook bijzonder ingewikkeld zijn om te doorgronden.

(uiteraard pak ik nu gelijk wat extremen, maar het gaat om het idee)
Het is vaak veel eenvoudiger om de implementatie aan te vallen dan de encryptie. De implementatie gebeurt vaak door niet-deskundige programmeurs. Je hoeft niet per se alles te weten van RSA of EC, maar je moet wel weten hoe je het correct gebruikt.
Rust libraries gebruiken ipv C++ libraries

[Reactie gewijzigd door BCC op 24 juli 2024 02:27]

Ik denk dat je in dit geval zelf de geheugenallocatie doet en de library het adres geeft waar de gedecodeerde data weggeschreven moet worden.

In het algemeen is deskundigheid inderdaad een probleem. 1 mindere ontwikkelaar in een team kan heel veel ellende veroorzaken.

Of dat nou security issues, performance problemen, integratie issues, race condities zijn, écht stabiele software schrijven is niet eenvoudig. Helaas geldt voor veel software dat een 'happy-path' wordt uitgewerkt en dat veel problemen heel lang aanwezig zijn zonder echt naar boven te komen.

Gebruik van externe libraries wordt in alle moderne platforms heel toegankelijk gemaakt, maar de afhankelijkheden weten maar weinigen. Hoe gevaarlijk dat is heeft het npm/leftpad debacle duidelijk aangetoond.

Je kunt dus eigenlijk niet zomaar een library gebruiken zonder écht te begrijpen hoe die werkt. Daarmee wil ik niet zeggen dat je alle details moet weten van een encryptie methode, maar ja: hoe veilig is een bepaalde methode? Daarop graag wél een antwoord.
In het algemeen is deskundigheid inderdaad een probleem. 1 mindere ontwikkelaar in een team kan heel veel ellende veroorzaken.
Maar dan kloppen je interne git afspraken ook niet. Als je een mindere ontwikkelaar in je team hebt en hij stuurt een pull request in dan zal een andere ontwikkelaar zijn pull request moeten goedkeuren.

Als die andere vervolgens de code niet goed naloopt aan wie heeft het dan gelegen?
Ik vraag me nu werkelijk af hoeveel moeilijker het is om een applicatie geschreven in Rust te hacken. Klaarblijkelijk zijn er veel c/c++ programmeurs die zichzelf overschatten. Zou een programmeertaal als Rust er voor kunnen zorgen dat deze programmeurs ineens wel veilige code kunnen schrijven?

Ik kom zelf uit de PHP-wereld. Als er een taal is waarin je heel erg gemakkelijk fouten maakt is het wel PHP. Zo beamen ook vaak c- en java-programmeurs. Tegelijkertijd wordt er door diezelfde c- en java-programmeurs soms keihard afgegeven op Rust.

Zou het ownership-principe van Rust in dit geval een buffer-overflow hebben voorkomen?
AuteurTijsZonderH Nieuwscoördinator @delphium26 augustus 2021 13:12
Zou een programmeertaal als Rust er voor kunnen zorgen dat deze programmeurs ineens wel veilige code kunnen schrijven?
Heb je een tl;dr voor niet-programmeurs waarom Rust veiliger zou zijn?

[Reactie gewijzigd door TijsZonderH op 24 juli 2024 02:27]

Nee, niet echt. Ik probeer het zelf even.
Wat er vaak misgaat in programmeertalen als c en c++ is dat een programmeur zelf geheugen moet toewijzen en ook weer moet vrij maken na gebruik. Als je dat vergeet zijn dus aanvallen zoals beschreven door Daan en Thijs mogelijk.

Rust neemt dat beheer van geheugenplaatsen voor een groot deel uit handen. De compiler zorgt ervoor dat als geheugen niet meer nodig is, het automatisch weer wordt vrijgegeven. Dit is wel heel simplistisch, maar voor een niet-programmeur wordt het al gauw abacadabra anders.

edit: aanvulling
Rust is eigenlijk specifiek ontworpen om dit geheugenprobleem op te lossen voor programmeurs. Rust wint snel aan populairiteit. Ook de Linuxkernel accepteert nu nieuwe code geschreven in Rust.

[Reactie gewijzigd door delphium op 24 juli 2024 02:27]

Leuk artikel om te lezen! Zelf zou ik nog wat meer theorie willen over hoe bijvoorbeeld memory exploits precies werken. Weet iemand toevallig een goed boek om hier meer over te leren?
Als plus-lid heb ik een mening over dit soort artikelen en word je regelmatig gevraagd wat je van dit artikel vindt. Zelf zou ik het fijn vinden als er een soort van poll onder een artikel zit, met cirkeldiagram, waarin je ziet wat onze mede-tweakers ervan vinden. De vragen die gesteld worden gaan over of het aansluit bij je verwachting, terwijl ik ook de vraag wil of ik dit artikel nou wel of helemaal niet zou willen zien op tweakers. Als het meerendeel het prima vind, dan is het goed, zoniet dan zie ik graag een herkenbare verandering.

Zelf zou ik dit niet als plus artikel willen zien, maar artikelen over nieuwe technieken voor zonnepanelen e.d. wel. Dit vind ik meer een klein achtergrond interview. Zonder tekort te willen doen aan schrijver en geïnterviewden.

[Reactie gewijzigd door no_way_today op 24 juli 2024 02:27]

Ik heb onlangs de podcast geluisterd met Thijs bij De Technoloog (https://www.bnr.nl/podcas...n-zoom-en-andere-software).
Tot nu toe was ik niet echt geneigd om op een Plus artikel te klikken, maar ik hoopte op een stuk meer diepgang hier. Helaas was een uur podcast vele malen boeiender dan een Plus artikel wat mij moest overhalen om nog meer DPG abbo's af te sluiten.

Op dit item kan niet meer gereageerd worden.