Panduan ini berisi 3 lab terpisah. Anda akan berperan sebagai Blue Team (membangun server target di VM Ubuntu) dan Red Team (menyerang server dari VM Kali).
Penting: Setiap lab akan menggunakan port yang berbeda (8080, 8081, 8082) sehingga Anda dapat menjalankan ketiga kontainer Docker secara bersamaan.
Tujuan: Memicu error database asli SQLite dan mem-bypass halaman login menggunakan SQL Injection.
Di Host Ubuntu Anda, buat server yang rentan terhadap SQLi.
1. Buat Folder & File Lab:
mkdir ~/lab-sqli
cd ~/lab-sqli
2. Buat Dockerfile (Diperbarui):
nano Dockerfile
Salin-tempel konten berikut:
# Gunakan base image PHP 8.1 dengan Apache
FROM php:8.1-apache
# [PERBAIKAN] Instal dependensi sistem (libsqlite3-dev) terlebih dahulu
RUN apt-get update && apt-get install -y \
libsqlite3-dev \
&& apt-get clean && rm -rf /var/lib/apt/lists/*
# Instal ekstensi PDO dan SQLite (untuk database nyata)
RUN docker-php-ext-install pdo pdo_sqlite
# Buat file DB dan berikan izin agar Apache (www-data) bisa menulis
RUN touch /var/www/html/users.db && chown www-data:www-data /var/www/html/users.db && chmod 664 /var/www/html/users.db
# Salin file web kita ke dalam container
COPY index.php /var/www/html/index.php
COPY product.php /var/www/html/product.php
# Expose port 80
EXPOSE 80
Simpan dan keluar (Ctrl+O, Enter, Ctrl+X).
3. Buat Halaman index.php (Rentan Bypass, Real DB):
nano index.php
Salin-tempel kode PHP berikut. Ini akan membuat dan mengisi DB saat pertama kali dijalankan.
<?php
// --- DB Setup ---
$db_file = '/var/www/html/users.db';
$db_exists = file_exists($db_file);
try {
$db = new PDO('sqlite:' . $db_file);
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
// Buat tabel jika belum ada
$db->exec("CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY, username TEXT, password TEXT)");
$db->exec("CREATE TABLE IF NOT EXISTS products (id INTEGER PRIMARY KEY, name TEXT, description TEXT)");
if ($db->query("SELECT COUNT(*) FROM users")->fetchColumn() == 0) {
// Isi data default jika tabel users kosong
// Kita hash passwordnya untuk perbandingan di lab perbaikan
$hashed_pass = password_hash('password123', PASSWORD_DEFAULT);
$db->exec("INSERT INTO users (username, password) VALUES ('admin', '$hashed_pass')");
}
if ($db->query("SELECT COUNT(*) FROM products")->fetchColumn() == 0) {
// Isi data default jika tabel products kosong
$db->exec("INSERT INTO products (id, name, description) VALUES (1, 'Laptop', 'Laptop Canggih'), (2, 'Mouse', 'Mouse Optik')");
}
} catch (PDOException $e) {
die("Error setting up database: " . $e->getMessage());
}
// --- Login Logic ---
$login_message = '';
$sql_query_display = '';
if ($_SERVER["REQUEST_METHOD"] == "POST" && isset($_POST['username'])) {
$user = $_POST['username'];
$pass = $_POST['password'];
// --- VULNERABLE CODE ---
$sql = "SELECT * FROM users WHERE username = '$user' AND password = '$pass'";
$sql_query_display = $sql;
try {
$stmt = $db->query($sql);
$result = $stmt->fetch(PDO::FETCH_ASSOC);
if ($result) {
$login_message = "<h3 style='color:green;'>Login Berhasil!</h3><p>Selamat datang, " . htmlspecialchars($result['username']) . "</p>";
// Payload bypass sederhana
if (strpos($user, "'") !== false) {
$login_message .= "<p>Token Rahasia (dari bypass): flag{real_sqli_bypass_sukses_31337}</p>";
}
} else {
$login_message = "<h3 style='color:red;'>Login Gagal. Kredensial salah.</h3>";
}
} catch (PDOException $e) {
$login_message = "<h3 style='color:red;'>SQL Error:</h3><pre>" . htmlspecialchars($e->getMessage()) . "</pre>";
}
}
?>
<!DOCTYPE html>
<html lang="id">
<head>
<meta charset="UTF-8">
<title>Lab SQLi (Real DB) - Login</title>
<style>
body { font-family: sans-serif; margin: 20px; background: #f4f4f4; }
.container { max-width: 400px; margin: 50px auto; padding: 20px; background: #fff; border-radius: 8px; box-shadow: 0 2px 5px rgba(0,0,0,0.1); }
input[type="text"], input[type="password"] { width: 100%; padding: 8px; margin-bottom: 10px; box-sizing: border-box; }
input[type="submit"] { background: #007bff; color: white; padding: 10px; border: none; border-radius: 4px; cursor: pointer; }
pre { background: #eee; padding: 10px; border-radius: 4px; white-space: pre-wrap; word-wrap: break-word; }
</style>
</head>
<body>
<div class="container">
<h2>Halaman Login (Boolean-Based, Real DB)</h2>
<form method="POST" action="index.php">
<input type="text" name="username" placeholder="Username">
<input type="password" name="password" placeholder="Password">
<input type="submit" name="Login" value="Login">
</form>
<p>Coba juga halaman produk: <a href="product.php?id=1">product.php?id=1</a></p>
<?php
if (!empty($sql_query_display)) {
echo "<hr><p><b>Query SQL yang dijalankan:</b><pre>" . htmlspecialchars($sql_query_display) . "</pre></p>";
}
if (!empty($login_message)) {
echo $login_message;
}
?>
</div>
</body>
</html>
Simpan dan keluar.
4. Buat Halaman product.php (Rentan Error-Based, Real DB):
nano product.php
Salin-tempel kode ini. Halaman ini akan memicu **error SQLite asli**.
<!DOCTYPE html>
<html><head><title>Lab SQLi - Produk (Real DB)</title></head>
<body>
<h2>Detail Produk (Error-Based, Real DB)</h2>
<p><a href="index.php">Kembali ke Login</a></p>
<?php
// --- DB Setup ---
$db_file = '/var/www/html/users.db'; // Gunakan DB yang sama
try {
$db = new PDO('sqlite:' . $db_file);
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} catch (PDOException $e) {
die("Error connecting to database: " . $e->getMessage());
}
if (isset($_GET['id'])) {
$id = $_GET['id']; // Vulnerable
// --- VULNERABLE CODE ---
$sql = "SELECT * FROM products WHERE id = $id LIMIT 1";
echo "Menjalankan kueri: <pre>" . htmlspecialchars($sql) . "</pre>";
try {
$stmt = $db->query($sql);
$product = $stmt->fetch(PDO::FETCH_ASSOC);
if ($product) {
echo "<p>Menampilkan produk: " . htmlspecialchars($product['name']) . "</p>";
echo "<p>Deskripsi: " . htmlspecialchars($product['description']) . "</p>";
} else {
echo "<p>Produk tidak ditemukan.</p>";
}
} catch (PDOException $e) {
// --- ERROR-BASED ---
// Ini akan menampilkan error SQLite asli
echo "<hr><b style='color:red;'>SQLite Error:</b> <pre>" . htmlspecialchars($e->getMessage()) . "</pre>";
}
} else {
echo "<p>Silakan berikan ID produk, cth: <a href='product.php?id=1'>product.php?id=1</a></p>";
}
?>
</body></html>
Simpan dan keluar.
5. Build & Jalankan Container:
sudo docker build -t lab-sqli-target .
sudo docker run -d -p 8080:80 --name target-sqli-lab lab-sqli-target
Target Anda sekarang berjalan di http://[IP_UBUNTU_ANDA]:8080.
Di VM Kali Linux Anda, buka terminal.
1. Serangan Error-Based (Manual):
Buka browser di Kali dan kunjungi URL berikut. Tambahkan tanda kutip (') di akhir.
http://[IP_UBUNTU_ANDA]:8080/product.php?id=1'
Hasil: Halaman akan menampilkan error SQLite asli, contoh: SQLite Error: ... unrecognized token: "'1'".
2. Serangan Error-Based (sqlmap):
Gunakan sqlmap (dari toolkit Anda) untuk mengeksploitasi error ini.
sqlmap -u "http://[IP_UBUNTU_ANDA]:8080/product.php?id=1" --batch --level=5 --risk=3 --dbms=sqlite
Hasil: sqlmap akan mengidentifikasi kerentanan dan bahkan bisa men-dump nama tabel (`users`, `products`).
3. Serangan Boolean-Based (Manual):
Buka http://[IP_UBUNTU_ANDA]:8080/index.php. Coba login dengan payload klasik SQLite di field Username. Password bisa diisi apa saja.
admin' --
Hasil: Halaman akan memuat ulang dan menampilkan "Login Berhasil!" serta flagnya.
Perbaikan yang **benar** adalah menggunakan **Prepared Statements** (Query berparameter).
1. Hentikan & Hapus Container Lama:
sudo docker stop target-sqli-lab
sudo docker rm target-sqli-lab
cd ~/lab-sqli
2. Perbaiki index.php (Halaman Login):
Buka file `index.php` dan ganti **SELURUH ISINYA** dengan kode aman di bawah ini.
nano index.php
<?php
// --- DB Setup ---
$db_file = '/var/www/html/users.db';
try {
$db = new PDO('sqlite:' . $db_file);
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$db->exec("CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY, username TEXT, password TEXT)");
if ($db->query("SELECT COUNT(*) FROM users")->fetchColumn() == 0) {
$hashed_pass = password_hash('password123', PASSWORD_DEFAULT);
$db->exec("INSERT INTO users (username, password) VALUES ('admin', '$hashed_pass')");
}
} catch (PDOException $e) {
die("Error setting up database: " . $e->getMessage());
}
// --- Login Logic ---
$login_message = '';
$sql_query_display = '';
if ($_SERVER["REQUEST_METHOD"] == "POST" && isset($_POST['username'])) {
$user = $_POST['username'];
$pass = $_POST['password'];
// --- FIXED CODE (Prepared Statements) ---
$sql = "SELECT * FROM users WHERE username = ?"; // Hanya username
$sql_query_display = "SELECT * FROM users WHERE username = ?";
try {
$stmt = $db->prepare($sql);
$stmt->execute([$user]); // Bind parameter
$result = $stmt->fetch(PDO::FETCH_ASSOC);
// Verifikasi password yang benar
if ($result && password_verify($pass, $result['password'])) {
$login_message = "<h3 style='color:green;'>Login Berhasil!</h3><p>Selamat datang, " . htmlspecialchars($result['username']) . "</p>";
} else {
$login_message = "<h3 style='color:red;'>Login Gagal. Kredensial salah.</h3>";
}
} catch (PDOException $e) {
$login_message = "<h3 style='color:red;'>SQL Error:</h3><pre>" . htmlspecialchars($e->getMessage()) . "</pre>";
}
}
?>
<!DOCTYPE html>
<html lang="id">
<head>
<meta charset="UTF-8">
<title>Lab SQLi (AMAN)</title>
<style>
body { font-family: sans-serif; margin: 20px; background: #f4f4f4; }
.container { max-width: 400px; margin: 50px auto; padding: 20px; background: #fff; border-radius: 8px; box-shadow: 0 2px 5px rgba(0,0,0,0.1); }
input[type="text"], input[type="password"] { width: 100%; padding: 8px; margin-bottom: 10px; box-sizing: border-box; }
input[type="submit"] { background: #007bff; color: white; padding: 10px; border: none; border-radius: 4px; cursor: pointer; }
pre { background: #eee; padding: 10px; border-radius: 4px; white-space: pre-wrap; word-wrap: break-word; }
</style>
</head>
<body>
<div class="container">
<h2>Halaman Login (Aman)</h2>
<form method="POST" action="index.php">
<input type="text" name="username" placeholder="Username">
<input type="password" name="password" placeholder="Password">
<input type="submit" name="Login" value="Login">
</form>
<p>Coba juga halaman produk: <a href="product.php?id=1">product.php?id=1</a></p>
<?php
if (!empty($sql_query_display)) {
echo "<hr><p><b>Query SQL yang dijalankan:</b><pre>" . htmlspecialchars($sql_query_display) . "</pre></p>";
}
if (!empty($login_message)) {
echo $login_message;
}
?>
</div>
</body>
</html>
Simpan dan keluar.
3. Perbaiki product.php (Halaman Produk):
Buka file `product.php` dan ganti **SELURUH ISINYA** dengan kode aman di bawah ini.
nano product.php
<!DOCTYPE html>
<html><head><title>Lab SQLi - Produk (Aman)</title></head>
<body>
<h2>Detail Produk (Aman)</h2>
<p><a href="index.php">Kembali ke Login</a></p>
<?php
// --- DB Setup ---
$db_file = '/var/www/html/users.db'; // Gunakan DB yang sama
try {
$db = new PDO('sqlite:' . $db_file);
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
// Pastikan tabel ada jika file ini diakses lebih dulu
$db->exec("CREATE TABLE IF NOT EXISTS products (id INTEGER PRIMARY KEY, name TEXT, description TEXT)");
if ($db->query("SELECT COUNT(*) FROM products")->fetchColumn() == 0) {
$db->exec("INSERT INTO products (id, name, description) VALUES (1, 'Laptop', 'Laptop Canggih'), (2, 'Mouse', 'Mouse Optik')");
}
} catch (PDOException $e) {
die("Error connecting to database: " . $e->getMessage());
}
if (isset($_GET['id'])) {
$id = $_GET['id'];
// --- FIXED CODE (Prepared Statements) ---
$sql = "SELECT * FROM products WHERE id = ? LIMIT 1";
echo "Menjalankan kueri aman: <pre>" . htmlspecialchars($sql) . "</pre>";
try {
$stmt = $db->prepare($sql);
$stmt->execute([$id]); // Bind parameter
$product = $stmt->fetch(PDO::FETCH_ASSOC);
if ($product) {
echo "<p>Menampilkan produk: " . htmlspecialchars($product['name']) . "</p>";
echo "<p>Deskripsi: " . htmlspecialchars($product['description']) . "</p>";
} else {
echo "<p>Produk tidak ditemukan atau ID tidak valid.</p>";
}
} catch (PDOException $e) {
// Ini seharusnya tidak terjadi lagi, tapi sebagai penjaga
echo "<hr><b style='color:red;'>Error:</b> <pre>" . htmlspecialchars($e->getMessage()) . "</pre>";
}
} else {
echo "<p>Silakan berikan ID produk, cth: <a href='product.php?id=1'>product.php?id=1</a></p>";
}
?>
</body></html>
Simpan dan keluar.
4. Build Ulang Container yang Aman:
sudo docker build -t lab-sqli-target-aman .
sudo docker run -d -p 8080:80 --name target-sqli-lab-aman lab-sqli-target-aman
5. Verifikasi (Red Team):
Buka http://[IP_ANDA]:8080/product.php?id=1'. Hasilnya: Halaman tidak akan error. Akan tertulis "Produk tidak ditemukan...".
Buka http://[IP_ANDA]:8080/index.php dan masukkan admin' --. Hasilnya: Login Gagal. Serangan SQLi telah diperbaiki.
Tujuan: Mengeksploitasi Reflected XSS di pencarian dan Stored XSS di buku tamu.
Di Host Ubuntu Anda, buat server yang rentan terhadap XSS.
1. Buat Folder & File Lab:
mkdir ~/lab-xss
cd ~/lab-xss
2. Buat Dockerfile (Diperbarui):
nano Dockerfile
Salin-tempel konten berikut (kita menambahkan libsqlite3-dev dan pdo_sqlite):
FROM php:8.1-apache
# [PERBAIKAN] Instal dependensi sistem (libsqlite3-dev) terlebih dahulu
RUN apt-get update && apt-get install -y \
libsqlite3-dev \
&& apt-get clean && rm -rf /var/lib/apt/lists/*
# Instal PDO dan SQLite untuk database buku tamu
RUN docker-php-ext-install pdo pdo_sqlite
# Atur izin agar Apache bisa menulis ke database
RUN touch /var/www/html/comments.db && chown www-data:www-data /var/www/html/comments.db && chmod 664 /var/www/html/comments.db
COPY . /var/www/html/
EXPOSE 80
Simpan dan keluar.
3. Buat Halaman search.php (Rentan Reflected XSS):
nano search.php
Salin-tempel kode ini:
<!DOCTYPE html><html><head><title>Lab XSS - Pencarian</title></head>
<body>
<h2>Pencarian Produk (Reflected)</h2>
<form method="GET" action="search.php">
<input type="text" name="q" placeholder="Cari...">
<input type="submit" value="Cari">
</form>
<div class="results">
<?php
if (isset($_GET['q'])) {
// --- VULNERABLE CODE ---
echo "Menampilkan hasil pencarian untuk: " . $_GET['q'];
}
?>
</div>
<a href="guestbook.php">Pergi ke Buku Tamu</a>
</body></html>
Simpan dan keluar.
4. Buat Halaman guestbook.php (Rentan Stored XSS):
nano guestbook.php
Salin-tempel kode ini. Halaman ini akan menyimpan dan menampilkan komentar tanpa sanitasi.
<?php
// Setup Database SQLite
$db_file = '/var/www/html/comments.db';
try {
$db = new PDO('sqlite:' . $db_file);
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
// Buat tabel jika belum ada
$db->exec("CREATE TABLE IF NOT EXISTS comments (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT, comment TEXT, timestamp DATETIME DEFAULT CURRENT_TIMESTAMP)");
} catch (PDOException $e) {
die("Error: " . $e->getMessage());
}
// Logika POST (Menyimpan data)
if ($_SERVER["REQUEST_METHOD"] == "POST" && !empty($_POST['comment'])) {
// --- VULNERABLE CODE (Input) ---
// Menyimpan data langsung ke database
$stmt = $db->prepare("INSERT INTO comments (name, comment) VALUES (?, ?)");
$stmt->execute([$_POST['name'], $_POST['comment']]);
header("Location: guestbook.php"); // Refresh halaman
exit;
}
?>
<!DOCTYPE html><html><head><title>Lab XSS - Buku Tamu</title></head>
<body>
<h2>Buku Tamu (Stored XSS)</h2>
<form method="POST" action="guestbook.php">
Nama: <input type="text" name="name"><br>
Komentar: <textarea name="comment"></textarea><br>
<input type="submit" value="Kirim">
</form>
<a href="search.php">Pergi ke Pencarian</a>
<hr>
<h3>Komentar:</h3>
<?php
// Logika GET (Menampilkan data)
$result = $db->query("SELECT * FROM comments ORDER BY timestamp DESC");
foreach ($result as $row) {
// --- VULNERABLE CODE (Output) ---
// Menampilkan data langsung dari DB tanpa sanitasi
echo "<div style='border:1px solid #ccc; padding:10px; margin-bottom:10px;'>";
echo "<b>" . $row['name'] . "</b> menulis:<br>";
echo "<p>" . $row['comment'] . "</p>";
echo "<small>" . $row['timestamp'] . "</small>";
echo "</div>";
}
?>
</body></html>
Simpan dan keluar.
5. Build & Jalankan Container:
sudo docker build -t lab-xss-target .
sudo docker run -d -p 8081:80 --name target-xss-lab lab-xss-target
Target Anda sekarang berjalan di port 8081.
Di VM Kali Linux Anda.
1. Serangan Reflected XSS (Browser):
Buka browser di Kali dan kunjungi URL berikut. Ganti IP-nya.
http://[IP_UBUNTU_ANDA]:8081/search.php?q=<script>alert('Reflected XSS')</script>
Hasil: Browser Anda akan langsung memunculkan kotak "alert". Ini hanya memengaruhi Anda.
2. Serangan Stored XSS (Browser):
http://[IP_UBUNTU_ANDA]:8081/guestbook.php.<script>alert('Stored XSS by Root Bakar')</script>Hasil: Halaman akan me-refresh, dan kotak "alert" akan muncul. Tutup alert-nya, lalu refresh halaman (F5) lagi. Alert akan muncul **LAGI**. Ini karena payload tersimpan di database.
Perbaikan untuk XSS (Reflected dan Stored) adalah sama: **selalu lakukan *escaping* (sanitasi) data pada saat menampilkannya (output)**.
1. Hentikan & Hapus Container Lama:
sudo docker stop target-xss-lab
sudo docker rm target-xss-lab
cd ~/lab-xss
2. Perbaiki search.php (Sanitasi Output):
nano search.php
Ubah baris PHP yang rentan:
UBAH DARI: echo "Menampilkan hasil pencarian untuk: " . $_GET['q'];
UBAH MENJADI:
// --- FIXED CODE ---
echo "Menampilkan hasil pencarian untuk: " . htmlspecialchars($_GET['q'], ENT_QUOTES, 'UTF-8');
Simpan dan keluar.
3. Perbaiki guestbook.php (Sanitasi Output):
nano guestbook.php
Temukan bagian `// Logika GET (Menampilkan data)`. Ubah blok `echo` yang rentan:
UBAH DARI:
echo "<b>" . $row['name'] . "</b> menulis:<br>";
echo "<p>" . $row['comment'] . "</p>";
UBAH MENJADI (kode aman):
// --- FIXED CODE (Output Sanitization) ---
echo "<b>" . htmlspecialchars($row['name'], ENT_QUOTES, 'UTF-8') . "</b> menulis:<br>";
echo "<p>" . htmlspecialchars($row['comment'], ENT_QUOTES, 'UTF-8') . "</p>";
Simpan dan keluar.
4. Build & Jalankan Container Aman:
sudo docker build -t lab-xss-target-aman .
sudo docker run -d -p 8081:80 --name target-xss-lab-aman lab-xss-target-aman
5. Verifikasi (Red Team):
Buka http://[IP_ANDA]:8081/search.php?q=<script>alert(1)</script>. Hasil: Tidak ada alert, teks payload tercetak di layar.
Buka http://[IP_ANDA]:8081/guestbook.php. Hasil: Tidak ada alert, komentar lama Anda (jika masih ada) akan ditampilkan sebagai teks <script>..., bukan dieksekusi.
Tujuan: Mengakses data pengguna lain dengan memanipulasi parameter ID di URL.
Di Host Ubuntu Anda, buat server yang rentan terhadap IDOR.
1. Buat Folder & File Lab:
mkdir ~/lab-idor
cd ~/lab-idor
2. Buat Dockerfile:
nano Dockerfile
Salin-tempel konten berikut:
FROM php:8.1-apache
COPY . /var/www/html/
# Aktifkan session
RUN mv "$PHP_INI_DIR/php.ini-production" "$PHP_INI_DIR/php.ini"
EXPOSE 80
Simpan dan keluar.
3. Buat Halaman index.php (Halaman Login):
nano index.php
Salin-tempel kode ini. Ini adalah halaman login palsu.
<?php session_start(); ?>
<!DOCTYPE html>
<html>
<head><title>Lab IDOR - Login</title></head>
<body>
<h2>Login sebagai Pengguna</h2>
<p>Klik untuk login sebagai 'Pengguna Anda' (ID: 1) atau 'User Lain' (ID: 2).</p>
<ul>
<li><a href="login_process.php?id=1" style="font-size: 1.2em;">Login sebagai 'Pengguna Anda' (ID 1)</a></li>
<li><a href="login_process.php?id=2" style="font-size: 1.2em;">Login sebagai 'User Lain' (ID 2)</a></li>
</ul>
</body>
</html>
Simpan dan keluar.
4. Buat login_process.php (Penyetel Sesi):
nano login_process.php
Salin-tempel kode ini. Ini mensimulasikan login.
<?php
session_start();
$user_id = $_GET['id'] ?? 1;
$_SESSION['user_id'] = $user_id;
$_SESSION['username'] = ($user_id == 1) ? 'Pengguna Anda' : 'User Lain';
// Arahkan ke halaman profil
header("Location: profile.php?id=" . $user_id);
exit;
Simpan dan keluar.
5. Buat Halaman profile.php yang Rentan:
nano profile.php
Salin-tempel kode PHP berikut. Halaman ini **rentan** karena tidak memeriksa sesi.
<?php
session_start();
// Database Palsu
$users_data = [
'1' => [
'nama' => 'Pengguna Anda',
'email_privat' => 'anda@gmail.com',
'no_hp_rahasia' => '081234567890'
],
'2' => [
'nama' => 'User Lain',
'email_privat' => 'userlain@gmail.com',
'no_hp_rahasia' => '089876543210'
]
];
// --- VULNERABLE CODE ---
// Mengambil data HANYA berdasarkan parameter 'id' dari URL.
// TIDAK memvalidasi siapa yang sedang login (dari $_SESSION)
$requested_id = $_GET['id'] ?? 0;
if (!array_key_exists($requested_id, $users_data)) {
die("Error: User tidak ditemukan.");
}
$user = $users_data[$requested_id];
?>
<!DOCTYPE html>
<html>
<head>
<title>Profil Pengguna</title>
<style>
body { font-family: sans-serif; margin: 20px; }
.profile { background: #fff; border: 1px solid #ccc; padding: 20px; border-radius: 8px; }
.welcome { font-size: 1.2em; }
.secret { color: red; font-weight: bold; background: #fff8c4; padding: 10px; }
</style>
</head>
<body>
<p class="welcome">
Anda login sebagai: <b><?php echo htmlspecialchars($_SESSION['username'] ?? 'Tamu'); ?></b> (ID: <?php echo htmlspecialchars($_SESSION['user_id'] ?? 'N/A'); ?>)
</p>
<hr>
<div class="profile">
<h2>Profil Milik: <?php echo htmlspecialchars($user['nama']); ?></h2>
<p class="secret">Email Privat: <?php echo htmlspecialchars($user['email_privat']); ?></p>
<p class="secret">No. HP Rahasia: <?php echo htmlspecialchars($user['no_hp_rahasia']); ?></p>
</div>
</body>
</html>
Simpan dan keluar.
6. Build & Jalankan Container:
sudo docker build -t lab-idor-target .
sudo docker run -d -p 8082:80 --name target-idor-lab lab-idor-target
Target Anda sekarang berjalan di http://[IP_UBUNTU_ANDA]:8082.
Di VM Kali Linux Anda.
1. Skenario Login Normal:
http://[IP_UBUNTU_ANDA]:8082 di browser Anda.profile.php?id=1.2. Eksploitasi IDOR (Manual):
profile.php?id=1 menjadi profile.php?id=2 dan tekan Enter.Hasil: Meskipun Anda login sebagai "Pengguna Anda (ID 1)", halaman sekarang menampilkan data sensitif milik "User Lain (ID 2)", yaitu "email_privat: userlain@gmail.com". Ini adalah IDOR.
Cara memperbaiki ini adalah dengan **memvalidasi ID sesi** terhadap ID yang diminta.
1. Hentikan & Hapus Container Lama:
sudo docker stop target-idor-lab
sudo docker rm target-idor-lab
cd ~/lab-idor
2. Perbaiki profile.php (Sanitasi Sesi):
nano profile.php
Tambahkan blok kode validasi ini tepat setelah `session_start();` di bagian atas file.
<?php
session_start();
// --- FIXED CODE (VALIDASI SESI) ---
// Cek apakah pengguna sudah login
if (!isset($_SESSION['user_id'])) {
// Arahkan ke halaman login jika belum login
header("Location: index.php");
exit;
}
// Cek apakah ID yang diminta sama dengan ID yang login
if ($_GET['id'] != $_SESSION['user_id']) {
// Jika tidak sama, tampilkan pesan error dan hentikan eksekusi
die("Akses Ditolak! Anda tidak diizinkan melihat profil ini.");
}
// --- AKHIR PERBAIKAN ---
// Database Palsu
$users_data = [
// ... (sisa file biarkan sama) ...
Simpan dan keluar.
3. Build & Jalankan Container Aman:
sudo docker build -t lab-idor-target-aman .
sudo docker run -d -p 8082:80 --name target-idor-lab-aman lab-idor-target-aman
4. Verifikasi (Red Team):
http://[IP_ANDA]:8082 dan login sebagai "Pengguna Anda (ID 1)".profile.php?id=1 (Ini akan berhasil).profile.php?id=2.Hasil: Halaman akan menampilkan "Akses Ditolak! Anda tidak diizinkan melihat profil ini." Kerentanan IDOR telah berhasil ditutup.