Back to IF4053 Keamanan Perangkat Lunak

Studi Kasus CVE - Open Redirect (CVE-2022-1058)

Questions/Cues

  • Apa inti CVE-2022-1058 dan versi Gitea mana yang terdampak serta yang menambalnya?
  • Apa akar penyebab open redirect ini (parser differential)?
  • Bagaimana cara menemukannya secara manual, dengan SAST, dan dengan DAST?
  • Mengapa open redirect memperkuat serangan phishing?
  • Bagaimana Gitea memperbaikinya dan apa pelajarannya?

Reference Points

  • IF4053 Keamanan Perangkat Lunak (W12 — CVE-2022-1058 Open Redirect Walkthrough)
  • IF4053 Keamanan Perangkat Lunak (W12 — bagian “Bypass Variants & How It Was Fixed”)

CVE at a Glance

CVE-2022-1058 adalah kerentanan Open Redirect (CWE-601) pada Gitea—slide menargetkan Gitea 1.16.4, dengan rentang affected Gitea < 1.16.5 dan patched pada Gitea 1.16.5. Severity-nya CVSS 3.1 = 6.1 (Medium). Vektor serangannya adalah cookie/parameter redirect_to pada /user/login. Kerentanan ini tidak membutuhkan auth—dieksploitasi selama alur login. Sumber resmi: nvd.nist.gov/vuln/detail/CVE-2022-1058. Catatan etis: hanya untuk edukasi—uji hanya pada container Docker milik sendiri.

Akar Penyebab — Dua Parser, Satu Ketidaksepakatan

Validator Gitea memakai url.Parse("//evil.com") yang mengembalikan Scheme dan Host kosong, sehingga validator menyimpulkan “tanpa scheme, tanpa host = path relatif yang aman”. Namun browser menafsirkan Location: //evil.com sebagai URL protocol-relative dan menavigasi ke http://evil.com. Inilah parser differential bug: input yang sama diurai berbeda oleh dua kode—validator Go vs parser browser. Bug semacam ini muncul di mana pun input diurai dua kali oleh kode berbeda.

// modules/context/context.go (v1.16.4)
func (ctx *Context) RedirectToFirst(location ...string) {
    for _, loc := range location {
        u, err := url.Parse(loc)
        if err != nil || ((u.Scheme != "" || u.Host != "") && ...) {
            continue
        }
        ctx.Redirect(loc)   // ← "//evil.com" lolos
    }
}

Penemuan Manual dan Varian Bypass

Jalankan versi rentan: docker run -d --name gitea -p 3000:3000 -p 222:22 gitea/gitea:1.16.4 (1.16.4 ada di Docker Hub gitea/gitea, bukan docker.gitea.com), selesaikan setup, buat admin dan user uji. PoC kanonik (inject cookie): tangkap POST /user/login di HTTP History, kirim ke Repeater (Burp) / Manual Editor (ZAP), tambahkan ke header Cookie: redirect_to=//example.com (jangan ganti cookie yang ada), kirim, lalu inspeksi header RESPONSE—munculnya Location: //example.com menandakan bug menyala. Bila token CSRF kedaluwarsa, tangkap ulang request segar. Varian bypass (tiap payload menguji keanehan parser): //example.com (protocol-relative, kanonik) → External ✓; /\example.com (normalisasi \/ browser) → External ✓; ///example.com (multi-slash, dianggap path) → Internal ✗; https://example.com (URL absolut) → Blocked ✗; //[email protected] (userinfo confusion) → Depends; //ex%E3%80%82com (IDN homograph, titik CJK) → browser quirk. Bug butuh dua syarat: server meloloskan string DAN browser menafsirkannya sebagai eksternal.

flowchart TD
    A["Penyerang kirim tautan login<br/>+ redirect_to=//evil.com"] --> B["Korban login ke Gitea sah"]
    B --> C["Validator Go: //evil.com = path relatif (aman)"]
    C --> D["Response Location: //evil.com"]
    D --> E["Browser: navigasi ke evil.com"]

Penemuan dengan SAST

SAST dapat menandai pola redirect tidak aman, tetapi eksploitasi sebenarnya membutuhkan pemahaman perilaku browser—yang tak dapat dinalar analisis statis. Rule Semgrep kustom menggunakan pattern-either untuk $CTX.Redirect($USER_INPUT) dan http.Redirect($W, $R, $USER_INPUT, ...) (severity WARNING, Go), menghasilkan kandidat untuk review manusia—selalu konfirmasi dengan pengujian manual. SAST menangkap: pemanggilan redirect dengan input terkontrol pengguna, validasi yang hilang sebelum redirect, dan penggunaan url.Parse tanpa pengecekan lanjutan. SAST melewatkan: interpretasi URL spesifik-browser, apakah validasi benar-benar memadai, kerentanan parser differential, dan varian bypass berbasis encoding. Analisis statis melihat kode, bukan browser nyata yang mengeksekusinya.

Penemuan dengan DAST

DAST adalah pasangan alami untuk open redirect—tembakkan payload, amati header Location. Setup ZAP Fuzzer: klik kanan POST /user/loginAttack → Fuzz, sorot nilai redirect_to di header Cookie → Add, tempel 30+ varian payload (Strings), tambahkan processor Encode → URL, mulai fuzzer, urutkan berdasarkan header Location. Padanan Burp: Intruder dengan serangan Sniper pada posisi yang sama. Active Scanner ZAP bahkan memiliki rule External Redirect bawaan (di Information Gathering). Yang mendeteksi: ZAP Active Scan (rule Ext. Redirect), Burp Pro scanner, fuzzing manual dengan daftar payload, dan Burp Collaborator (kasus blind). Yang tidak menemukan: ZAP baseline (pasif), Nikto, Nuclei (kecuali template CVE dimuat), serta crawler generik tanpa fuzzing. Open redirect terdeteksi hanya lewat pengujian aktif, bukan observasi pasif.

Coverage Matrix, Dampak, dan Mitigasi

Coverage Matrix: injeksi cookie manual di Repeater (✓, rendah), ZAP Fuzzer (✓, medium), Burp Intruder Sniper (✓, medium), ZAP Active Scanner (✓, rendah), Burp Pro scanner (✓, rendah), Semgrep rule kustom (✓ kandidat, medium), gosec (✗), Trivy image scan (✓ via versi, rendah)—CVE ini paling merata terdeteksi lintas SAST dan DAST di antara ketiga studi kasus. Dampak: open redirect memperkuat phishing karena URL bar menampilkan domain sah sebelum pengalihan ke situs jahat. Mitigasi (Gitea 1.16.5): tambahkan pemeriksaan string mentah SEBELUM url.Parse—menolak lebih awal URL yang diawali // atau /\ (if len(loc) > 1 && loc[0] == '/' && (loc[1] == '/' || loc[1] == '\\') { continue }). Pendekatan ini melewati parser sepenuhnya. Pelajaran: validasi string mentah, bukan objek hasil parse, dan kadang lima baris (termasuk komentar) sudah cukup—patch terkecil terkadang yang paling tepat.

Summary

CVE-2022-1058 adalah open redirect (CWE-601) pada Gitea < 1.16.5 (slide menyorot 1.16.4; ditambal di 1.16.5, CVSS 6.1 Medium) via cookie/parameter redirect_to pada /user/login, tanpa auth dan dipicu selama alur login. Akar penyebabnya parser differential: validator Go menganggap //evil.com sebagai path relatif aman, sementara browser menavigasinya sebagai http://evil.com. Penemuan manual dengan menyisipkan redirect_to=//example.com di Repeater dan menguji varian bypass (//, /\, userinfo, IDN); SAST menandai pola Redirect($USER_INPUT) sebagai kandidat; DAST sangat efektif lewat ZAP Fuzzer/Active Scanner dan Burp Intruder/scanner. Perbaikannya memeriksa string mentah sebelum url.Parse (lima baris), menegaskan prinsip validasi string mentah, bukan objek hasil parse. Open redirect berbahaya karena memperkuat phishing.