Introduction Stored XSS di DVWA adalah teknik serangan di mana penyerang menyisipkan kode JavaScript berbahaya ke dalam aplikasi web dan menyimpannya di server (misalnya melalui form komentar). Setiap kali halaman tersebut dibuka oleh pengguna lain, kode tersebut otomatis dijalankan di browser mereka.
Analogi Bayangkan sebuah buku tamu di tempat umum; jika seseorang menuliskan pesan berbahaya di dalamnya, setiap orang yang membaca buku tersebut akan terus melihat pesan itu dan bisa terpengaruh tanpa menyadari risikonya.
Dampak Serangan ini dapat menyebabkan pencurian cookie secara massal, pengambilalihan akun banyak pengguna, penyebaran malware, hingga manipulasi konten website secara terus-menerus karena payload tersimpan secara permanen di server.
LOW
Bericut adalah source code untuk level low
<?php
if( isset( $_POST[ 'btnSign' ] ) ) {
// Get input
$message = trim( $_POST[ 'mtxMessage' ] );
$name = trim( $_POST[ 'txtName' ] );
// Sanitize message input
$message = stripslashes( $message );
$message = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $message ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));
// Sanitize name input
$name = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $name ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));
// Update database
$query = "INSERT INTO guestbook ( comment, name ) VALUES ( '$message', '$name' );";
$result = mysqli_query($GLOBALS["___mysqli_ston"], $query ) or die( '<pre>' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '</pre>' );
//mysql_close();
}
?> ada beberapa proses sanitasi seperti dengan function stripslashes( ) dan mysqli_real_escape_string ( ).
Step 1: ubah max length dari textarea agar dapat masuk payload kita semua

step 2: kita masukan payload ke dalam text area dan kita click sign guestbook
<script>window.location='https://www.google.com/search?client=firefox-b-e&channel=entpr&q=cat'</script>
step 3: maka akan keluaralert, dan click ok

step 4: akan ke reroute ke google.com/cat

MEDIUM
Berikut adalah source code dari level medium
<?php
if( isset( $_POST[ 'btnSign' ] ) ) {
// Get input
$message = trim( $_POST[ 'mtxMessage' ] );
$name = trim( $_POST[ 'txtName' ] );
// Sanitize message input
$message = strip_tags( addslashes( $message ) );
$message = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $message ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));
$message = htmlspecialchars( $message );
// Sanitize name input
$name = str_replace( '<script>', '', $name );
$name = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $name ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));
// Update database
$query = "INSERT INTO guestbook ( comment, name ) VALUES ( '$message', '$name' );";
$result = mysqli_query($GLOBALS["___mysqli_ston"], $query ) or die( '<pre>' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '</pre>' );
//mysql_close();
}
?>Dapat kita lihat bahwa sanitasi di level ini jauh lebih ketat di bagian message input, sekarang sudah ada function strip_tags( ) dan htmlspecialchars( ), yang memperseulit memasukan payload pada kolom tersebut. Sehingga pada level ini kita akan memasukan payload pada kolom nama.
Pertama-tama kita ubah maxlength dari input tag terlebih dahulu.

masukan payload kedalam kolom name, dan untuk message kita bisa masukan dummy message apapun.

<img src=x onerror=window.location='https://www.google.com'>
atau
<scr<script>ipt>window.location='https://www.google.com'</script>diatas merupakan payload yang kita gunakan, kita akan redirect ke google.com saja, jika kita mencoba redirect ke link sebelumnya yang menampilkan gambar kucing maka tidak berhasil, ini dikarenakan URL yang lebih kompleks mengandung karakter seperti &, yang bisa mengganggu parsing di dalam atribut HTML atau string JavaScript. Jadi URL sederhana seperti https://google.com jalan lancar, sedangkan URL panjang bisa gagal karena isu encoding dan parsing.
HIGH
Berikut adalah source code dari level high.
<?php
if( isset( $_POST[ 'btnSign' ] ) ) {
// Get input
$message = trim( $_POST[ 'mtxMessage' ] );
$name = trim( $_POST[ 'txtName' ] );
// Sanitize message input
$message = strip_tags( addslashes( $message ) );
$message = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $message ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));
$message = htmlspecialchars( $message );
// Sanitize name input
$name = preg_replace( '/<(.*)s(.*)c(.*)r(.*)i(.*)p(.*)t/i', '', $name );
$name = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $name ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));
// Update database
$query = "INSERT INTO guestbook ( comment, name ) VALUES ( '$message', '$name' );";
$result = mysqli_query($GLOBALS["___mysqli_ston"], $query ) or die( '<pre>' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '</pre>' );
//mysql_close();
}
?> Dapat kita lihat bahwa kedua kolom, yaitu name input dan message input sekarang mempunyai kemanan yang jauh lebih ketat. Untuk sanitasi kolom message mungkin masih sama seperti pada level medium, ada function strip_tags( ) dan addslashes( ). Tetapi ada penambahan sanitasi pada name input, yang menggunakan function preg_replace( ) yang menggubah " /< " , " s ", " c ", " r " , " i ", " p ", dan " t".
untuk level high kita masih dapat menggunakan payload sebelumnya, yaitu:
<img src=x onerror=window.location='https://www.google.com'>kita dapat masukan payload tersebut di kolom name input, saya mencoba memasukan payload yang sama di bagian message input, tetapi gagal.
IMPOSSIBLE
Berikut adalah source code untuk level impossible
?php
if( isset( $_POST[ 'btnSign' ] ) ) {
// Check Anti-CSRF token
checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' );
// Get input
$message = trim( $_POST[ 'mtxMessage' ] );
$name = trim( $_POST[ 'txtName' ] );
// Sanitize message input
$message = stripslashes( $message );
$message = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $message ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));
$message = htmlspecialchars( $message );
// Sanitize name input
$name = stripslashes( $name );
$name = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $name ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));
$name = htmlspecialchars( $name );
// Update database
$data = $db->prepare( 'INSERT INTO guestbook ( comment, name ) VALUES ( :message, :name );' );
$data->bindParam( ':message', $message, PDO::PARAM_STR );
$data->bindParam( ':name', $name, PDO::PARAM_STR );
$data->execute();
}
// Generate Anti-CSRF token
generateSessionToken();
?>Pada level impossible di DVWA, aplikasi sulit direntas karena sudah menerapkan kombinasi perlindungan yang benar: semua input pengguna dibersihkan dengan fungsi seperti htmlspecialchars() sehingga karakter berbahaya (misalnya < dan >) diubah menjadi teks biasa dan tidak bisa dieksekusi sebagai JavaScript (mencegah XSS), penggunaan prepared statement (PDO dengan bindParam) mencegah injeksi SQL, serta adanya mekanisme token CSRF (checkToken dan generateSessionToken) memastikan setiap request valid berasal dari sesi pengguna yang sah. Dengan lapisan validasi input, encoding output, dan proteksi request ini, hampir tidak ada celah langsung untuk mengeksploitasi aplikasi seperti pada level sebelumnya.