Skip to content
endr
Go back

Broken Access Control: Panel Admin yang Cuma Ngecek Login, Bukan Role

ID

Dua post terakhir di seri ini, SSRF lewat Fetch URL sama RCE lewat LFI, dua-duanya mendarat di endpoint yang ada admin/-nya, dan gua ngerjainnya sambil login sebagai kipli, akun user biasa yang gua daftarin sendiri lewat fitur register yang kebuka buat umum. Waktu itu gua lewatin gitu aja, kayak wajar admin panel kebuka buat gua. Padahal itu sendiri bug-nya, dan mungkin yang paling parah dari semuanya. Post ini soal kenapa kipli bisa ada di dalem admin/ sama sekali.

Login bukan berarti berhak

Ada dua pertanyaan beda yang sering dikira satu. Pertama, “lo siapa?” Jawabannya authentication, dikasih pas login. Kedua, “lo boleh ngapain?” Jawabannya authorization, dikasih tiap kali lo minta sesuatu. Authentication cuma mastiin lo punya akun. Authorization yang mutusin akun itu boleh nyentuh apa.

Broken Access Control (CWE-862, Missing Authorization) itu pas aplikasi jawab pertanyaan pertama terus lupa nanya yang kedua. Udah login? Masuk. Mau ke halaman user atau halaman admin, sama aja, asal udah login. OWASP naruh ini di nomor satu, A01:2021, bukan tanpa alasan: ini kelas bug yang paling sering ketemu.

Satu baris yang ngejaga seluruh panel admin

Tiap halaman di /admin/*.php dibuka sama guard yang kira-kira begini:

<?php
session_start();

if (!isset($_SESSION['user_id'])) {
    header('Location: /login.php');
    exit;
}

// ... seluruh isi halaman admin di bawah sini

Baca pelan-pelan. Yang dicek cuma satu: $_SESSION['user_id'] ada atau enggak. Artinya “ada orang yang udah login”. Titik. Enggak ada satu baris pun yang nanya orang ini role-nya apa.

kipli role-nya user, tapi $_SESSION['user_id']-nya keisi, jadi guard-nya lolos, sama persis kayak kalau yang buka beneran admin. Guard yang bener harusnya nanya dua hal: udah login, dan role-nya admin. Yang kepasang di sini cuma setengahnya.

PoC-nya: ngetik URL

Enggak ada tahap eksploitasi di sini, dan itu justru yang bikin serem. Enggak ada parameter yang di-tamper, enggak ada tool, enggak ada payload. Cukup:

  1. Login pake akun user biasa, kipli.
  2. Ketik https://exam.redacted/admin/ di address bar.
  3. Enter.

Admin Dashboard TokoPak.Wowo kebuka pake akun user biasa, nyapa "Welcome back, kipli!", lengkap sama menu Products, Orders, Users, Upload, Fetch URL, Backup plus statistik 51 produk, 16 order, 30 user (URL di address bar disensor)

Admin Dashboard-nya kebuka. “Welcome back, kipli!”, disapa pake nama user biasa gua, di halaman yang harusnya cuma buat admin. Menu-nya lengkap: Products, Orders, Users, Upload, Fetch URL, Backup. Plus statistik toko: 51 produk, 16 order, 30 user. Semua kebuka buat akun yang harusnya cuma bisa belanja.

Yang kebuka di balik satu baris itu

Panel admin bukan cuma dashboard buat dilihat-lihat. Tiap menu itu aksi beneran, dan semuanya sekarang ada di tangan user biasa:

Ini yang disebut vertical access control: user level rendah naik ngerjain hal yang harusnya cuma buat level tinggi. Beda sama IDOR yang kemarin, yang itu horizontal, user ngintip data user lain yang selevel. Di sini kipli lompat ke atas, langsung jadi admin.

Akarnya: authorization yang enggak pernah ditanya

Akarnya sama persis kayak temuan-temuan sebelumnya di seri ini: ada satu asumsi yang kepasang diem-diem di kepala developer dan enggak pernah dicek ke kodenya. Di LFI asumsinya “yang masuk ke include() pasti nama file yang bener”. Di SSRF, “yang ditempel admin pasti alamat http”. Di sini, “yang nyampe ke halaman admin pasti admin”.

Session bilang siapa yang login. Session enggak bilang orang itu boleh ngapain, itu keputusan yang harus diambil ulang tiap request, dibandingin sama role yang kesimpan. Begitu langkah itu di-skip, “udah login” diam-diam berubah arti jadi “boleh semuanya”.

Benerinnya: tanya role-nya, di tiap request

Prinsipnya deny by default. Default-nya tolak, akses baru dikasih kalau syaratnya kebukti, bukan kebalikannya.

Guard tadi dibenerin jadi nanya role juga:

<?php
function require_admin(): void
{
    if (!isset($_SESSION['user_id'])) {
        header('Location: /login.php');
        exit;
    }

    if (($_SESSION['role'] ?? null) !== 'admin') {
        http_response_code(403);
        exit('403 Forbidden');
    }
}

Terus dipanggil di paling atas tiap file admin, sebelum satu baris output pun jalan:

<?php
session_start();
require_once __DIR__ . '/../auth/guard.php';

require_admin(); // bukan admin = berhenti di sini, 403

// ... isi halaman

Tiga hal yang gampang kelewat.

Pertama, cek-nya di tiap endpoint, bukan cuma dashboard. /admin/users.php, /admin/upload.php, /admin/fetch_url.php, /admin/backup.php, tiap file kena cek sendiri. Kalau cuma dashboard yang dijaga tapi users.php bisa diakses langsung, lo cuma mindahin bug-nya. Authorization dicek per-request, per-resource, bukan sekali di depan pintu.

Kedua, taro di satu tempat. Jangan copy-paste if ($_SESSION['role'] ...) di 20 file, cepet atau lambat ada satu yang kelupaan, dan yang kelupaan itu yang bakal ketemu. Satu fungsi require_admin() atau satu middleware, dipanggil dari semua. Di framework beneran ini biasanya route middleware (role:admin / permission:...) yang diterapin ke grup route admin sekaligus, jadi enggak ada route yang bisa lolos tanpa dijaga.

Ketiga, nyumputin menu di UI itu bukan access control. Kalau fix-nya cuma if ($role == 'admin') { echo '<a href="/admin">'; }, itu cuma ngilangin link. URL-nya tetep ada, tetep bisa diketik manual, persis yang gua lakuin. Gating di frontend itu buat kerapian tampilan doang, yang ngejaga harus server, tiap request.

Habis dipasang, buktiin dari sisi penyerang: ambil sesi role=user, tembak semua path admin, pastiin semua balikin 403.

for path in index.php users.php upload.php fetch_url.php backup.php; do
  code=$(curl -s -o /dev/null -w '%{http_code}' \
    -b "PHPSESSID=<sesi-user-biasa>" \
    "https://exam.redacted/admin/$path")
  echo "$path -> $code"   # semua harus 403
done

Kalau ada satu yang balik 200, berarti masih ada file admin yang lupa dijaga.


Sama kayak post-post sebelumnya, ini berhenti di titik pembuktian: satu halaman admin kebuka pake akun user biasa, cukup buat nunjukin role-nya emang enggak pernah dicek. Studi kasusnya dari lab exam bootcamp cyber security yang emang disiapin buat dibongkar kayak gini. Dan dari semua temuan di seri ini, yang ini yang paling di akar: begitu pintu admin kebuka buat sembarang user, SSRF, upload, sama backup tadi tinggal nyusul.


Sumber:


Share this post:

Previous Post
SSRF: Nyuruh Server Ngirim Request yang Harusnya Enggak Boleh
Next Post
Algoritma Luhn: Cara Form Tau Nomor Kartu Salah Sebelum Lo Submit