XSS語義分析的階段性總結(一)

本文作者:Kale

 

前言


 

由於X3Scan的研發已經有些進展了,所以對這一階段的工作做一下總結!對於X3Scan的定位,我更加傾向於主動+被動的結合。主動的方面主要體現在可以主動抓取頁面鏈接並發起請求,並且後期可能參考XSStrike加入主動fuzz的功能,這個目前還未加入,正在糾結中。而被動的方面,主要的工作就是xss語義分析的研究,通過xss語義分析而不是盲目的使用payload進行fuzz。

 

語義分析


 

業內提的比較早的一款waf產品,語義分析說白了就是根據上下文來進行分析,而不是通過正則搜索的方式來匹配污染源,也就是我們的漏洞觸發點。由於這個需求,我們需要開發一款可以理解上下文的工具。來幫助我們識別我們的payload是輸出在什麼樣的語義環境,從而給出精確的payload,而這一點xray目前做的效果挺不錯的。

 

AST語法樹


 

在此之前我們先簡單了解一下JS抽象語法樹。

Javascript 程式碼的解析(Parse )步驟分為兩個階段:詞法分析(Lexical Analysis)和 語法分析(Syntactic Analysis)。這個步驟接收程式碼並輸出抽象語法樹,亦稱 AST

在分析 Javascript 的 AST 過程中,藉助於工具 AST Explorer 能幫助我們對 AST 節點有一個更好的感性認識。

下面是AST Explorer對 Javascript程式碼的解析,經過AST Explorer的解析Javascript程式碼會被抽象成AST的形式。

1.png

下面簡單介紹幾個節點類型,更多的參考官方文檔定義//esprima.readthedocs.io/en/3.1/syntax-tree-format.html

使用下面的demo為例

var param = location.hash.split(“#”)[1];document.write(“Hello ” + param + “!”);

 

VariableDeclaration


 

變數聲明,kind 屬性表示是什麼類型的聲明,因為 ES6 引入了 const/letdeclarations 表示聲明的多個描述,因為我們可以這樣:let a = 1, b = 2;

2.png

 

VariableDeclarator

變數聲明的描述,id 表示變數名稱節點,init 表示初始值的表達式,可以為 null

3.png

 

Identifier

標識符,就是我們寫 JS 時自定義的名稱,如變數名,函數名,屬性名,都歸為標識符

4.png

一個標識符可能是一個表達式,或者是解構的模式(ES6 中的解構語法)。

 

Literal

字面量,就代表了一個值的字面量,如 「hello」1 這些,還有正則表達式(有一個擴展的 Node 來表示正則表達式),如 /\d?/

5.png

6.png

value 這裡即對應了字面量的值,我們可以看出字面量值的類型,字元串,布爾,數值,null 和正則。

 

BinaryExpression

由於這裡存在兩個個二元運算,所以簡單再介紹其中一個,其它的便不多簡紹。

二元運算表達式節點,left 和 right 表示運算符左右的兩個表達式,operator 表示一個二元運算符。

7.png

這裡進行運算的一個是Literal類型也就是hello,一個是Identifier類型也就是param變數,運算符為+

AST的介紹先到這裡。下面介紹一下檢測的原理

 

檢測原理


 

xss漏洞一般有兩種檢測方法,第一種是簡單粗暴的使用收集來的payload進行fuzz,通過頁面是否回顯來判斷是否存在漏洞,這種手段目前已經不適用了。另一種就是通過對返回頁面進行解析,結合語義分析,根據輸出在不同的上下文來選擇發送我們的payload,這樣的話,我們的payload即精巧又準確。

還是使用這個demo

var param = location.hash.split(“#”)[1];document.write(“Hello ” + param + “!”);

檢測思路一般為,我們首先找到document.write這個函數,從而定位到param,由param我們可以進行回溯到location.hash.split("#")[1],從而證明觸發點是可控的。在污點分析模型裡面,我們稱document.write為sink,也就是污點匯聚點,代表直接產生安全敏感操作(違反數據完整性)或者泄露隱私數據到外界(違反數據保密性),稱location.hash.split("#")[1]source,也就是污點源,代表直接引入不受信任的數據或者機密數據到系統中。很多程式碼審計工具也是基於了這樣的模型。

基於上面的分析,我們需要開發一個可以理解js上下文的工具,幫助我們找到sinksource,讓我們可以由sink回溯source,或者由反過來亦可,正則上實現這個問題已經基本不可能了,我們需要能夠給上下文賦予準確意義。

而上面的AST語法樹可以滿足我們的需求,因為它可以幫助我們分析xss的輸出點的上下文

幸運的是python裡面有將js程式碼解析為語法樹的庫pyjsparser,還有在其基礎上實現的js2py

from pyjsparser import parseimport jsonjs = ”’    var param = location.hash.split(“#”)[1];document.write(“Hello ” + param + “!”);    ”’ast = parse(js)print(json.dumps((ast)))

解析出來的效果跟AST Explorer是一致的

8.png

接下來我們需要設計一個遞歸來找到每個表達式,每一個IdentifierLiteral類型等等。

部分程式碼如下:

9.png

然後再遍歷body的節點,找尋輸出位置

0.png

仍是上面的demo,我們嘗試找到Hello

11.png

12.png

輸出結果如下:

13.png

我們找到了Hello,並且輸出位置的上下文為Literal

有了上面的研究,通過sink回溯source的方法便可以實現,對於dom型xss的分析,也會更加精確,對於反射型xss輸出在js的情況,同樣適用

如果回顯在JS腳本中,發送測試payload後,通過js語法樹解析確定IdentifierLiteral這兩個類型中是否包含,如果payload是Identifier類型,就可以直接判斷存在xss,如果payload是Literal類型,再通過單雙引號來測試是否可以閉合。

 

最後


 

關於js語義分析暫時先分析到這裡,難點還是dom型xss的檢測,因為dom xss檢測識別有點複雜,下一篇會探討一下sink輸出在html的情況,探討一下html解析的一些問題。