taibeihacker
Moderator
XSS 漏洞相关
1 定义及原理
XSS(跨站腳本攻擊),瀏覽器將用戶輸入的內容當做腳本執行,執行了惡意的功能,這種針對用戶瀏覽器的攻擊,即跨站腳本攻擊主要分為三個類型:
反射型
存儲型
DOM 型
XSS 危害:
盜取cookie
盜取賬戶
惡意軟件下載
鍵盤記錄
廣告引流
2 反射型 XSS
2.1 原理
應用程序或API 包含未经验证和未经转义的用户输入,直接作为 HTML 输出的一部分。一個成功的攻擊可以讓攻擊者在受害者的瀏覽器中執行任意的HTML 和JavaScript。特點:非持久化,必須用戶點擊帶有特定參數的鏈接才能引起。
影響範圍:僅執行腳本的用戶。
3 存储型 XSS
3.1 原理
存儲型XSS 是指應用程序通過Web 請求獲取不可信賴的數據,在未檢驗數據是否存在XSS 代碼的情況下,便將其存入数据库。當下一次從數據庫中獲取該數據時程序也未对其进行过滤,頁面再次執行XSS 代碼,存儲型XSS 可以持續攻擊用戶。存儲型XSS 出現位置:
留言板
評論區
用戶頭像
個性簽名
部落格
4 DOM 型 XSS
4.1 原理
4.1.1 DOM
DOM 模型用一個逻辑树來表示一個文檔,每個分支的終點都是一個節點(node),每個節點都包含著對象(objects)。 DOM 的方法(methods)讓你可以用特定方式操作這個樹,用這些方法你可以改變文檔的結構、樣式或者內容。
4.1.2 DOM XSS
DOM 型XSS 其實是一種特殊類型的反射型XSS,通過JS 操作 DOM 树動態地输出数据到页面,而不依賴於將數據提交給服務器端,它是基於DOM 文檔對像模型的一種漏洞。1
2
3
4
5
6
7
html
body
script
document.write('scriptalert(1)\/script')
/script
/body
/html
4.1.3 示例
首先這是一個DOM XSS,產生的原因是JS 代碼動態拼接了一個類似這樣的代碼:1
$('head').append('meta'+text+'/meta')
以下面的POC 為例:

可以看到div 中的代碼是被HTML 實體編碼後的形式,但是最後結果還是會彈窗

原因在於innerHTML 輸入進去的代碼是不會被執行的。
比如你按如下代碼來動態插入一個DOM節點
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
!DOCTYPE html
!DOCTYPE html
html lang='en'
head
meta charset='UTF-8'
meta http-equiv='X-UA-Compatible' content='IE=edge'
meta name='viewport' content='width=device-width, initial-scale=1.0'
titleDOM XSS POC/title
/head
body
div id='demo'lt;scriptgt;alert`1`lt;/scriptgt;/div
script src='https://libs.baidu.com/jquery/2.1.1/jquery.min.js'/script
br
div id='test'/div
script
document.getElementById('test').innerHTML=document.getElementById('demo').innerHTML + '';
/script
/body
/html
會發現div id=test 標籤不會被執行,但是jquery 之類的框架會在插入的時候把節點的標籤eval 下,使得它可以執行,因為這個append() 方法本身就是要讓插入的元素執行,有這個需求的。
4.1.4 与反射型 XSS 的异同与危害
同:都是沒有控制好輸入,並且把javascript 腳本輸入作為輸出插入到HTML 頁面。异:反射型XSS 是经过后端语言後,頁面引用後端輸出生效。
DOM XSS 是經過JS 對DOM 樹直接操作後插入到頁面。
危害性:前後端分離,不經過WAF 的檢測。
5 伪协议与编码绕过
5.1 伪协议
偽協議不同於因特網上所廣泛使用的如http://、https//、ftp://在URL 中使用,用於執行特定的功能Data 偽協議:
data:text/html;base64, PHNjcmlwdD5hbGVydCgxKTs8L3NjcmlwdD4=
JavaScript 偽協議:
javascript:alert('1')

5.2 编码绕过
5.2.1 UNICODE 编码
ISO(國際標誰化組織)制定的包括了地球上所有文化所有字母和符號的編碼,使用两个字节表示一個字符Unicode 只是一個符號集,它只規定了符號的二進制代碼,卻沒有規定這個二進制代碼應該如何存儲。具體存儲由:UTF-8,UTF-16 等實現

5.2.2 浏览器解码
解析一篇HTML 文檔時主要有三個處理過程:HTML 解析並創建DOM 樹,URL 解析和JavaScript 解析。每個解析器負責解碼和解析HTML 文檔中它所對應的部分,且順序也有所區別。
5.2.3 HTML 解析过程
5.2.3.1 解析过程
HTML 有 5 类元素:空元素(Void elements),有area、base、br、col、command、embed、hr、img、input、keygen、link、meta、param、source、track、wbr 等原始文本元素(Raw text elements),有script 和style
RCDATA 元素(RCDATA elements),有textarea 和title
外部元素(Foreign elements),例如MathML 命名空間或者SVG 命名空間的元素
基本元素(Normal elements),即除了以上4 種元素以外的元素
五类元素的区别如下:空元素,不能容納任何內容(因為它們沒有閉合標籤,沒有內容能夠放在開始標籤和閉合標籤中間)。
原始文本元素,可以容納文本。
RCDATA 元素,可以容納文本和字符引用。
外部元素,可以容納文本、字符引用、CDATA 段、其他元素和註釋
基本元素,可以容納文本、字符引用、其他元素和註釋
HTML 解析器以狀態機的方式運行,它從文檔輸入流中消耗字符並根據其轉換規則轉換到不同的狀態。

以如下代碼作為示例:
1
2
3
4
5
html
body
This is Geekby's blog
/body
/html
初始狀態為「Data」State,當遇到字符,狀態變為「Tag open」state,讀取一個a-z 的字符將產生一個開始標籤符號,狀態相應變為「Tag name」state,一直保持這個狀態直到讀取到,每個字符都附加到這個符號名上,例子中創建的是一個html 符號。
當讀取到,當前的符號就完成了,此時,狀態回到「Data」state,body 標籤重複這一處理過程。此時,html 和body 標籤都識別出來了。現在,回到「Data」State,讀取「This is Geekby’s blog」中的每個字符生成一個字符符號。
這樣直到遇到/body 中的。現在,又回到了「Tag open」,讀取下一個字符/,進入到「Close tag open」,創建一個閉合標籤符號,並且狀態轉移到「Tag name」state,還是保持這一狀態,直到遇到。然後,產生一個新的標籤符號並回到「Data」State。後面的閉合標籤處理過程同上。
資訊
HTML 解析器處於数据状态(Data State)、RCDATA 状态(RCDATA State)、属性值状态(Attribute ValueState)时,字符實體會被解碼為對應的字符。
示例
1
div#60;img src=x onerror=alert(4)#62;/div
和被編碼為字符實體#60; 和#62;
當HTML 解析器解析完div 時,會進入數據狀態並發布標籤令牌。
接著解析到實體#60; 時因為處在數據狀態,就會對實體進行解碼為,
後面的#62; 同樣道理被解碼為。
問題
被解碼後,img 是否會被解析為HTML 標籤而導致JS 執行呢?
因為解析器在使用字符引用後不會轉換到標籤打開狀態(Tag Open State),不進入標籤打開狀態就不會被發佈為HTML 標籤。因此,不會創建新HTML 標籤,只會將其作為數據來處理。
5.2.3.2 几种特殊情况
原始文本元素在HTML中,屬於Raw text elements 的標籤有兩個:script、style。在Raw text elements 類型標籤下的所有內容塊都屬於該標籤。
Raw textelements 類型標籤下的所有字符實體編碼都不會被HTML 解碼。 HTML 解析器解析到script、style 標籤的內容塊(數據)部分時,狀態會進入Script Data State,該狀態並不在我們前面說的會解碼字符實體的三條狀態之中。
因此,script#97;#108;#101;#114;#116#40;#57;#41;#59/script 這樣字符實體並不會被解碼,也就不會執行JS。
RCDATA 情況
在HTML中,屬於RCDATA 的標籤有兩個:textarea、title。
RCDATA Elements 類型的標籤可以包含文本內容和字符實體。
解析器解析到textarea、title 標籤的數據部分時,狀態會進入RCDATA State。
前面我們提到,處於RCDATA State 狀態時,字符實體是會被解析器解碼的。
示例
1
textarea#60;script#62;alert(5)#60;/script#62;/textarea
解析器解析到它們時會進行解碼
但是裡面的JS 同樣還是不會被執行,原因還是因為解碼字符實體狀態機不會進入標籤打開狀態(Tag Open State),因此裡面的script 並不會被解析為HTML 標籤
5.2.4 JavaScript 解析
形如\uXXXX 這樣的Unicode 字符轉義序列或Hex 編碼是否能被解碼需要看情況。首先,JavaScript 中有三個地方可以出現Unicode 字符轉義序列:
字符串中
Unicode 轉義序列出現在字符串中時,它只會被解釋為普通字符,而不會破壞字符串的上下文。
例如,scriptalert('\u0031\u0030');/script
被編碼轉義的部分為10,是字符串,會被正常解碼,JS 代碼也會被執行。
標識符中
若Unicode 轉義序列存在於標識符中,即變量名(如函數名等…),它會被進行解碼。
例如,script\u0061\u006c\u0065\u0072\u0074(10);/script
被編碼轉義的部分為alert 字符,是函數名,屬於在標識符中的情況,因此會被正常解碼,JS 代碼也會被執行。
控製字符中
若Unicode 轉義序列存在於控製字符中,那麼它會被解碼但不會被解釋為控製字符,而會被解釋為標識符或字符串字符的一部分。
控製字符即‘、'、() 等。
例如,scriptalert\u0028'xss');/script,( 進行了Unicode 編碼,那麼解碼後它不再是作為控製字符,而是作為標識符的一部分alert( 。
因此函數的括號之類的控製字符進行Unicode 轉義後是不能被正常解釋的。
示例
1
script\u0061\u006c\u0065\u0072\u0074\u0028\u0031\u0031\u0029/script
被編碼部分為alert(11)。該例子中的JS 不會被執行,因為控製字符被編碼了。
1
script\u0061\u006c\u0065\u0072\u0074(\u0031\u0032)/script
被編碼部分為alert 及括號內為12。該例子中JS 不會被執行,原因在於括號內被編碼的部分不能被正常解釋,要么使用ASCII 數字,要么加'' 或’ ’ 使其變為字符串,作為字符串也只能作為普通字符。
1
scriptalert('13\u0027)/script
被編碼處為'。該例的JS 不會執行,因為控製字符被編碼了,解碼後的' 將變為字符串的一部分,而不再解釋為控製字符。因此該例中字符串是不完整的,因為沒有' 來結束字符串。
1
scriptalert('14\u000a')/script
該例的JS 會被執行,因為被編碼的部分處於字符串內,只會被解釋為普通字符,不會突破字符串上下文。
5.2.5 URL 解析
URL 解析器也被建模為狀態機,文檔輸入流中的字符可以將其導向不同的狀態。首先,要注意的是URL 的協議部分必須為ASCII 字符,即不能被任何編碼,否則URL 解析器的狀態機將進入No Scheme 狀態。
示例
1
a href='%6a%61%76%61%73%63%72%69%70%74:%61%6c%65%72%74%28%31%29'/a
URL 編碼部分的是javascript:alert(1)。 JS 不會被執行,因為作為Scheme 部分的javascript 這個字符串被編碼,導致URL 解析器狀態機進入No Scheme 狀態。
URL中的: 也不能被以任何方式編碼,否則URL 解析器的狀態機也將進入No Scheme 狀態。
示例
1
a href='javascript%3aalert(3)'/a
由於: 被URL編碼為%3a,導致URL 狀態機進入No Scheme 狀態, JS 代碼不能執行。
示例
1
a href='#x6a;#x61;#x76;#x61;#x73;#x63;#x72;#x69;#x70;#x74;%61%6c%65%72%74%28%32%29'
javascript 這個字符串被實體化編碼, 沒有被編碼,alert(2) 被URL 編碼。可以成功執行。
首先,在HTML 解析器中,HTML 狀態機處於屬性值狀態(Attribute Value State)時,字符實體時會被解碼的,此處在href 屬性中,所以被實體化編碼的javascript 字符串會被解碼。
其次,HTML 解析是在URL 解析之前的,所以在進行URL 解析之前,Scheme 部分的javascript 字符串已被解碼,而並不再是被實體編碼的狀態。
5.2.6 解析顺序
首先瀏覽器接收到一個HTML 文檔時,會觸發HTML 解析器對HTML 文檔進行詞法解析,這一過程完成HTML 解碼並創建DOM 樹。接下來JavaScript 解析器會介入對內聯腳本進行解析,這一過程完成JS 的解碼工作。
如果瀏覽器遇到需要URL 的上下文環境,這時URL 解析器也會介入完成URL 的解碼工作,URL 解析器的解碼順序會根據URL 所在位置不同,可能在JavaScript 解析器之前或之後解析。 HTML 解析總是第一步。
URL 解析和JavaScript 解析,它們的解析順序要根據情況而定。
示例
1
a href='UserInput'/a
該例子中,首先由HTML 解析器對UserInput 部分進行字符實體解碼;
接著URL 解析器對UserInput 進行URL decode;如果URL 的Scheme 部分為javascript 的話,JavaScript 解析器會再對UserInput 進行解碼。所以解析順序是:HTML 解析-URL 解析-JavaScript 解析。
示例
1
a href=# onclick='window.open('UserInput')'/a
該例子中,首先由HTML 解析器對UserInput 部分進行字符實體解碼;
接著由JavaScript 解析器會再對onclick 部分的JS 進行解析並執行JS;
執行JS 後window.open(‘UserInput’) 函數的參數會傳入URL,所以再由URL 解析器對UserInput 部分進行解碼。
因此解析順序為:HTML 解析