Fuzzing az amerikai Fuzzy Lop segítségével – Gyorstalpaló

A biztonságos kódolás nagyon nehéz gyakorlat. Általában, amikor a programozók szoftvert fejlesztenek, az a céljuk, hogy a szoftver működjön, és ne törjön össze. Ebben a folyamatban sebezhetőségek alakulhatnak ki olyan esetekben, amikor egy régebbi funkciót használtak egy biztonságosabb helyett. Következésképpen az örökölt szoftverek különösen sebezhetőek.

A C egyike azoknak a nyelveknek, amelyek eleve nagyon sokoldalúak és erősek, de van egy kritikus hátránya – a C-alapú szoftverek biztonsága a programozó tudásától függ. Ez azt jelenti, hogy ha a programozó tisztában van a biztonságos kódolással, akkor a szoftvere is biztonságos lesz. Másrészt, és ez képezi a fő darabot, ha a programozó nem elég kifinomult, akkor a szoftverükben lesznek kiskapuk, amelyek végül kihasználáshoz vezetnek.

A hozzám hasonló, a programozásban jártas, de a biztonsági iparágban új embereknek nagyon fontos a sebezhető kód tanulmányozása és a lehetséges következmények megértése. Ez segít a kódolási készségek finomításában és a támadói attitűd kialakításában a kódolási fázis alatt, nem pedig a teljes szoftver kódolása UTÁN.

Őszintén szólva elég nehézkes egy alkalmazás teljes forráskódját tanulmányozni, amikor olyan sebezhetőségeket keresünk, mint a puffer túlcsordulás. Bár ennek a módszernek megvannak a maga érdemei, nem a legegyszerűbb módszer az egyszerű sebezhetőségek megtalálására, amelyek kritikusak lehetnek. Az ilyen sebezhetőségeket azonnal orvosolni kell, és ezek megtalálásának legegyszerűbb módja a Fuzzing nevű technika.

A Fuzzing egy olyan technika, amellyel “egyszerű” sebezhetőségeket találhatunk a kódban úgy, hogy “véletlenszerűen” generált adatokat küldünk egy futtatható programnak. Általában háromféle fuzzertípus létezik:

  1. Mutáció: A “buta” fuzzing egy olyan típusa, ahol rosszul formált bemeneti mintákat generálnak és adnak a futtatható programnak. Ez a bemenet vagy megfelel, vagy nem felel meg az alkalmazás által elvárt bemenet típusának, így a valódi hibák megtalálásának valószínűsége nem nagy.
  2. Generálás: Az “intelligens” fuzzing egy olyan típusa, amelyhez szükség van bizonyos kezdeti tesztadatokra, amelyekből a fuzzer algoritmus a semmiből rossz formájú bemenetet generálhat. Ez a fajta fuzzing sok esetben jobb, mint a dumb fuzzing, mert a program azt a bemenetet kapja, amire számít.
  3. Evolúciós: Az ilyen típusú fuzzerek az egyes “fuzzok” visszajelzéseit használják arra, hogy idővel megtanulják a bemenet formátumát.

Ebben a bejegyzésben az American Fuzzy Lop (AFL) fuzzingot fogjuk megvizsgálni. Ez egyfajta evolúciós fuzzer, amely olyan programok fuzzolására alkalmas, amelyek STDIN-ből vagy egy fájlból veszik a bemenetet.

Egy csomó fuzzer van a vadonban, köztük a Peach és a syzkaller. Szóval, miért az AFL?

  1. A felhasználási eset. Ez a legfontosabb pont, amit figyelembe kell venni. Az én felhasználási esetem egy olyan alkalmazás fuzzolása volt, amely egy fájlból veszi a bemenetet. Fontos megjegyezni, hogy az AFL nem képes hálózatokon keresztül fuzzolni.
  2. Egyszerűen telepíthető.
  3. Az AFL felhasználói felülete rengeteg információt tartalmaz, beleértve a fuzzolási folyamat valós idejű statisztikáit.

Az AFL telepítése

Az AFL telepítése egyszerű, és megkönnyítettem a dolgodat azzal, hogy írtam egy egyszerű (de durva) shell scriptet, ami telepíti neked! Futtasd a szkriptet a felhasználói jogosultságaiddal, és telepíteni fogja az összes függőséget, az AFL-t és a kapcsolódó eszközöket. A shell szkriptet itt találod: https://github.com/nikhilh-20/enpm691_project/blob/master/install_afl.sh

Válasszuk ki a fuzzolni kívánt alkalmazást

Ebben a bejegyzésben csak azoknak az alkalmazásoknak a fuzzolásával foglalkozunk, amelyeknek megvan a forráskódja. Ennek oka, hogy az AFL a forráskódot instrumentálja a végrehajtás, a hibák és egyéb, a teljesítménnyel kapcsolatos dolgok megfigyeléséhez. Lehetséges közvetlenül fuzzolni egy futtatható állományt is, de ez kísérleti jellegű, és nem tartozik ennek a posztnak a tárgykörébe (tipp: ehhez QEMU szükséges).

Válaszd bármelyik nyílt forráskódú rendszert a GitHubról a fuzzoláshoz. Minél ismertebb a választásod, annál kevesebb sebezhetőséget fog valószínűleg tartalmazni. Mások is keresik a hibákat! Az egyik egyszerű módszer, amit a sebezhető kódok megtalálására használok, a GitHub Search használata. A következőt teszem:

  1. Keresd egy sebezhető függvényt, mondjuk a strcpy-t.
  2. A találatok száma milliós nagyságrendű lesz. Menj a találatok commits kategóriájába. Itt találod meg azokat a tárolókat, ahol a strcpy-t használták (vagy esetleg eltávolították). Ezek a tárolók jó kiindulópontot jelentenek a fuzzing megkezdéséhez.

Az alkalmazás műszerezése

Adatvédelmi okokból nem hozhatom nyilvánosságra az általam használt tárolót.

Klónozzuk a git-tárat.

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

Állítsunk be egy környezeti változót, AFL_HARDEN=1. Ez bizonyos kódkeményítési opciókat aktivál az AFL-ben a fordítás során, ami megkönnyíti a memóriakorrupciós hibák felderítését.

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

Beállít bizonyos fordítói zászlókat, hogy az alkalmazás úgy legyen lefordítva, hogy könnyen megtaláljuk (és kihasználjuk) a sebezhetőségeket. Ideális esetben környezeti változókkal állítanánk be, amire szükségünk van, de elég sok a testreszabás. így közvetlenül a Makefile-t fogjuk szerkeszteni.

Egyőződjünk meg róla, hogy a használt fordító afl-gcc vagy afl-clang a gcc, illetve clang helyett. Ez teszi lehetővé, hogy az AFL instrumentálja a forráskódot.

Adjunk hozzá fordítói zászlókat:

-fno-stack-protector kikapcsolja a veremvédelmet, ami lehetővé teszi számunkra a puffer túlcsordulások kihasználását.

-m32 csak akkor szükséges, ha 32 bites gépet használunk, egyébként nem.

Ha végeztünk ezekkel a módosításokkal, itt az ideje lefordítani az alkalmazást. Futtasd a make programot. Ha ez megtörtént, a naplóban ilyen utasításokat KELL látnia:

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

Ha nem lát ilyen utasításokat, az azt jelenti, hogy az AFL nem aktiválta az alkalmazás kódját fuzzingra. Más szóval nem instrumentálta sikeresen a forráskódot.

Tesztminták

Az AFL egy evolúciós típusú fuzzer. Ez azt jelenti, hogy a generáció-alapú fuzzerekhez hasonlóan neki is szüksége van kezdeti tesztadatokra, hogy megértse, milyen adatokat vár el a célalkalmazás. Ha nyílt forráskódú rendszereket veszünk célba, ezt könnyű megtalálni. Csak nézd meg a tesztkönyvtárukat, és megtalálod az összes szükséges tesztadatot.

nikhilh@ubuntu:~/vuln$ mkdir afl_in afl_out

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

A fuzzing kezdődik

Most, hogy megvannak a tesztmintáink, készen állunk a fuzzolásra!

Oh, várjunk csak… azt is meg kell változtatnunk, hogy hova mennek az alkalmazás crash értesítései. 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. Ide tároltuk a kezdeti tesztadatokat.
  2. -o- Ez az a könyvtár, ahová az AFL hasznos információkat ír az összeomlásokra, akadásokra stb. vonatkozóan.
  3. -S – Ez a Slave mód. Alapvetően az AFL véletlenszerűen módosítja a bemenetet, ami nemdeterminisztikus fuzzingot okoz.
  4. A -M opció a Master mód, ami determinisztikus fuzzing, ami alapvetően azt jelenti, hogy a bemenet minden bitje módosul valamilyen módon. (Ez lassú! … Nyilvánvalóan.)
  5. @@ – Ez az a pozíció, ahol a bemeneti tesztfájl lesz. Az AFL ezt automatikusan helyettesíti helyetted. Ha a futtatható programod STDIN-ből veszi a bemenetet, akkor erre nincs szükség.

Fuzzing eredmények

Ez egy kis időt vesz igénybe, mire megjelenik. Sokszor előfordul, hogy az emberek több mint 24 órán keresztül fuzzolnak (és lehet, hogy a végén semmit sem kapnak). Az én esetemben szerintem az alkalmazás egy kicsit túl sebezhető volt, így egy órán belül 516 egyedi összeomlás történt. Ez azonban nem jelenti azt, hogy 516 sebezhetőség van!

A fuzzing munkamenetből a Ctrl-C billentyűvel léphetsz ki.

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

AAFL megmutatja, hogy milyen input okozta az alkalmazás összeomlását. Ebben az esetben a fájl: id:000046,sig:11,src:000004,op:havoc,rep:4 StackBufferOverflow-t okozott az alkalmazásban. Az ilyen fájlok a ../afl_out-slaveX/crashes/

Done!

Ez a fuzzing gyors kezdete! A folyamat nagyon egyszerű és nagyon kényelmes, mivel az egész automatizált. A következő lépés az lenne, hogy elemezzük, miért okozott a bemenet Buffer Overflow-t, és keressük a kihasználás módját. Ne feledje, hogy nem minden sebezhetőség vezethet exploithoz.

Vélemény, hozzászólás?

Az e-mail-címet nem tesszük közzé.