Fuzzing med American Fuzzy Lop – Quickstart

Säker kodning är en mycket svår uppgift. När programmerare utvecklar programvara är deras mål vanligtvis att få programvaran att fungera snarare än att gå sönder. I den här processen kan sårbarheter utvecklas i de fall då en gammal funktion har använts i stället för en säkrare funktion. Därför är äldre programvara särskilt sårbar.

C är ett av de språk som i sig är mycket mångsidigt och kraftfullt, men det har en kritisk nackdel – C-baserad programvarusäkerhet är beroende av programmerarens kunskaper. Detta innebär att om programmeraren är väl medveten om säker kodning kommer deras programvara också att vara säker. Å andra sidan, och det är detta som utgör den största delen, om programmeraren inte är tillräckligt sofistikerad kommer det att finnas kryphål i programvaran som i slutändan leder till en exploatering.

För personer som jag, som kan programmering men som är nya inom säkerhetsbranschen, är det mycket viktigt att studera sårbar kod och förstå de möjliga konsekvenserna. Detta hjälper till att förfina kodningsfärdigheterna och utveckla en angriparattityd UNDER kodningsfasen snarare än EFTER att ha kodat hela programvaran.

I ärlighetens namn är det ganska besvärligt att studera hela källkoden för ett program när man letar efter sårbarheter som buffertöverflöden. Även om denna metod har sina egna förtjänster är det inte den enklaste metoden för att hitta enkla sårbarheter som kan vara kritiska. Sådana sårbarheter måste lösas omedelbart och det enklaste sättet att hitta dem är genom en teknik som kallas Fuzzing.

Fuzzing är en teknik för att hitta ”enkla” sårbarheter i kod genom att sända ”slumpmässigt” genererade data till en körbar kod. I allmänhet finns det tre typer av fuzzers:

  1. Mutation: En typ av ”dum” fuzzing där missbildade inmatningsprov genereras och skickas till den körbara datorn. Denna inmatning kan överensstämma eller inte överensstämma med den typ av inmatning som förväntas av programmet, så sannolikheten att hitta riktiga fel är inte hög.
  2. Generering: En typ av ”smart” fuzzing som kräver vissa initiala testdata från vilka fuzzeralgoritmen kan generera missbildad indata från grunden. Denna typ av fuzzing är bättre än dumb fuzzing i många fall eftersom programmet får den indata som det förväntar sig.
  3. Evolutionär: Den här typen av fuzzer använder feedback från varje ”fuzz” för att med tiden lära sig formatet på inmatningen.

I det här inlägget ska vi titta på fuzzing med American Fuzzy Lop (AFL). Det är en typ av evolutionär fuzzer som lämpar sig för att fuzza program som tar inmatning från STDIN eller en fil.

Det finns en mängd fuzzers i naturen, däribland Peach och syzkaller. Så varför AFL?

  1. Användningsfallet. Detta är den viktigaste punkten att ta hänsyn till. Mitt användningsfall var att fuzza ett program som tar emot indata från en fil. Det är viktigt att notera att AFL inte har möjlighet att fuzza över nätverk.
  2. Det är enkelt att installera.
  3. AFL:s användargränssnitt innehåller massor av information, bland annat realtidsstatistik över fuzzingprocessen.

Installation av AFL

Installation av AFL är enkelt och jag har gjort det enklare för dig genom att skriva ett enkelt (men grovt) skalskript som installerar det åt dig! Kör skriptet med dina användarrättigheter och det kommer att installera alla beroenden, AFL och relaterade verktyg. Skalskriptet finns här: https://github.com/nikhilh-20/enpm691_project/blob/master/install_afl.sh

Välj programmet att fuzza

I det här inlägget kommer vi bara att titta på fuzzning av de program som vi har källkoden till. Detta beror på att AFL instrumenterar källkoden för att övervaka exekvering, fel och andra saker som rör prestanda. Det är också möjligt att direkt fuzza en körbar fil men det är experimentellt och ligger utanför det här inlägget (tips: det kräver QEMU).

Välj ett valfritt system med öppen källkod från GitHub för fuzzing. Ju mer välkänt ditt val är, desto mindre antal sårbarheter kommer det sannolikt att ha. Andra letar också efter buggar! En enkel metod som jag använder för att hitta sårbar kod är att använda GitHub Search. Så här gör jag:

  1. Sök efter en sårbar funktion, säg strcpy.
  2. Resultaten kommer att vara i miljontals. Gå till kategorin commits i resultaten. Det är där du hittar de repositories där strcpy användes (eller kanske togs bort). Dessa repositories är en bra utgångspunkt för att börja fuzza.

Instrumentering av applikationen

Av integritetsskäl kan jag inte avslöja det repository som jag använder.

Klona git-förrådet.

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

Sätt en miljövariabel, AFL_HARDEN=1. Detta aktiverar vissa kodhärdningsalternativ i AFL under kompileringen, vilket gör det lättare att upptäcka minneskorruptionsfel.

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

Inställ vissa kompilatorflaggor, så att programmet kompileras på ett sätt som gör det lätt för oss att hitta (och utnyttja) sårbarheter. Helst skulle vi använda miljövariabler för att ställa in det vi behöver, men det finns en hel del anpassningsmöjligheter. så vi redigerar Makefile direkt.

Säkerställ att kompilatorn som används är afl-gcc eller afl-clang istället för gcc respektive clang. Detta gör det möjligt för AFL att instrumentera källkoden.

Lägg till kompilatorflaggor:

-fno-stack-protector stänger av stackprotektorn, vilket gör det möjligt för oss att utnyttja buffertöverflöden.

-m32 behövs bara om du använder en 32-bitarsmaskin, annars nej.

När du är klar med dessa ändringar är det dags att kompilera programmet. Kör make. När du gör det MÅSTE du se uttalanden som dessa i loggen:

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

Om du inte ser sådana uttalanden betyder det att AFL inte har aktiverat applikationens kod för fuzzing. Med andra ord har den inte instrumenterat källkoden framgångsrikt.

Testprov

AFL är en evolutionär typ av fuzzer. Det innebär att den, liksom generationsbaserade fuzzerare, också kräver inledande testdata för att förstå vilken typ av data som målprogrammet förväntar sig. När man riktar in sig på system med öppen källkod är detta lätt att hitta. Titta bara i deras testkatalog så hittar du alla testdata du behöver.

nikhilh@ubuntu:~/vuln$ mkdir afl_in afl_out

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

Fuzzing Begins

Nu när vi har våra testprover är vi redo att fuzza!

Oh, vänta… vi måste också ändra var applikationens kraschmeddelanden går till. 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. Det är här vi lagrade de ursprungliga testdata.
  2. -o- Detta är katalogen där AFL skriver användbar information om krascher, hängningar etc.
  3. -S – Detta är slavläget. I princip kommer AFL att slumpmässigt modifiera inmatningen vilket orsakar icke-deterministisk fuzzing.
  4. -M-alternativet är Master-läget som är deterministisk fuzzing, vilket i princip innebär att varje bit av inmatningen modifieras på något sätt. (Detta är långsamt! … Uppenbarligen.)
  5. @@ – Detta är den position där testfilen för indata kommer att vara. AFL ersätter detta för dig automatiskt. Om din körbara fil tar inmatning från STDIN behövs inte detta.

Fuzzing Results

Det här kommer att ta lite tid att visa. Det händer ofta att folk fuzzar i mer än 24 timmar (och kanske slutar med ingenting). I mitt fall tror jag att applikationen var lite för sårbar, så jag hade 516 unika krascher inom en timme. Det betyder dock inte att det finns 516 sårbarheter!

Du kan avsluta fuzzing-sessionen med en 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 visar dig vilken indata som fick programmet att krascha. I det här fallet orsakade filen: id:000046,sig:11,src:000004,op:havoc,rep:4 ett StackBufferOverflow i programmet. Sådana filer kan hittas under ../afl_out-slaveX/crashes/

Done!

Det var allt för en snabbstart i fuzzing! Processen är verkligen enkel och mycket bekväm eftersom allt är automatiserat. Nästa steg skulle vara att analysera varför inmatningen orsakade ett buffertöverflöde och söka efter ett sätt att utnyttja det. Kom ihåg att inte alla sårbarheter kan leda till en exploatering.

Lämna ett svar

Din e-postadress kommer inte publiceras.