Zabezpečené kódování je velmi obtížná praxe. Když programátoři vyvíjejí software, obvykle je jejich cílem, aby software fungoval, a ne aby se rozbil. Při tomto procesu mohou vzniknout zranitelnosti v případech, kdy byla použita starší funkce místo bezpečnější. V důsledku toho je starší software obzvláště zranitelný.
C je jedním z těch jazyků, které jsou ve své podstatě velmi univerzální a výkonné, ale má jednu zásadní nevýhodu – bezpečnost softwaru založeného na C závisí na znalostech programátora. To znamená, že pokud má programátor dobré znalosti o bezpečném kódování, bude bezpečný i jeho software. Na druhou stranu, a to tvoří hlavní kámen úrazu, pokud programátor není dostatečně sofistikovaný, budou v jeho softwaru mezery, které nakonec povedou ke zneužití.
Pro lidi jako já, kteří umí programovat, ale jsou v oblasti bezpečnosti nováčky, je velmi důležité prostudovat zranitelný kód a pochopit možné důsledky. Pomáhá to zdokonalovat dovednosti kódování a rozvíjet přístup útočníka PŘI kódování, nikoli PO nakódování celého softwaru.
Přímo řečeno, studovat kompletní zdrojový kód aplikace je při hledání zranitelností, jako je přetečení bufferu, docela těžkopádné. Ačkoli má tato metoda své přednosti, není nejjednodušší metodou pro nalezení jednoduchých zranitelností, které mohou být kritické. Takové zranitelnosti je třeba okamžitě vyřešit a nejjednodušší způsob, jak je najít, je technika zvaná Fuzzing.
Fuzzing je technika pro hledání „snadných“ zranitelností v kódu pomocí odesílání „náhodně“ generovaných dat do spustitelného souboru. Obecně existují tři typy fuzzerů:
- Mutace: Jedná se o typ „hloupého“ fuzzingu, kdy jsou generovány vzorky chybných vstupů a poskytovány spustitelnému souboru. Tento vstup může, ale nemusí odpovídat typu vstupu, který aplikace očekává, takže pravděpodobnost nalezení skutečných chyb není vysoká.
- Generování: Typ „chytrého“ fuzzingu, který vyžaduje určitá počáteční testovací data, z nichž může algoritmus fuzzeru generovat chybný vstup od začátku. Tento typ fuzzingu je v mnoha případech lepší než hloupý fuzzing, protože program dostává vstup, který očekává.
- Evoluční: Tento typ fuzzerů využívá zpětnou vazbu z každého „fuzzu“ k tomu, aby se postupem času naučil formát vstupu.
V tomto příspěvku se podíváme na fuzzing pomocí American Fuzzy Lop (AFL). Jedná se o typ evolučního fuzzeru, který je vhodný pro fuzzování programů, které přijímají vstup ze STDIN nebo souboru.
V přírodě existuje celá řada fuzzerů, včetně Peach a syzkaller. Proč tedy AFL?
- Případ použití. To je nejdůležitější bod, který je třeba zvážit. Můj use-case byl fuzz aplikace, která přijímá vstup ze souboru. Je důležité si uvědomit, že AFL nemá schopnost fuzzovat po sítích.
- Je jednoduché ho nainstalovat.
- Uživatelské rozhraní AFL obsahuje spoustu informací včetně statistik procesu fuzzování v reálném čase.
Nastavení AFL
Nastavení AFL je snadné a já jsem vám ho usnadnil napsáním jednoduchého (ale hrubého) shellového skriptu, který ho nainstaluje za vás! Spusťte skript s uživatelskými právy a on nainstaluje všechny závislosti, AFL a související nástroje. Shellový skript naleznete zde: https://github.com/nikhilh-20/enpm691_project/blob/master/install_afl.sh
Výběr aplikace pro fuzzování
V tomto příspěvku se budeme zabývat pouze fuzzováním těch aplikací, ke kterým máme zdrojový kód. Je to proto, že AFL instrumentuje zdrojový kód, aby mohl sledovat provádění, chyby a další věci související s výkonem. Je také možné fuzzovat přímo spustitelný soubor, ale to je experimentální a mimo rozsah tohoto příspěvku (tip: vyžaduje QEMU).
Pro fuzzování si vyberte libovolný open source systém z GitHubu. Čím známější je váš výběr, tím menší počet zranitelností bude pravděpodobně obsahovat. Ostatní také hledají chyby! Jednou z jednoduchých metod, kterou používám k nalezení zranitelného kódu, je použití vyhledávače GitHub. Postupuji takto:
- Vyhledejte zranitelnou funkci, například strcpy.
- Výsledků budou miliony. Ve výsledcích přejděte do kategorie revizí. Zde najdete ty repozitáře, kde byla funkce strcpy použita (nebo třeba odstraněna). Tyto repozitáře jsou dobrým výchozím bodem pro zahájení fuzzingu.
Instrumentace aplikace
Z důvodu ochrany osobních údajů nemohu prozradit repozitář, který používám.
Klonujte repozitář git.
nikhilh@ubuntu:~$ git clone https://github.com/vuln; cd vuln
Nastavte proměnnou prostředí, AFL_HARDEN=1. Tím se při kompilaci aktivují určité volby pro zpevnění kódu v AFL, které usnadní odhalování chyb s poškozením paměti.
nikhilh@ubuntu:~/vuln$ export AFL_HARDEN=1
Nastavte určité příznaky kompilátoru, aby se aplikace kompilovala způsobem, který nám usnadní hledání (a zneužívání) zranitelností. V ideálním případě bychom k nastavení toho, co potřebujeme, použili proměnné prostředí, ale je zde docela dost možností přizpůsobení. takže budeme přímo upravovat soubor Makefile.
Ujistěte se, že použitý kompilátor je afl-gcc nebo afl-clang namísto gcc, resp. clang. To umožní AFL instrumentovat zdrojový kód.
Přidejte příznaky kompilátoru:
-fno-stack-protector vypne ochranu zásobníku, což nám umožní zneužívat přetečení bufferu.
-m32 je potřeba pouze v případě, že používáte 32bitový stroj, jinak ne.
Pokud jste s těmito změnami hotovi, je čas aplikaci zkompilovat. Spusťte příkaz make. Když tak učiníte, MUSÍTE v protokolu vidět příkazy, jako jsou tyto:
Instrumented 123 locations (32-bit, hardened-mode, ratio 100%).
Pokud takové příkazy nevidíte, znamená to, že AFL neaktivoval kód aplikace pro fuzzing. Jinými slovy, nepodařilo se mu úspěšně instrumentovat zdrojový kód.
Ukázky testů
AFL je evoluční typ fuzzeru. To znamená, že stejně jako fuzzery založené na generování také vyžaduje počáteční testovací data, aby pochopil, jaký druh dat cílová aplikace očekává. Při cílení na systémy s otevřeným zdrojovým kódem je to snadné zjistit. Stačí se podívat do jejich adresáře test a najdete všechna potřebná testovací data.
nikhilh@ubuntu:~/vuln$ mkdir afl_in afl_out
nikhilh@ubuntu:~/vuln$ cp test/* afl_in/
Fuzzing začíná
Teď, když máme naše testovací vzorky, jsme připraveni na fuzzování!
Oh, počkat… musíme také změnit místo, kam chodí oznámení o pádu aplikace. 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:
- -i — This marks the test input directory. Sem jsme uložili počáteční testovací data.
- -o- Toto je adresář, kam AFL zapisuje užitečné informace týkající se pádů, zavěšení atd.
- -S – Toto je režim Slave. AFL bude v podstatě náhodně upravovat vstup, což způsobí nedeterministický fuzzing.
- Volba -M je režim Master, což je deterministický fuzzing, což v podstatě znamená, že každý bit vstupu je nějakým způsobem modifikován. (To je pomalé! … Samozřejmě.)
- @@ – Toto je pozice, kde bude vstupní testovací soubor. AFL ji za vás nahradí automaticky. Pokud váš spustitelný soubor přijímá vstup ze STDIN, pak toto není potřeba.
Výsledky fuzzingu
Zobrazení bude chvíli trvat. Mnohokrát se stává, že lidé fuzzují déle než 24 hodin (a mohou skončit bez výsledku). V mém případě byla aplikace asi příliš zranitelná, takže jsem během hodiny zaznamenal 516 unikátních pádů. To však neznamená, že existuje 516 zranitelností!
Relaci fuzzingu můžete ukončit pomocí klávesy 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 vám ukáže, jaký vstup způsobil pád aplikace. V tomto případě soubor: id:000046,sig:11,src:000004,op:havoc,rep:4 způsobil v aplikaci StackBufferOverflow. Takové soubory najdete v adresáři ../afl_out-slaveX/crashes/
Dokončeno!
To je vše pro rychlý začátek fuzzingu! Proces je opravdu jednoduchý a velmi pohodlný, protože je celý automatizovaný. Dalším krokem bude analyzovat, proč vstup způsobil přetečení bufferu, a hledat způsob, jak jej zneužít. Nezapomeňte, že ne všechny chyby mohou vést ke zneužití.