目錄
反射型
low
服務器代碼:
<?php
header ("X-XSS-Protection: 0");
// Is there any input?
if( array_key_exists( "name", $_GET ) && $_GET[ 'name' ] != NULL ) {
// Feedback for end user
$html .= '<pre>Hello ' . $_GET[ 'name' ] . '</pre>';
}
?>
代碼解釋:
對輸入的內容沒有任何過濾。直接構造:
<script>alert(1)</script>
medium
服務器代碼:
<?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
$html .= "<pre>Hello ${name}</pre>";
}
?>
關鍵代碼解釋:
str_replace():以其他字符替換字符串中的一些字符(區分大小寫)。
第一個參數:必需,指定需要被替換的值。
第二個參數:必需,指定要替換成的值。
第三個參數:必需,指定被檢索的字符串。
第四個參數:可選,指定對替換數進行計數的變量。
代碼解釋:
可以看到把<script>替換成了空,str_replace函數區分大小寫。這裏我們可以不用這個標籤了,也可以換成大寫。
<Script>alert(1)</Script>
<img src=1 οnerrοr=alert(1)>
high
服務器代碼:
<?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
$html .= "<pre>Hello ${name}</pre>";
}
?>
關鍵代碼解釋:
preg_replace():執行一個正則表達式的替換和搜索。
第一個參數:必需,指定要搜索的模式,可以是字符串或者一個字符串數組。
第二個參數:必需,指定用於替換的字符串或者字符串數組。
第三個參數:必需,指定要被檢索替換的字符串或者字符串數組。
第四個參數:可選,指定每個被檢索的字符串的最大可替換數。
第五個參數:可選,指定爲替換執行的次數。
代碼解釋:
/i忽略大小寫,所以大寫也不可以了。並且只要有script順序的都被過濾了。這裏用別的標籤即可。
<img src=1 οnerrοr=alert(1)>
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
$html .= "<pre>Hello ${name}</pre>";
}
// Generate Anti-CSRF token
generateSessionToken();
?>
關鍵代碼解釋:
Htmlspecialchars():把預定義的字符 "<" 、 ">" 、& 、‘’、“” 轉換爲 HTML 實體,防止瀏覽器將其作爲HTML元素。
代碼解釋:
輸入進去的內容只會被當做字符串,不會被當做html元素,不管輸入什麼都不會被執行。
存儲型
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();
}
?>
關鍵代碼解釋:
1.trim():移除字符串兩側的字符。
第一個參數:必需,指定要檢查的字符串。
第二個參數:可選,指定從字符串中刪除哪些字符。如果省略,則移除預定義字符,\0 ,\t,\n,\x0B,\r,空格。
2.stripslashes():刪除反斜槓。
只有一個參數,必需,指定要檢查的字符串。
3.mysqli_real_escape_string():轉義在 SQL 語句中使用的字符串中的特殊字符,\x00,\n,\r,\,‘,“,\x1a。
第一個參數:必需,指定要使用的mysql的連接。
第二個參數:必需,指定要轉義的字符串。
代碼解釋:
對用戶的輸入沒有做任何防護措施,直接存儲到數據庫中。
這裏對name做了長度的限制,可以在前端改一下。message可以直接輸入。
<script>alert(1)</script>
可以在數據庫中看到我們插入的內容。
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();
}
?>
關鍵代碼解釋:
1.addslashes():在每個預定義字符(單引號,雙引號,反斜槓,null)前面添加反斜槓。
只有一個參數,必需,指定需要轉義的字符串。
2.strip_tag():函數剝去字符串中的 HTML、XML 以及 PHP 的標籤。該函數始終會剝離 HTML 註釋。
第一個參數:必需,指定要檢查的字符串。
第二個參數:可選,指定允許的標籤,這些標籤將不會被刪除。
代碼解釋:
對message先在每個預定義字符前面添加反斜槓,再去除html的標籤,並且做了實體化的處理。所以只能在name中進行xss測試。
可以看到對name的處理是將<script>替換成空,只做了這一項措施。可以雙寫繞過
<sc<script>ript>alert(/xss/)</script>
也可以大小寫混合繞過
<Script>alert(/xss/)</script>
還可以換一個標籤
<img src=1 οnerrοr=alert(1)>
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();
}
?>
代碼解釋:這一關就是比medium裏面多了對name的一項處理,這裏用了正則表達式的處理,處理同反射型的high。只要是script順序的都不行,不區分大小寫,所以用img標籤還是可以的。
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();
?>
代碼解釋:對name和message都進行了實體轉換impossible。同前。
DOM型
low
服務器端代碼什麼也沒有。
前端代碼:
關鍵代碼解釋:
1.document對象和windows對象。
document是一個文檔對象,代表給定瀏覽器窗口中的HTML文檔;windows是一個窗口對象,表示瀏覽器中打開的窗口。一個窗口下面可以有多個文檔對象。
所以一個窗口下面只能有一個window.location.href,但是可以有多個document.URL,document.location.href。
使用document對象可以對HTML文檔進行檢查、修改或添加內容,並處理該文檔的內部事件。
瀏覽器會爲HTML文檔創建一個window對象,併爲每個框架創建一個額外的window對象。
window是一個頂層對象,而不是另一個對象的屬性即瀏覽器的窗口。
document對象是window對象和frame對象的一個屬性,是顯示與窗口或框架內的一個文檔。
document只是屬於window的一個子對象。
Window.location包含href屬性,直接取值賦值相當於window.location.href(當前頁面完整URL)。
document.URL取值時等價於window.location.href或者document.location.href。在某些瀏覽器中通過對docum.URL賦值來實現頁面跳轉,但是某些瀏覽器中不行。
document.write可以在文檔流中寫入字符串。所以可以寫入標籤以及js代碼。參考 https://blog.csdn.net/m0_37589327/article/details/78992784
2.indexOf()方法:返回某個指定的字符串值在字符串中首次出現的位置。
第一個參數:必需的,規定要檢索的字符串。
第二個參數:可選,整數,規定要開始檢索的位置。省略將從頭開始檢索。
對大小寫敏感,如果沒有出現返回-1。
3.substring()方法:用於提取字符串中介於兩個指定下標之間的字符。
第一個參數:必需,開始的位置,非負整數。
第二個參數:可選,停止的位置,非負整數。如果省略,則到結尾。
4.decodeURI()方法:可對 encodeURI() 函數編碼過的 URI 進行解碼。只有一個參數,就是要解碼的URI或者其他文本。
代碼解釋:
先判斷 default= 是否存在,存在的話就將default的值取出來寫入option標籤中<option value='lang的值'>lang解碼的值</option>。對default的值沒有進行任何過濾,所以我們可以直接構造。
構造:
http://127.0.0.1/DVWA/vulnerabilities/xss_d/?default=%3Cscript%3Ealert(1)%3C/script%%3E
medium
服務器端代碼:
<?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;
}
}
?>
關鍵代碼解釋:
1.array_key_exists():檢查某個數組中是否存在指定的鍵名,如果鍵名存在則返回 true,如果鍵名不存在則返回 false。
第一個參數:必需,指定鍵名。
第二個參數:必需,指定數組。
2.stripos() :查找字符串在另一字符串中第一次出現的位置(不區分大小寫)。
第一個參數:必需,指定被檢索的字符串。
第二個參數:必需,指定要查找的字符串。
第三個參數:可選,指定開始檢索的位置。
如果沒有找到則返回false。
3.header():向客戶端發送原始的HTTP報頭。
第一個參數:必需,指定要發送的報頭字符串。
第二個參數:可選,指定該報頭是否替換之前的,默認是true即替換。否則添加第二個報頭,即false,允許同類型的多個報頭。
第三個參數:可選,強制指定HTTP響應碼。
代碼解釋:
過濾了<script>標籤。可以用別的標籤繞過。這裏不能用雙寫。
medium的前端代碼和low的一樣,沒有過濾。但是插入的內容在select標籤的option標籤裏面,這裏用img標籤插入,所以我們要想執行的話要先閉合option標籤,再閉合select標籤。
構造一下:
</option></select><img src=1 οnerrοr=alert(1)>
high
前端代碼同前面一樣,沒有過濾。
服務器端代碼:
<?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;
}
}
?>
代碼解釋:根據傳入default參數的值,執行switch語句,switch的default裏面給default設置的值是English,所以傳入的只能是French,English,German,Spanish其中的一個。所以需要傳入的代碼不經過服務器的過濾。而url中如果有字符 # ,那麼#後面的數據不會送到服務器端。我們可以根據這個來構造:
English #<script>alert(1)</script>
impossible
服務器端什麼也沒有。
前端代碼:
跟之前的區別就是在將default的值寫入標籤的時候沒有對輸入的值進行解碼,所以我們輸入的任何值都是經過URL編碼的了,所以不存在。