DVWA XSS練習
反射型(Reflected)
Low
輸入框裏隨便輸入,比如aaa
點擊submit
訪問:http://localhost/DVWA/vulnerabilities/xss_r/?name=aaa#
查看源代碼發現只有一處有回顯:
<pre>Hello aaa</pre>
可控的變量是name
直接插script
標籤進行嘗試:
payload:<script>alert(1)</script>
成功彈窗
代碼分析:
<?php
header ("X-XSS-Protection: 0");
// Is there any input?
if( array_key_exists( "name", $_GET ) && $_GET[ 'name' ] != NULL ) {
// Feedback for end user
echo '<pre>Hello ' . $_GET[ 'name' ] . '</pre>';
}
?>
沒有做任何的過濾。
Medium
插script標籤進行嘗試:
查看源代碼:
<pre>Hello alert(1)</pre>
應該是過濾了<script>
一類的
構造payload:<scr<script>ipt>alert(1)</script>
成功彈窗
代碼分析
<?php
header ("X-XSS-Protection: 0");
// Is there any input?
if( array_key_exists( "name", $_GET ) && $_GET[ 'name' ] != NULL ) {
// Get input
$name = str_replace( '<script>', '', $_GET[ 'name' ] );
// Feedback for end user
echo "<pre>Hello ${name}</pre>";
}
?>
把<script>
標籤置換成了空
High
嘗試了 low , medium 等級的payload都是過濾掉了,試試img標籤。
payload:<img src=x onerror=alert(1)>
成功彈窗
<?php
header ("X-XSS-Protection: 0");
// Is there any input?
if( array_key_exists( "name", $_GET ) && $_GET[ 'name' ] != NULL ) {
// Get input
$name = preg_replace( '/<(.*)s(.*)c(.*)r(.*)i(.*)p(.*)t/i', '', $_GET[ 'name' ] );
// Feedback for end user
echo "<pre>Hello ${name}</pre>";
}
?>
通過正則表達式進行了過濾,無論script
中間夾了什麼都能置空。
Impossible
<?php
// Is there any input?
if( array_key_exists( "name", $_GET ) && $_GET[ 'name' ] != NULL ) {
// Check Anti-CSRF token
checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' );
// Get input
$name = htmlspecialchars( $_GET[ 'name' ] );
// Feedback for end user
echo "<pre>Hello ${name}</pre>";
}
// Generate Anti-CSRF token
generateSessionToken();
?>
將輸入過來的值利用htmlspecialchars
函數實體化了。就基本沒有可能xss了。
htmlspecialchars:
語法:
htmlspecialchars(string,flags,character-set,double_encode)
定義和用法
htmlspecialchars() 函數把預定義的字符轉換爲 HTML 實體。預定義的字符是:
& (和號)成爲 &
" (雙引號)成爲 "
' (單引號)成爲 '
< (小於)成爲 <
> (大於)成爲 >
提示:如需把特殊的 HTML 實體轉換回字符,請使用
htmlspecialchars_decode()
函數。
存儲型(Stored)
Low
隨便輸入一個數據,查看源代碼:
<div id="guestbook_comments">
Name: admin<br />
Message: test<br />
</div>
構造payload:
<script>alert(1)</script>
發現Name處有長度限制。不過Message部分也可以觸發xss進行彈窗。
看看能不能通過審查元素更改Name輸入框的長度限制:
查看源代碼:
<input name="txtName" type="text" size="30" maxlength="10">
把maxlength="10"
刪掉,提交後發現彈了兩下窗口。
測試完成以後可以點擊那個Clear Guestbook
來清除之前提交的記錄,不然每次刷新都彈窗很煩。
代碼分析
<?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();
}
?>
代碼的大概意思就是收到name跟message之後直接存到數據庫裏。基本上沒有什麼過濾,message是用stripslashes
函數去除了一下斜槓。
Medium
提交了 Low 等級的payload後返回頁面的源代碼:
<div id="guestbook_comments">
Name: aaa<br>
Message: alert(1)<br>
</div>
也是<script>
標籤被過濾了。
構造一下:<scr<script>ipt>alert(1)</script>
還是被過濾了,代碼可能是跟反射型漏洞的high一樣叭,試試別的標籤。
<img src=x onerror=alert(1)>
em~ 也過濾了。
看了一眼代碼,發現是把message實體化了,只能通過Name來傳payload了。
也是利用代碼審計把長度限制去掉,也可以利用抓包工具在改一下數據內容。
payload:<scr<script>ipt>alert(1)</script>
就可以彈窗了。
代碼分析
<?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();
}
?>
代碼的意思就是說,傳入message跟name,然後兩個變量都是把<script>
置空,然後把message實體化之後才保存至數據庫。
High
既然medium難度就把message實體化了,所以high應該也是實體化了,所以把name的長度限制去掉,傳入一個img標籤的payload試試:
payload:<img src=x onerror=alert(1)>
彈窗了。
代碼分析
<?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();
}
?>
處理方法跟反射型的high難度有異曲同工之妙。也是利用了正則表達式。
Impossiable
impossiable難度是安全的。
代碼分析
<?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();
?>
兩個變量都實體化了。
Dom型 (DOM)
Dom的一開始做了只做會了Low等級的,就暫時放一邊了。
Low
點擊select時訪問:http://localhost/DVWA/vulnerabilities/xss_d/?default=English
查看源代碼發現default之後的值就是<option>
標籤裏的值,查看源代碼:
<option value="English">English</option>
payload:
<script>alert(1)</script>
成功彈窗:
代碼裏什麼也沒寫。
彈窗以後想要逃出value="English"
這個地方觸發xss。但是">
傳入這個之後沒有什麼反應。查看前端代碼時發現一段js代碼:
if (document.location.href.indexOf("default=") >= 0) {
var lang = document.location.href.substring(document.location.href.indexOf("default=")+8);
document.write("<option value='" + lang + "'>" + decodeURI(lang) + "</option>");
document.write("<option value='' disabled='disabled'>----</option>");
}
document.write("<option value='English'>English</option>");
document.write("<option value='French'>French</option>");
document.write("<option value='Spanish'>Spanish</option>");
document.write("<option value='German'>German</option>");
這裏lang就是我們傳入的值。
在瀏覽器的console裏查看了一下lang的值,發現都是經過了url編碼的。
Medium
嘗試了一下Low等級的payload,直接返回到了
http://localhost/DVWA/vulnerabilities/xss_d/?default=English
這個地址。
嘗試別的標籤:http://localhost/DVWA/vulnerabilities/xss_d/?default=<img src=x onerror=alert(1)>
還是沒有什麼反應。
嘗試關閉了option
標籤也是逃不過去,看了一下html嵌套規則:https://www.cnblogs.com/xiyangbaixue/p/4090511.html
發現得把select標籤頁一併關閉纔可以,構造一下payload:
</option></select><img src=x onerror=alert(1)>
成功
代碼分析:
<?php
// Is there any input?
if ( array_key_exists( "default", $_GET ) && !is_null ($_GET[ 'default' ]) ) {
$default = $_GET['default'];
# Do not allow script tags
if (stripos ($default, "<script") !== false) {
header ("location: ?default=English");
exit;
}
}
?>
發現只要包含script標籤就會執行跳轉。
High
首先嚐試了一下medium難度的payload,執行了跳轉操作。
沒什麼思路,看了一下代碼:
<?php
// Is there any input?
if ( array_key_exists( "default", $_GET ) && !is_null ($_GET[ 'default' ]) ) {
# White list the allowable languages
switch ($_GET['default']) {
case "French":
case "English":
case "German":
case "Spanish":
# ok
break;
default:
header ("location: ?default=English");
exit;
}
}
?>
網上搜了一下:https://www.cnblogs.com/huangming-zzz/p/9901770.html
利用了#號,URL中#號之後的內容,不會被提交到服務器,可以直接與瀏覽器進行交互
payload:?default=English#<script>alert(1)</script>
Impossiable
安全,後端也沒有代碼。前端跟之前的不一樣:
document.write("<option value='" + lang + "'>" + decodeURI(lang) + "</option>");
改成了:
document.write("<option value='" + lang + "'>" + (lang) + "</option>");
去掉了url解碼的部分