Fuzzing met American Fuzzy Lop – Quickstart

Veilig coderen is een zeer moeilijke praktijk. Wanneer programmeurs software ontwikkelen, is hun doel meestal om de software te laten werken in plaats van te breken. Tijdens dit proces kunnen kwetsbaarheden ontstaan in gevallen waar een legacy functie werd gebruikt in plaats van een veiligere.

C is een van die talen die van nature zeer veelzijdig en krachtig zijn, maar er kleeft één nadeel aan: de beveiliging van software in C is afhankelijk van de kennis van de programmeur. Dit betekent dat als de programmeur goed op de hoogte is van veilig coderen, zijn software ook veilig zal zijn. Aan de andere kant, en dit vormt de grootste brok, als de programmeur niet geavanceerd genoeg is, zullen er mazen in hun software zitten die uiteindelijk leiden tot een exploit.

Voor mensen zoals ik, die verstand hebben van programmeren maar nieuw zijn in de beveiligingsindustrie, is het erg belangrijk om kwetsbare code te bestuderen en de mogelijke gevolgen te begrijpen. Dit helpt bij het verfijnen van codeervaardigheden en het ontwikkelen van een aanvallershouding tijdens de codeerfase in plaats van erna.

In alle eerlijkheid, het is nogal omslachtig om de complete broncode van een applicatie te bestuderen als je op zoek bent naar kwetsbaarheden zoals buffer overflows. Hoewel deze methode zijn eigen verdiensten heeft, is het niet de gemakkelijkste methode om eenvoudige kwetsbaarheden te vinden die kritisch kunnen zijn. Dergelijke kwetsbaarheden moeten onmiddellijk worden verholpen en de eenvoudigste manier om ze te vinden is door middel van een techniek die Fuzzing wordt genoemd.

Fuzzing is een techniek om “eenvoudige” kwetsbaarheden in code te vinden door “willekeurig” gegenereerde gegevens naar een uitvoerbaar programma te sturen. In het algemeen zijn er drie soorten fuzzers:

  1. Mutatie: Een type “domme” fuzzing waarbij misvormde input samples worden gegenereerd en aan de executable worden verstrekt. Deze invoer kan al dan niet overeenkomen met het type invoer dat door de toepassing wordt verwacht, zodat de kans op het vinden van echte bugs niet groot is.
  2. Generatie: Een type “slimme” fuzzing die een aantal initiële testgegevens vereist waaruit het fuzzer-algoritme misvormde input kan genereren vanuit het niets. Dit type fuzzing is in veel gevallen beter dan domme fuzzing, omdat het programma de invoer krijgt die het verwacht.
  3. Evolutionair: Dit type fuzzers gebruikt feedback van elke “fuzz” om na verloop van tijd het formaat van de invoer te leren.

In dit bericht kijken we naar fuzzing met American Fuzzy Lop (AFL). Het is een type evolutionaire fuzzer die geschikt is om programma’s te fuzzen die invoer krijgen van STDIN of een bestand.

Er zijn een heleboel fuzzers in het wild, waaronder Peach en syzkaller. Dus waarom AFL?

  1. De use-case. Dit is het belangrijkste punt om te overwegen. Mijn use-case was om een applicatie te fuzzen die invoer uit een bestand haalt. Het is belangrijk om op te merken dat AFL niet de mogelijkheid heeft om over netwerken te fuzzen.
  2. Het is eenvoudig te installeren.
  3. AFL’s UI interface bevat een ton aan informatie, inclusief real-time statistieken van het fuzzing proces.

Instellen AFL

Het instellen van AFL is eenvoudig en ik heb het u gemakkelijker gemaakt door een eenvoudig (maar ruw) shell script te schrijven dat het voor u zal installeren! Voer het script uit met uw gebruikersrechten en het zal alle afhankelijkheden, AFL en gerelateerde tools installeren. Het shell script kan hier gevonden worden: https://github.com/nikhilh-20/enpm691_project/blob/master/install_afl.sh

Kies de applicatie om te fuzzen

In dit bericht zullen we alleen kijken naar het fuzzen van applicaties waarvan we de broncode hebben. De reden hiervoor is dat AFL de broncode instrumenteert om uitvoering, fouten en andere zaken gerelateerd aan performance te monitoren. Het is ook mogelijk om direct een executable te fuzzen, maar dat is experimenteel en buiten het bereik van deze post (tip: het vereist QEMU).

Kies een open source systeem van GitHub om te fuzzen. Hoe bekender je keuze, hoe minder kwetsbaarheden het waarschijnlijk zal hebben. Anderen zijn ook op zoek naar bugs! Een eenvoudige methode die ik gebruik om kwetsbare code te vinden is om GitHub Search te gebruiken. Dit is wat ik doe:

  1. Zoek naar een kwetsbare functie, bijvoorbeeld strcpy.
  2. De resultaten zullen in de miljoenen lopen. Ga naar de commits categorie van de resultaten. Hier vind je de repositories waar strcpy is gebruikt (of misschien verwijderd). Deze repositories zijn een goed startpunt om te beginnen met fuzzing.

Instrumenting the Application

Om privacy redenen kan ik de repository die ik gebruik niet vrijgeven.

Kloon de git repository.

nikhilh@ubuntu:~$ git clone https://github.com/vuln; cd vuln

Stel een omgevingsvariabele in, AFL_HARDEN=1. Dit activeert bepaalde code hardening opties in AFL tijdens het compileren, wat het makkelijker maakt om geheugencorruptie bugs te detecteren.

nikhilh@ubuntu:~/vuln$ export AFL_HARDEN=1

Stel bepaalde compiler vlaggen in, zodat de applicatie wordt gecompileerd op een manier die het voor ons makkelijk maakt om kwetsbaarheden te vinden (en uit te buiten). Idealiter zouden we omgevingsvariabelen gebruiken om in te stellen wat we nodig hebben, maar er is nogal wat aan te passen. dus we zullen direct de Makefile bewerken.

Zorg ervoor dat de gebruikte compiler afl-gcc of afl-clang is in plaats van respectievelijk gcc en clang. Dit stelt AFL in staat om de broncode te instrumenteren.

Voeg compiler flags toe:

-fno-stack-protector schakelt de stack protector uit, waardoor we buffer overflows kunnen misbruiken.

-m32 is alleen nodig als je een 32-bit machine gebruikt, anders niet.

Als je klaar bent met deze veranderingen, is het tijd om de applicatie te compileren. Start make. Als je dat doet, MOET je verklaringen zien zoals deze in het log:

Instrumented 123 locations (32-bit, hardened-mode, ratio 100%).

Als je zulke verklaringen niet ziet, betekent dit dat AFL de code van de applicatie niet heeft geactiveerd voor fuzzing. Met andere woorden, het heeft de broncode niet succesvol geïnstrumenteerd.

Test Samples

AFL is een evolutionair type fuzzer. Dat betekent dat het, net als generatie-gebaseerde fuzzers, ook initiële testgegevens nodig heeft om te begrijpen wat voor soort gegevens de doelapplicatie verwacht. Als je je richt op open source systemen, is dit makkelijk te vinden. Kijk gewoon in hun test directory en je zult alle test data vinden die je nodig hebt.

nikhilh@ubuntu:~/vuln$ mkdir afl_in afl_out

nikhilh@ubuntu:~/vuln$ cp test/* afl_in/

Fuzzing Begins

Nu we onze test samples hebben, zijn we klaar om te fuzzen!

Oh, wacht… we moeten ook veranderen waar de crashmeldingen van de applicatie naar toe gaan. By default, when an application crashes, the core dump (basically, the contents of RAM are stored in a file to help in debugging) notification is sent to the system’s core handler. We don’t want this. Why? By the time this notification reaches AFL, it’ll be classified as a timeout rather than a crash.

nikhilh@ubuntu:~/vuln$ sudo su

password for nikhilh:

root@ubuntu:/home/nikhilh/vuln# echo core > /proc/sys/kernel/core_pattern

root@ubuntu:/home/nikhilh/vuln# exit

NOW, we are ready to fuzz!

nikhilh@ubuntu:~/vuln$ afl-fuzz -i afl_in -o afl_out -S slaveX — ./vuln @@

Command line flags used:

  1. -i — This marks the test input directory. Dit is waar we de initiële testgegevens hebben opgeslagen.
  2. -o- Dit is de directory waar de AFL nuttige informatie over crashes, hangs, enz. wegschrijft.
  3. -S – Dit is de Slave-modus. In principe zal AFL de invoer willekeurig tweaken, wat niet-deterministische fuzzing veroorzaakt.
  4. De -M optie is de Master mode, dat is deterministische fuzzing, wat in feite betekent dat elk bit van de invoer op een of andere manier wordt gewijzigd. (Dit is traag! … Uiteraard.)
  5. @@ – Dit is de positie waar het invoer test bestand zal zijn. AFL vervangt dit automatisch voor je. Als je executable input neemt van STDIN, dan is dit niet nodig.

Fuzzing Results

Het zal enige tijd duren om dit te laten zien. Vaak fuzzen mensen meer dan 24 uur (en eindigen dan met niets). In mijn geval, denk ik dat de applicatie een beetje te kwetsbaar was, dus ik had 516 unieke crashes binnen een uur. Dat wil echter niet zeggen dat er 516 kwetsbaarheden zijn!

Je kunt de fuzz-sessie afbreken met een Ctrl-C.

Analysis Phase

Now that we have results, we need to analyze them to see which ones are exploitable. To this end, we will use one of AFL’s utilities called afl-collect. This will have been installed through the installation script as well.

nikhilh@ubuntu:~/afl-utils$ afl-collect -d crashes.db -e gdb_script -r -rr ~/vuln/afl_out/slaveX ./output_dir — ~/vuln/vuln

To understand what each command line flag does, refer to its help section.

nikhilh@ubuntu:~/afl-utils$ afl-collect — help

If you see lines such as these in the output, celebrate! You’ve found something interesting to try and exploit.

*** GDB+EXPLOITABLE SCRIPT OUTPUT ***

slaveX:id:000000,sig:11,src:000000,op:havoc,rep:2……………:EXPLOITABLE

slaveX:id:000046,sig:11,src:000004,op:havoc,rep:4……………:EXPLOITABLE

AFL zal je laten zien welke invoer de applicatie heeft laten crashen. In dit geval veroorzaakte het bestand: id:000046,sig:11,src:000004,op:havoc,rep:4 een StackBufferOverflow in de applicatie. Dergelijke bestanden kunnen worden gevonden onder ../afl_out-slaveX/crashes/

Done!

Dat is het voor een snelle start in fuzzing! Het proces is echt eenvoudig en zeer handig omdat het allemaal geautomatiseerd is. De volgende stap zou zijn om te analyseren waarom de input een Buffer Overflow veroorzaakt en te zoeken naar een manier om het uit te buiten. Vergeet niet dat niet alle kwetsbaarheden kunnen leiden tot een exploit.

Geef een antwoord

Het e-mailadres wordt niet gepubliceerd.