SECURITY Fuzzing Serüveni 101: "Rastgelelik" Nasıl Silaha Dönüşür?

Navigation

Computer Science

Mathematics

Security

PicoCTF

HackTheBox

TryHackMe

Geometry

Cheatsheet

Sosyal Medya

Güncel Makaleler

Yükleniyor...
Erciyes Uni.
Bilgisayar Muh.
CBFRPRO CBTEAMER CNPEN eMAPT

Security

Fuzzing Serüveni 101: "Rastgelelik" Nasıl Silaha Dönüşür?

December 19, 2025

Merhaba! Dürüst olayım, yıllar önce güvenliğe ilk merak saldığımda Fuzzing’i “bir programa rastgele çöp veri fırlatıp patlamasını beklemek” sanıyordum. Hatta yazdığım ilk basit python script’i ile saatlerce ekran başında bekleyip, “Neden bu aptal program çökmiyor?” diye kendi kendime söylendiğimi hatırlıyorum.

Meğer o zamanlar buzdağının sadece görünen kısmıyla oynuyormuşum. Bugün geldiğimiz noktada, eğer modern bir hedefi (örneğin Adobe Reader gibi devasa bir PDF okuyucuyu veya karmaşık bir oyun emülatörünü) sadece rastgele veriyle hacklemeye çalışırsanız, muhtemelen güneş sönene kadar beklersiniz. İşin rengi, Coverage-Guided (Kapsam Odaklı) kavramını anladığımda değişti.

Bu serinin ilk bölümünde, hem teknik detaylara gireceğiz hem de bu süreçte yaşadığım “aydınlanma” anlarından bahsedeceğim. Masamızda endüstri standardı AFL++, arkasında yatan matematik ve en önemlisi hedefi hazırlama (Harnessing) sanatı var.

1. Kör Talih Değil, Evrimsel Strateji

Fuzzing aslında benim gözümde bir optimizasyon problemi. Amacımız, o devasa samanlıkta iğneyi aramak değil; mıknatısa dönüşmek. Hedef, main() fonksiyonundan girip, o derinlerde saklanan, geliştiricinin bile unuttuğu if (ozel_durum) bloğuna ulaşmak.

İlk defa AFL’nin arayüzünü açıp “paths found” (bulunan yollar) sayısının arttığını gördüğümde hissettiğim tatmin duygusunu tarif edemem. Bu araçlar Genetik Algoritmalar kullanıyor ve süreç biyolojideki evrime çok benziyor:

Fuzzing Akış Diyagramı - Konsept Çizim Şekil 1: Modern bir fuzzer’ın (örneğin AFL++) çalışma döngüsü: Döngü, bir başlangıç girdisiyle (Corpus Input) başlar. Mutasyon motoru (Mutation Engine) bu girdiyi bit çevirme veya ekleme gibi yöntemlerle değiştirir. Değiştirilen girdi hedef programa gönderilir. Program çalışırken elde edilen “Coverage” (Kapsam) bilgisi, fuzzer’a hangi girdilerin yeni kod yollarını keşfettiğini söyler. Bu geri bildirim döngüsü ile fuzzer kendini eğitir ve en sonunda programı çökerten (Crash) girdiyi bulmaya çalışır.

  1. Corpus (Girdi Havuzu): Elimizde geçerli dosyalar vardır (örneğin bir PDF okuyucu için “hello.pdf”).
  2. Mutation (Mutasyon): Fuzzer bu dosyayı alır ve değiştirir. Bitleri çevirir (bitflip), baytları siler, aritmetik işlemler yapar (örneğin dosyadaki 0x10 değerini 0x11 yapar) veya iki dosyayı birbirine ekler (splicing).
  3. Execution & Feedback: Program çalıştırılır. Eğer yeni üretilen girdi, programın daha önce hiç çalışmamış bir kısmını çalıştırırsa, bu girdi “ilginç” (interesting) olarak işaretlenir ve havuza eklenir.

2. AFL++ Kaputun Altında Nasıl Çalışır?

AFL++’ın sihirli tarafı, programın “neresinin çalıştığını” bilmesidir. Buna Instrumentation (Enstrümantasyon) denir.

Coverage Map (Kapsam Haritası) ve Paylaşılan Bellek

Derleme sırasında (örneğin afl-clang-fast kullanırken), AFL her temel bloğun (basic block) veya dallanmanın (branch) içine küçük bir kod parçası enjekte eder. Program çalıştığında, bu kodlar Shared Memory (Paylaşılan Bellek - shm) adı verilen 64KB’lık bir haritayı günceller.

AFL Kapsam Haritası - Şematik Çizim Şekil 2: AFL’nin çalışma mantığı. Sol taraftaki akış diyagramında (CFG) gerçekleşen her geçiş (örneğin A’dan B’ye), sağ taraftaki Paylaşılan Bellek haritasında belirli bir hücreye karşılık gelir. cur_loc ^ prev_loc formülü sayesinde her geçişin kimliği benzersiz (veya ona yakın) olur. Böylece fuzzer, hangi kod yollarından geçildiğini harita üzerindeki “işaretlenmiş” (hit) kutucuklardan anlar.

AFL’nin hangi yoldan gidildiğini anlama formülü şudur: shared_mem[cur_location ^ prev_location]++;

Burada cur_location şu anki bloğun ID’si, prev_location ise bir önceki bloğun ID’isidir. XOR (^) işlemi sayesinde AFL sadece “A bloğuna gidildi” bilgisini değil, “A bloğundan B bloğuna geçildi” (Edge Coverage) bilgisini tutar. Bu sayede programın akış haritasını çıkarır.

Forkserver: Hız Canavarı

Her test girdisi için programı baştan başlatmak (execve sistem çağrısı) çok yavaştır. AFL, bunun yerine Forkserver mekanizmasını kullanır.

  1. Hedef program bir kez başlatılır ve main fonksiyonuna girmeden hemen önce durdurulur.
  2. Fuzzer her yeni test için bu durdurulmuş süreçten bir kopya (fork) oluşturur.
  3. fork() işlemi çok hızlıdır, bu sayede saniyede binlerce test yapılabilir.

3. Hedefi Hazırlamak: “Harnessing” Sanatı

Bir emülatörü veya sunucuyu olduğu gibi fuzzer’ın kucağına atamazsınız. Bunu acı yoldan öğrendim. Zamanında basit bir FTP sunucusunu fuzz’lamaya çalışırken, fuzzer’ın sürekli network timeout (zaman aşımı) beklediği için saniyede sadece 5 test yapabildiğini görmüştüm. O hızla bir bug bulmam aylar sürerdi.

İşte burada Harnessing devreye giriyor. Programı cerrah titizliğiyle modifiye etmemiz gerekiyor:

  • Desocketing (Soketleri Söküp Atmak): O FTP sunucusu örneğine dönersek; ağ dinleyen recv() fonksiyonunu iptal edip, veriyi direkt dosya sisteminden okuyacak şekilde kodu yamaladım (patchledim). Sonuç? Hız saniyede 5’ten 2000’e fırladı. O an “Harnessing her şeydir” sözünü dövme yaptırmayı bile düşündüm :)
  • GUI’yi Kapatmak: Bir keresinde görsel arayüzü olan bir uygulamayı fuzz’larken, programın her seferinde pencere açıp kapatmaya çalıştığını fark ettim. Grafik işlemleri CPU’yu sömürüyordu. Görüntü kodlarını /dev/null‘a yönlendirip sadece işlemci mantığına (CPU logic) odaklandığımda, performans 100 kat arttı.
  • Persistent Mode (Kalıcı Mod): Programı her test için fork etmek (yeniden başlatmak) bile bir süre sonra yavaş kalıyor. Persistent Mode ile programı bir kez başlatıp, hedef fonksiyonu bir döngüye (while(__AFL_LOOP(10000))) aldığınızda, makine adeta makineli tüfeğe dönüşüyor.

4. Kaynak Kod Yoksa? (Binary-Only Fuzzing)

CTF yarışmalarında veya kapalı kaynak yazılımlarda kaynak kodunuz olmayabilir. Yine de çaresiz değiliz.

  • QEMU Modu (-Q): AFL++, programı QEMU emülatörü içinde çalıştırır. QEMU, programın her bir komutunu işlerken araya girer ve AFL’nin ihtiyaç duyduğu “Coverage” bilgisini toplar. Kaynak kodlu fuzzing’e göre daha yavaştır ama her binary üzerinde çalışır.
  • Static Rewriting: E9AFL gibi araçlar, derlenmiş binary dosyasını alır ve içine AFL’nin takip kodlarını (trampolines) sonradan enjekte eder. Bu, QEMU modundan çok daha hızlıdır.

5. Görünmez Hataları Yakalamak: Sanitizer’lar

Programın “Segmentation Fault” verip çökmemesi, güvenli olduğu anlamına gelmez. Bu dersi, görünürde taş gibi çalışan ama arka planda sessizce bellek sızdıran bir C kütüphanesini test ederken almıştım. Program çökmüyordu ama aslında delik deşikti.

Klasik hatalar (örneğin bellek sızıntıları veya “off-by-one” taşmaları) hemen çökme yaratmaz ama sinsice orada bekler. Bu hayalet hataları yakalamak için derleme sırasında ASAN (Address Sanitizer) kullanıyoruz.

Buffer Overflow Örneği - El Çizimi Şekil 3: Basit bir C programında klasik bir bellek taşması (Buffer Overflow) senaryosu. strcpy fonksiyonu, kopyaladığı verinin boyutunu kontrol etmez (Unsafe Function). Eğer input (kaynak) boyutu, buffer (hedef) boyutundan büyükse (örneğin 64 baytlık bir alana 100 bayt yazmaya çalışırsak), taşan veri bellekteki kritik bölgelerin (Stack) üzerine yazılır. Bu durum, programın çökmesine veya saldırganın kod çalıştırmasına (Exploit) yol açabilir.

ASAN, bellek bloklarının etrafına “zehirli bölgeler” (redzones) yerleştirir. Program çalışırken eğer bu yasaklı bölgeye tek bir byte bile dokunursa, ASAN anında “Hop dedik!” deyip programı durdurur ve size hatanın tam satır numarasını verir. İlk kullandığımda bana sihir gibi gelmişti.


Özetle: Fuzzing, sadece rastgele tuşlara basmak değil; programın iç yapısını haritalayan, genetik algoritmalarla en uygun girdiyi evrimleştiren ve işletim sistemi seviyesinde optimizasyonlar (forkserver, shared memory) gerektiren sofistike bir süreçtir.

Serinin 2. Bölümünde: Teori bitti. Bir sonraki yazıda basit bir C programını (veya pdfinfo gibi gerçek bir hedefi) alacağız, afl-clang-fast ile derleyip, ilk Harness‘ımızı yazacağız ve AFL++ arayüzündeki o hipnotize edici istatistik ekranını yorumlayacağız.

Hazır olun, işlemcileri ısıtacağız!

Paylaş

Yorumlar

🔔
Yeni yazılardan haberdar ol! Bildirim al, hiçbir yazıyı kaçırma.