Hoe AFL en AFL++ effectief te gebruiken voor het fuzzen van binaire bestanden

Laatste update: 28/02/2026
Auteur: Isaac
  • Fuzz-testen automatiseren het verzenden van onjuist geformuleerde invoer om beveiligingslekken in software en systemen op te sporen.
  • AFL en AFL++ zijn intelligente fuzzers die gebruikmaken van geïnstrumenteerde compilers om zichzelf te sturen op basis van code coverage en nieuwe uitvoeringspaden te vinden.
  • Door crashes te analyseren met tools zoals AFLTriage en GDB kun je de exploiteerbaarheid van kwetsbaarheden zoals Integer Underflow beoordelen.
  • Het integreren van fuzzing in de ontwikkelingscyclus verbetert de robuustheid, helpt bij het voldoen aan veiligheidsnormen en vermindert het risico op kritieke incidenten.

Fuzzing met AFL in binaire vorm

Als je geïnteresseerd bent in Binair georiënteerde cyberbeveiliging en exploitatieVroeg of laat kom je fuzzing tegen, en met name AFL en AFL++. We hebben het hier niet over een theoretisch labprogramma, maar over iets dat dagelijks wordt gebruikt om zeer reële fouten in bekende programma's te vinden, net zoals dat is gebeurd met kwetsbaarheden in... populaire applicaties zoals 7-Zip.

In de volgende regels zullen we in detail en op een vriendelijke toon bekijken, Hoe gebruik je AFL en AFL++ om binaire bestanden te fuzzen?We leggen precies uit wat fuzz-testen zijn, waarom ze zo krachtig zijn op het gebied van beveiliging en hoe je ze kunt integreren in een moderne ontwikkelingsworkflow. We bekijken ook een praktijkvoorbeeld van een kwetsbaarheid (een integer-underflow die uitvoering van code op afstand mogelijk maakt) en bespreken aanvullende tools zoals... GDB met uitbreidingen, AFLTriage en bedieningsmodules.

Wat zijn AFL/AFL++ en waarom worden ze beschouwd als slimme fuzzers?

AFL-tool voor fuzz-testen

AFL (American Fuzzy Lop) en zijn evolutie AFL++ Dit zijn open-source fuzzingtools die zijn ontworpen om binaire bestanden automatisch te testen op fouten zoals overflows, geheugenproblemen of onverwacht gedrag dat leidt tot crashes of zelfs exploiteerbare kwetsbaarheden.

De schoonheid van AFL zit hem niet alleen in het versturen van willekeurige data, maar in het versturen van specifieke gegevens. intelligente fuzzer of coverage-guided fuzzerUitgaande van een of meer geldige voorbeeldinvoer (de zogenaamde testgevallenAFL genereert constant variaties en detecteert, dankzij de instrumentatie van het programma, wanneer een nieuwe mutatie ervoor zorgt dat het binaire bestand een ander uitvoeringspad volgt.

Om deze instrumentatie te realiseren, heeft AFL eigen compilers, zoals afl-gcc en afl-g++Deze voegen tijdens de compilatie kleine "haakjes" in de code in. Deze haakjes laten AFL weten dat een bepaalde invoer is gevonden. nieuwe routes in de programmastroomHierdoor kunt u zich concentreren op mutaties die daadwerkelijk extra dekking bieden en geen tijd verspillen aan gegevens die niets veranderen.

Deze aanpak maakt van AFL iets vergelijkbaars met de "dirb voor binaire bestanden": een bekend hulpmiddel, gemakkelijk te gebruiken zodra je de basiswerking begrijpt, en krachtig genoeg om te ontdekken. ernstige gebreken in productieprogramma'sook al bestaan ​​er meer geavanceerde of gespecialiseerde fuzzers.

Inleiding tot fuzz-testen en de rol ervan in cyberbeveiliging.

Fuzztesten, of kortweg fuzzing, is een techniek van geautomatiseerde beveiligings- en robuustheidstestsHet idee is om een ​​applicatie te overspoelen met een stortvloed aan misvormde, willekeurige of ongebruikelijke gegevens, met als doel te zien of het programma vastloopt, crasht of zich onverwacht gedraagt.

Bij het fuzzen van software is het doel om fouten op te sporen, zoals: bufferoverloop, fouten bij invoervalidatie, problemen met geheugenbeheer of raceomstandighedenVeel van deze kwetsbaarheden kunnen leiden tot het uitvoeren van willekeurige code, het lekken van informatie of het afbreken van diensten.

In een typische workflow doorloopt fuzz-testen drie duidelijke fasen: eerst wordt de testdataset gegenereerd (ofwel volledig willekeurig, gegenereerd volgens een specifiek formaat, of door het muteren van geldige invoergegevens); vervolgens... Voer het te testen binaire bestand uit terwijl het wordt gemonitord. Het gedrag ervan wordt gemonitord; en tot slot worden crashes of ongebruikelijk gedrag geregistreerd en geanalyseerd, zodat ontwikkelaars de oorzaak van het probleem kunnen onderzoeken.

Er bestaan ​​verschillende fuzzing-strategieën. Sommige tools richten zich op... generatievervagingHet creëren van items die een welomschreven formaat respecteren (bijvoorbeeld een bestandsstructuur of een netwerkprotocol); andere zijn gebaseerd op mutatie van geldige invoerDit is precies de aanpak waarin AFL en AFL++ uitblinken. Er zijn ook meer willekeurige benaderingen en andere die afhankelijk zijn van specifieke kennis van het te testen systeem om de zwakke punten ervan te vinden.

In de moderne cybersecurity is fuzz-testen een fundamentele bondgenoot: het helpt bij het vinden van kwetsbaarheden in een systeem. proactief, voordat de software in productie gaat.Het draagt ​​bij aan de naleving van regelgeving en normen zoals ISO 27001 of de vereisten voor gegevensbescherming. Bovendien verkleint het het reputatierisico dat zou ontstaan ​​als een kritieke, exploiteerbare kwetsbaarheid in handen van aanvallers zou vallen.

Een praktijkvoorbeeld: Integer Underflow en RCE-kwetsbaarheid in een decompressor.

Om de werkelijke waarde van AFL en fuzzing te begrijpen, is het erg nuttig om naar een specifieke kwetsbaarheid te kijken op basis van een Integer-underflow in een zeer populaire bestandsdecompressor.Dit is een fout voor het uitvoeren van code op afstand (RCE) die wordt geactiveerd bij het verwerken van gecomprimeerde bestanden met het Zstandard-algoritme, met een hoge, maar niet maximale, CVSS-score, juist omdat hiervoor een iets specifiekere context nodig is (bijvoorbeeld dat specifieke bestanden worden verwerkt).

Een integer underflow treedt op wanneer een integer variabele met goed gedefinieerde ondergrenzen Het wordt gebruikt bij aftrekbewerkingen totdat de minimumwaarde is bereikt.Omdat veel integer-typen geen negatieve waarden toestaan, treedt er bij het "onder" gaan een sprong op naar de maximaal mogelijke waarde binnen het bereik in plaats van een getal onder nul, vanwege de manier waarop die getallen intern worden weergegeven.

  Onderteken scripts en beveilig het uitvoeringsbeleid met AppLocker en WDAC.

Stel je een integer variabele voor die in theorie nooit negatief zou mogen zijn. Als je een constante waarde blijft aftrekken tot je bij 0 komt en vervolgens doorgaat met aftrekken, Je krijgt geen -1, -2, enz.In plaats daarvan zal de waarde omkeren en een gigantisch getal worden aan de bovengrens van het toegestane bereik. Dit absurde getal kan, indien gebruikt als index of grootte bij geheugenbewerkingen, leiden tot lees- of schrijfbewerkingen buiten de beoogde limieten.

Het probleem wordt cruciaal wanneer die integer-variabele die kan onderlopen wordt gebruikt voor om een ​​buffer te beheren of om offsets te berekenenEen typisch pseudo-voorbeeld zou zijn het indirect gebruiken van een door de gebruiker gecontroleerde variabele als offset in een memcpy-achtige functie, in een lus die in elke iteratie een constante verlaagt totdat deze zogenaamd nul of een niet-positief getal bereikt.

In zo'n scenario, als de beginwaarde nooit "netjes" aan de exitvoorwaarde voldoet, zal de onderloop het getal uiteindelijk enorm groot maken, de lus zal blijven draaien en een massale corruptie van het geheugenDit vertaalt zich in crashes en, met voldoende exploitatiewerk, de mogelijkheid om willekeurige code uit te voeren.

Hoe AFL helpt bij het opsporen van dit soort kwetsbaarheden.

Als het concept van integer underflow eenmaal begrepen is, wordt duidelijk waarom AFL en AFL++ zo nuttig zijn: beginnend bij één of enkele volkomen geldige invoerbestandenDe fuzzer kan tienduizenden variaties genereren, totdat hij de variant vindt die de specifieke bug in het betreffende decompressiepad activeert.

Tijdens het fuzzingproces controleert AFL het geïnstrumenteerde binaire bestand om te bepalen wanneer een invoer ervoor zorgt dat een ander stroomcontrolepad wordt uitgevoerd. Telkens wanneer een nieuw pad wordt gedetecteerd, Markeer die vermelding als interessant. en bewaart het als basis voor toekomstige mutaties. Op deze manier worden niet alleen de gebruikelijke uitvoeringspaden verkend, maar ook die combinaties van gegevens die ongebruikelijke coderoutes activeren, zoals het decompressieproces met vreemde parameters.

In het geval van een integer-underflow kan AFL een voorbeeld vinden dat ervoor zorgt dat de betreffende variabele in de lus nooit de verwachte uitvoerwaarde bereikt. Het resultaat is een detecteerbare crash, die wordt opgeslagen in de bijbehorende AFL-resultatenmap, klaar voor verdere analyse.

Na een paar minuten of uren van uitvoering geeft AFL doorgaans aan dat de uitvoering is voltooid. eerste relevante crashNiet alle gevonden bugs zullen exploiteerbaar zijn, maar ze zijn wel een duidelijke indicatie dat er iets mis is met het geheugenbeheer, de invoervalidatie of de interne logica van het binaire bestand.

Dat is waar ondersteunende tools zoals AFLTriage of een debugger zoals GDB van pas komen. Hiermee kun je dieper in de crash duiken, de status van de registers en het geheugen bekijken en beoordelen of de fout betrouwbaar kan worden uitgebuit of dat het slechts een vastgelopen systeem is dat vanuit het oogpunt van de aanvaller weinig nut heeft.

Basisinstallatie van AFL/AFL++ en analyseomgeving

Het opzetten van de minimale omgeving voor fuzzing met AFL op een standaard Linux-systeem is vrij eenvoudig. In veel distributies is het voldoende om het pakket te installeren dat overeenkomt met de geïnstrumenteerde compilers, bijvoorbeeld met een commando zoals installeer afl-g++ of het equivalente AFL++-pakket Neem de hele suite mee.

Daarmee worden zowel de fuzzing-tools (zoals afl-fuzz) zoals gespecialiseerde compilers (afl-gcc, afl-g++ en hun moderne varianten in AFL++). Deze compilers gebruik je wanneer je je doelprogramma wilt hercompileren met instrumentatie, wat essentieel is om het maximale uit coverage-guided fuzzing te halen.

Naast de fuzzer zelf is een goede debugomgeving vrijwel onmisbaar. Een uitgebreide GDB met plugins zoals PEDA of GEF Het maakt het leven een stuk gemakkelijker dankzij extra commando's, handige geheugenopmaak, stackvisualisaties en snelkoppelingen voor het controleren van registers en geheugenkaarten.

Als je GDB nog niet hebt, kun je het installeren via de pakketbeheerder van je distributie. Integreer vervolgens de gewenste plugin (bijvoorbeeld door GEF of PEDA te downloaden vanuit hun repositories en toe te voegen aan je GDB-configuratiebestand, of door het commando `source` in de debugger zelf te gebruiken).

Met deze "GDB op steroïden" kun je geïnstrumenteerde binaire bestanden analyseren die crashen onder fuzzing, en de download ervan. invoer die de crash veroorzaakt en volg stap voor stap wat er precies op het moment van de fout gebeurt tijdens de uitvoering.

Compileer een doelbinair bestand met AFL voor fuzzing.

Zodra je AFL of AFL++ gereed hebt, is de volgende stap voor serieus fuzzing: Compileer het doel met de AFL-compilers.en dat te doen met de juiste opties om zowel de analyse als de potentiële exploitatie te vergemakkelijken, in het geval dat het doel van de oefening offensief onderzoek is.

  SmartScreen blokkeert legitieme apps: wat gebeurt er en wat kunt u doen?

Bij grote projecten, zoals een decompressor of een archiveringsprogramma, is het raadzaam om debugsymbolen in te schakelen (optie DEBUG of vlaggen zoals -gen minimaliseer optimalisaties (bijvoorbeeld door -O0 in plaats van -O2 te gebruiken). Dit helpt enorm bij het begrijpen van wat er in de broncode gebeurt in relatie tot de waargenomen crash.

Je moet doorgaans het juiste makefile of buildbestand vinden en Wijzig de regels waarin de compilatievlaggen zijn gedefinieerd.Dit kan onder meer inhouden dat een DEBUG-variabele wordt geactiveerd, een specifiek optimalisatieniveau wordt afgedwongen en, het allerbelangrijkste, dat CC en CXX worden vervangen door afl-gcc en afl-g++ (of de equivalente AFL++-tools).

Je zou de build bijvoorbeeld kunnen starten met iets als CC=afl-gcc en CXX=afl-g++ samen met het juiste makefile. Het resultaat is een geïnstrumenteerd binair bestand dat de benodigde hooks bevat, zodat AFL de code coverage tijdens de uitvoering kan meten.

Wanneer de compilatie voltooid is, is het raadzaam om te controleren of het resulterende binaire bestand daadwerkelijk debugsymbolen en de juiste architectuur bevat, met behulp van een commando zoals het uitvoerbare bestand overschrijvenDit zorgt ervoor dat je niet blindelings aan het fuzzen bent en dat je eventuele crashes die zich voordoen gemakkelijk kunt debuggen.

Voorbereiding van testgevallen en uitvoering van AFL

Voordat je AFL op je binaire bestand uitvoert, moet je een aantal zaken voorbereiden. minimaal aantal geldige invoerAls u een decompressor test die problemen ondervindt met bestanden die zijn gecomprimeerd met een specifiek algoritme, moet u ten minste één correct opgemaakt bestand maken dat die compressiemethode gebruikt.

De gebruikelijke werkwijze is om een ​​map aan te maken, bijvoorbeeld genaamd 'testcases', en daarin een of meer werkende voorbeelden op te slaan. Deze dienen als uitgangspunt voor AFL. genereert steeds agressievere mutatiesHet programma probeert nieuwe uitvoeringspaden te ontdekken, wat uiteindelijk tot crashes leidt.

Het gebruikelijke commando om AFL te starten ziet er ongeveer zo uit: geef een sessie- of synchronisatienaam op, de invoermap met testgevallen, het uitvoerpad waar de resultaten worden opgeslagen en enkele extra parameters zoals de time-out om te voorkomen dat het programma in oneindige lussen terechtkomt.

De opdrachtregel specificeert ook hoe het doelprogramma wordt uitgevoerd. het scorebord @@die AFL automatisch zal vervangen door het pad van elk testgeval dat het genereert. Je kunt dus zoiets specificeren als: ./binaire argumenten @@ En AFL is verantwoordelijk voor het duizenden keren starten van het uitvoerbare bestand met verschillende bestanden.

Het is belangrijk om te weten dat AFL in eerste instantie mogelijk een foutmelding geeft over de systeemconfiguratie (vanwege resourcebeperkingen, core dumps, enz.). In dat geval is er meestal een hulpscript of -tool beschikbaar die de kernel- en sessieparameters aanpast om de fuzzing-omgeving te optimaliseren. Deze tool moet met de juiste machtigingen worden uitgevoerd voordat de test wordt herhaald.

Ongevallenanalyse met AFLTriage en GDB

Na een periode van 'fuzzing' zal AFL de uitvoermap geleidelijk vullen met interessante nieuwe items en vooral met... bestanden die crashes hebben veroorzaaktDeze worden meestal opgeslagen in een specifieke submap (bijvoorbeeld crashes in de sessiemap).

Om te voorkomen dat je gek wordt van het één voor één controleren van elke invoer, zijn er tools zoals AFLTriage waarmee je dat kunt doen. Voer het binaire bestand automatisch uit voor elk crashbestand. en gestructureerde rapporten genereren met details over waar en hoe de storing is opgetreden.

Bij het starten van AFLTriage specificeert u de invoermap (de map met de crashrapporten), een uitvoermap voor het opslaan van de rapporten en de opdracht om het binaire bestand uit te voeren met behulp van @@. De tool analyseert elk geval en Het programma genereert tekstbestanden met de relevante informatie. voor de analist.

De typische inhoud van deze rapporten omvat het type signaal dat de crash veroorzaakte, de richting waarin de fout optrad, een basis stack trace en, in veel gevallen, de functie of macro waar de code vastliep (bijvoorbeeld een specifieke kopieerbewerking, een compressielus of een COPY_CHUNKS-macro die de groottes in elke iteratie aftrekt).

Zodra een interessante crash is gevonden, is de volgende stap om deze te reproduceren in GDB. Hiervoor wordt de debugger gestart met het binaire bestand en het betreffende invoerbestand als argumenten, waarna het programma vanuit GDB wordt uitgevoerd. Wanneer de crash is gereproduceerd, wordt de status van Belangrijke gegevens zoals RAX, RDX, enz.Controleer de exacte instructie die wordt uitgevoerd en raadpleeg de geheugenkaarten (vmmap) om te zien naar welk gebied er wordt gelezen of geschreven.

Van crash tot potentiële exploitatie: analysemodules in GDB

Het feit dat een binair bestand uitvalt, betekent niet altijd dat de kwetsbaarheid ook daadwerkelijk te exploiteren is. Dit is waar GDB-extensies zoals de module van pas komen. verwerkbaar, ontworpen om de context van een ongeval te analyseren en een ruwe classificatie te geven van de vraag of de fout al dan niet kan worden uitgebuit.

Dit soort uitbreidingen onderzoekt aspecten zoals de geldigheid van de instructiepointer, de status van de stackpointer, de geheugenrechten van het adres waar de lees- of schrijfpoging wordt gedaan, en de aard van het signaal dat de crash veroorzaakte. Met al deze informatie leveren ze een indicatief oordeel over exploitatiepotentieel (bijvoorbeeld door de bug te categoriseren als waarschijnlijk exploiteerbaar, mogelijk exploiteerbaar of van weinig belang).

  Hoe voorkom je kwaadaardige links in Microsoft Teams?

In het geval van een integer-underflow die ertoe leidt dat er naar alleen-lezen geheugen wordt geschreven, kan de plugin suggereren dat dit een potentieel exploiteerbaar geval is, omdat een schrijfbewerking wordt uitgevoerd buiten de verwachte limieten in een gebied waar niets aangeraakt zou mogen worden.

Niet alle scenario's zullen zo eenvoudig zijn, maar deze combinatie van AFL om de crash te ontdekken, AFLTriage om de gevallen te groeperen en te beschrijven, en GDB met uitbreidingen om de exploiteerbaarheid te beoordelen, vormt een Zeer robuuste binaire auditpipelineVanaf dat moment bestaat het resterende werk puur uit onderzoek en ontwikkeling van de exploit, iets wat enorm kan variëren in complexiteit, afhankelijk van het binaire bestand en de actieve beveiligingen.

Een ander belangrijk onderdeel van de analyse, met name wanneer een CVE en bijbehorende patch worden gepubliceerd, is het beoordelen hoe de kwetsbaarheid in de broncode is verholpen. Dit houdt meestal in dat... wijzigingen in het type gegevens dat wordt gebruikt (bijvoorbeeld het omzetten van een getekende byte naar een niet-getekende byte, waardoor het bereik wordt uitgebreid) en het toevoegen van extra controles (if-statements die foutcodes retourneren als bepaalde limieten worden overschreden) om underflow of geheugenbeschadiging te voorkomen.

Andere relevante fuzzers en tools in het ecosysteem

Hoewel AFL en AFL++ tot de bekendste opties behoren voor het fuzzen van native binaire bestanden, is het fuzz-testecosysteem veel breder en zeker de moeite waard om in de gaten te houden als je regelmatig met dergelijke programma's werkt. softwareveiligheid.

Een van de meest gebruikte opties is LibFuzzer, een bibliotheek die oorspronkelijk door Google is ontwikkeld en die direct integreert met C/C++-code en analyses uitvoert. Fuzzing op het niveau van specifieke functiesIdeaal voor het testen van individuele componenten met zeer nauwkeurig gecontroleerde ingangen.

Er is ook Peach Fuzzer, een meer algemeen toepasbaar en configureerbaar platform waarmee je kunt definiëren Nauwkeurige modellen van protocollen, bestandsformaten en complexe structurenHet is erg populair in sectoren waar betrouwbaarheid cruciaal is, zoals industriële systemen of IoT-omgevingen.

In het webdomein bevatten tools zoals OWASP ZAP fuzzing-modules die specifiek zijn ontworpen voor webapplicaties en die in staat zijn om... gemanipuleerde verzoeken, onjuist geformuleerde laadgegevens en injectietests HTTP-eindpunten voor het detecteren van kwetsbaarheden zoals SQL-injecties, problemen met invoervalidatie of fouten bij de verwerking van formulieren.

Dit alles vormt een aanvulling op AFL/AFL++ binaire fuzzing, omdat het dekking biedt van laag-niveau native code tot de applicatielagen die het meest blootgesteld worden aan de eindgebruiker. Het gemeenschappelijke doel is in alle gevallen hetzelfde: het automatiseren van de zoektocht naar kwetsbaarheden voordat aanvallers dat doen.

Hoe integreer je fuzz-testen in de ontwikkelingscyclus?

Om met fuzzing consistent echte waarde te behalen, is het niet voldoende om af en toe AFL of AFL++ op te starten en er verder geen aandacht meer aan te besteden. Het is veel effectiever. Fuzz-testen integreren in de softwareontwikkelingscyclusOp een vergelijkbare manier als bij unit- of integratietests.

De eerste stap is het definiëren welke onderdelen van het systeem het meest cruciaal zijnBestandsparsers, netwerkmodules, componenten die gebruikersgegevens verwerken of extern toegankelijke services. Specifieke fuzzingcampagnes kunnen rondom deze doelen worden ontworpen, met behulp van zorgvuldig geselecteerde testgevallen.

Vervolgens moet je voor elk geval de juiste tool kiezen. Voor complexe native binaire bestanden zijn AFL++ of LibFuzzer meestal geschikte kandidaten, terwijl voor web-API's of HTTP-applicaties een webgeoriënteerde tool meer geschikt is. Het belangrijkste is dat de fuzzing correct wordt uitgevoerd. herhaalbaar en automatiseerbaar.

Integratie met CI/CD-systemen is erg nuttig: de pipeline kan zo worden geconfigureerd dat in bepaalde branches of vóór bepaalde deployments een fuzzing-test met een beperkte tijdsduur wordt uitgevoerd, waarbij eventuele nieuwe crashes worden opgeslagen en de build mislukt als er potentieel ernstige kwetsbaarheden aan het licht komen.

Tot slot is het belangrijk te begrijpen dat fuzzing een iteratief proces is. Telkens wanneer een bug is verholpen, moet het betreffende onderdeel opnieuw worden getest om te controleren of de patch werkt en of er geen nieuwe bugs zijn geïntroduceerd. geen nieuwe regressies of kwetsbaarhedenOp deze manier wordt fuzz-testen een extra laag van continue bescherming en geen geïsoleerde controle.

Al het bovenstaande laat zien hoe AFL, AFL++ en fuzz-testen in het algemeen ons in staat stellen om over te stappen van een beveiligingsaanpak die uitsluitend gebaseerd is op handmatige controles naar een meer omvattende aanpak. veel meer geautomatiseerd en grondig.Het is in staat om fouten aan het licht te brengen die anders verborgen zouden blijven. Door te werken met praktijkvoorbeelden, zoals underflow-kwetsbaarheden in veelgebruikte decompressors, wordt duidelijk dat dit niet alleen theorie is, maar dat problemen daadwerkelijk worden uitgebuit. Daarom kan het regelmatig updaten van software en het uitvoeren van fuzzing-tests het verschil maken tussen een gecompromitteerd systeem en een systeem dat bestand is tegen aanvallen.