Back to IF4053 Keamanan Perangkat Lunak

Fuzzing dan Gitea Exercise

Questions/Cues

  • Mengapa fuzzing disebut “systematic distrust” dan apa beda sikap engineer vs fuzzer?
  • Apa yang ditemukan fuzzing tetapi luput dari code review?
  • Bagaimana cara kerja Go native fuzzer (FuzzParseLimit) dan HTTP fuzzer Python?
  • Sinyal apa pada output fuzzer yang menandai sebuah finding (“A 500 is not a failure”)?
  • Apa empat langkah guided exercise Gitea dan tiga kebiasaan attacker?

Reference Points

  • IF4053 Keamanan Perangkat Lunak (W13 Attacker Mindset — bagian “Module 3: Fuzzing, Module 4: Guided exercise & Closing”)

Fuzzing sebagai Systematic Distrust

Fuzzing adalah ketidakpercayaan yang sistematis (systematic distrust) terhadap input. Perbedaan sikapnya tajam: seorang engineer berkata “I tested the happy path and two error cases”; sebuah fuzzer berkata “I will send 10,000 inputs and watch what breaks”. Engineer menguji segelintir kasus yang ia bayangkan; fuzzer membanjiri program dengan input dalam jumlah besar lalu mengamati apa yang patah.

Singgungan dengan W09: definisi umum fuzzing (melontarkan input invalid/random lalu memantau crash/assertion/memory leak) dibahas di Fault Injection, Mutation, dan Fuzz Testing. Di sini sudut pandangnya attacker/praktik — bagaimana fuzzing dipakai untuk menemukan dan membuktikan kerentanan — bukan metodologi testing-nya.

Apa yang Ditemukan Fuzzing tetapi Luput dari Code Review

Code review membaca logika; fuzzing menemukan hal-hal yang sulit terlihat dengan membaca, yaitu:

  • Integer overflow/underflow: MAX_INT, -1, 0.
  • Special chars: %00, ../, ' OR 1=1.
  • Resource exhaustion pada input yang sangat besar.
  • Crashes pada data yang malformed-tapi-masih-parseable.
  • Inconsistent behaviour antar endpoint.
  • Timing differences yang mengungkap logika percabangan.

Dua jenis fuzzer dibedakan menurut tingkatannya: Go native fuzzer — function-level, unit testing, input parsing; dan RESTler / HTTP fuzzer — end-to-end, API behaviour, auth bypass.

Go Native Fuzzer

Fuzzing tingkat unit di Go: kamu mendefinisikan invariant (apa yang harus selalu benar), lalu fuzzer berusaha melanggarnya (violate it). Cocok untuk parsing functions, input sanitisation, dan logika tingkat unit.

func FuzzParseLimit(f *testing.F) {
    f.Add(10)    // seed: normal value
    f.Add(0)     // seed: boundary
    f.Add(-1)    // seed: negative
    f.Fuzz(func(t *testing.T, limit int) {
        result := parseLimit(limit)
        // invariant: result must always be 1-50
        if result < 1 || result > 50 {
            t.Errorf("parseLimit(%d) = %d, out of range", limit, result)
        }
    })
}

Dijalankan dengan: go test -fuzz=FuzzParseLimit. Seed (f.Add) memberi nilai awal; fuzzer lalu memutasinya untuk mencoba membuat parseLimit mengembalikan nilai di luar rentang invariant 1–50.

HTTP Fuzzing (Skrip Python requests)

Untuk fuzzing tingkat end-to-end terhadap endpoint search Gitea, dipakai skrip Python berbasis requests:

import requests
 
TARGET = "http://localhost:3000/api/v1/repos/search"
 
payloads = {
    "limit": [0, -1, 999999, "abc", None, "%00", 2**31],
    "q":     ["", " ", "a"*10000, "../admin", "' OR '1'='1"],
    "page":  [0, -1, 999999, "abc", None],
}
 
for param, values in payloads.items():
    for value in values:
        r = requests.get(TARGET, params={param: value}, timeout=5)
        print(f"{param}={repr(value):20} -> {r.status_code} | {len(r.content)} bytes")

Skrip mengiterasi setiap parameter dengan daftar payload bermasalah (nilai ekstrem, special chars, string sangat panjang, percobaan SQL injection) lalu mencetak kode status dan ukuran body tiap respons — dua sinyal utama untuk dibaca.

Membaca Output Fuzzer

Yang dipantau dalam aliran respons:

SinyalArtiInterpretasi attacker
500Unexpected code path hitSesuatu yang developer tak duga harus ditangani — ini sebuah finding, bukan kegagalan fuzzer
large bodyRespons luar biasa besarPotensi data leakage — data yang dikembalikan lebih banyak dari semestinya
timeoutRequest timeoutPotensi DoS vector — input ini menghabiskan terlalu banyak sumber daya server
differentRespons inkonsisten untuk tipe input samaOracle behaviour — memungkinkan pertanyaan ya/tidak tentang state tersembunyi sistem

Prinsip penutup modul ini: “A 500 is not a failure of your fuzzer. It is a finding.” Status 500 berarti input menyentuh jalur kode yang tidak diantisipasi developer — justru itulah yang dicari.

Gitea Guided Exercise

Setup: docker run -p 3000:3000 gitea/gitea · Python + requests · Swagger di localhost:3000/swagger. Empat langkah berurutan:

  1. Entry point mapping — daftarkan 3 endpoint dengan parameter integer, 3 dengan parameter string, dan 1 di mana autentikasi mengubah data yang dikembalikan.
  2. Code review (5 mnt) — di /api/v1/repos/search: temukan tempat limit diparse. Apakah ada maksimum yang dipaksakan? Apa yang terjadi dengan limit=1000000?
  3. Run the fuzzer (7 mnt) — perluas skrip: fuzz topic dengan nilai non-boolean, q dengan string sangat panjang, page dengan integer besar. Catat semua status code tak terduga.
  4. Write a finding (3 mnt) — format: “Input [X] to param [Y] on endpoint [Z] produced response [W]. An attacker could use this to [impact].”

Closing — Tiga Kebiasaan Attacker

Tiga kebiasaan untuk diterapkan pada setiap code review sepanjang karier:

  1. Assume every input is hostile — sampai kode itu sendiri membuktikan sebaliknya; bukan niat developer, bukan dokumentasi, melainkan kode.
  2. Read permission checks with suspicion — apakah ada di tempat yang benar, mengecek hal yang benar, dan mustahil dilewati dari jalur kode mana pun?
  3. Different outputs for the same logical input is a finding — error oracle, perbedaan timing, dan perbedaan ukuran respons semuanya memberi attacker informasi tentang sistem.

Kutipan penutup: “The developer imagined a user who wants the software to work. Your job as a security tester is to be the user who doesn’t.”

Summary

Fuzzing adalah systematic distrust: alih-alih menguji happy path + dua error case (engineer), fuzzer mengirim 10.000 input dan mengamati apa yang patah. Ia menemukan hal yang luput dari code review — integer overflow (MAX_INT/-1/0), special chars (%00, ../, ' OR 1=1), resource exhaustion, crash pada data malformed, perilaku inkonsisten, dan timing differences. Go native fuzzer (FuzzParseLimit, dijalankan go test -fuzz=) menguji invariant tingkat unit; HTTP fuzzer Python membanjiri endpoint search Gitea lalu mencetak status code + ukuran body. Membaca output: 500 = unexpected code path (“A 500 is not a failure… It is a finding”), large body = data leakage, timeout = DoS, different = oracle. Gitea exercise empat langkah: entry-point mapping, code review (limit=1000000), run fuzzer, write a finding. Tiga kebiasaan attacker: anggap setiap input hostile, baca permission check dengan curiga, dan output berbeda untuk input logis sama = sebuah finding.