程式碼分析引擎 CodeQL 初體驗
作者:w7ay@知道創宇404實驗室
日期:2019年11月18日
原文連結:
QL是一種查詢語言,支援對C++,C#,Java,JavaScript,Python,go等多種語言進行分析,可用於分析程式碼,查詢程式碼中控制流等資訊。
之前筆者有簡單的研究透過JavaScript語義分析來查詢XSS,所以對於這款引擎有濃厚的研究興趣 。
安裝
1.下載分析程式:
分析程式支援主流的作業系統,Windows,Mac,Linux
2.下載相關庫檔案:
庫檔案是開源的,我們要做的是根據這些庫檔案來編寫QL指令碼。
3.下載最新版的VScode,安裝CodeQL擴充套件程式:
-
用vscode的擴充套件可以方便我們看程式碼
-
然後到擴充套件中心配置相關引數
4.
- cli填寫下載的分析程式路徑就行了,windows可以填寫codeql.cmd
- 其他地方預設就行
建立資料庫
以JavaScript為例,建立分析資料庫,建立資料庫其實就是用分析程式來分析原始碼。到要分析原始碼的根目錄,執行
codeql database create jstest --language=javascript
接下來會在該目錄下生成一個
jstest
的資料夾,就是資料庫的資料夾了。
接著用vscode開啟之前下載的ql庫檔案,在ql選擇夾中新增剛才的資料庫檔案,並設定為當前資料庫。
接著在QL/javascript/ql/src目錄下新建一個test.ql,用來編寫我們的ql指令碼。為什麼要在這個目錄下建立檔案呢,因為在其他地方測試的時候
import javascript
匯入不進來,在這個目錄下,有個
javascript.qll
就是基礎類庫,就可以直接引入
import javascript
,當然可能也有其他的方法。
看它的庫檔案,它基本把JavaScript中用到的庫,或者其他語言的定義語法都支援了。
輸出一段hello world試試?
語義分析查詢的原理
剛開始接觸ql語法的時候可能會感到它的語法有些奇怪,它為什麼要這樣設計?我先說說自己之前研究基於JavaScript語義分析查詢dom-xss是怎樣做的。
首先一段類似這樣的javascript程式碼
var param = location.hash.split("#")[1];document.write("Hello " + param + "!");
常規的思路是,我們先找到
document.write
函式,由這個函式的第一個引數回溯尋找,如果發現它最後是
location.hash.split("#")[1];
,就尋找成功了。我們可以稱
document.write
為
sink
,稱
location.hash.split
為
source
。基於語義分析就是由sink找到source的過程(當然反過來找也是可以的)。
而基於這個目標,就需要我們設計一款理解程式碼上下文的工具,傳統的正則搜尋已經無法完成了。
第一步要將JavaScript的程式碼轉換為語法樹,透過
pyjsparser
可以進行轉換
from pyjsparser import parseimport json html = ''' var param = location.hash.split("#")[1];document.write("Hello " + param + "!"); ''' js_ast = parse(html)print(json.dumps(js_ast)) # 它輸出的是python的dict格式,我們用轉換為json方便檢視
最終就得到了如下一個樹結構
這些樹結構的一些定義可以參考:
大概意思可以這樣理解:變數
param
是一個
Identifier
型別,它的初始化定義的是一個
MemberExpression
表示式,該表示式其實也是一個
CallExpression
表示式,
CallExpression
表示式的引數是一個
Literal
型別,而它具體的定義又是一個
MemberExpression
表示式。
第二步,我們需要設計一個遞迴來找到每個表示式,每一個
Identifier
,每個
Literal
型別等等。我們要將之前的
document.write
轉換為語法樹的形式
{ "type":"MemberExpression", "object":{ "type":"Identifier", "name":"document" }, "property":{ "type":"Identifier", "name":"write" } }
location.hash
也是同理
{ "type":"MemberExpression", "object":{ "type":"Identifier", "name":"location" }, "property":{ "type":"Identifier", "name":"hash" } }
在找到了這些
sink
或
source
後,再進行正向或反向的回溯分析。回溯分析也會遇到不少問題,如何處理物件的傳遞,引數的傳遞等等很多問題。之前也基於這些設計寫了一個線上基於語義分析的
QL語法
QL語法雖然隱藏了語法樹的細節,但其實它提供了很多類似
類
,
函式
的概念來幫助我們查詢相關'語法'。
依舊是這段程式碼為例子
var param = location.hash.split("#")[1];document.write("Hello " + param + "!");
上文我們已經建立好了查詢的資料庫,現在我們分別來看如何查詢sink,source,以及怎樣將它們關聯起來。
我也是看它的文件: 學習的,它提供了很多方便的函式,我沒有仔細看。我的查詢語句都是基於語法樹的查詢思想,可能官方已經給出了更好的查詢方式,所以看看就行了,反正也能用。
查詢 document.write
import javascript from Expr dollarArg,CallExpr dollarCall where dollarCall.getCalleeName() = "write" and dollarCall.getReceiver().toString() = "document" and dollarArg = dollarCall.getArgument(0) select dollarArg
這段語句的意思是查詢document.write,並輸出它的第一個引數
查詢 location.hash.split
import javascript from CallExpr dollarCall where dollarCall.getCalleeName() = "split" and dollarCall.getReceiver().toString() = "location.hash" select dollarCall
查詢location.hash.split並輸出
資料流分析
接著從
sink
來找到
source
,將上面語句組合下,按照官方的文件來就行
class XSSTracker extends TaintTracking::Configuration { XSSTracker() { // unique identifier for this configuration this = "XSSTracker" } override predicate isSource(DataFlow::Node nd) { exists(CallExpr dollarCall | nd.asExpr() instanceof CallExpr and dollarCall.getCalleeName() = "split" and dollarCall.getReceiver().toString() = "location.hash" and nd.asExpr() = dollarCall ) } override predicate isSink(DataFlow::Node nd) { exists(CallExpr dollarCall | dollarCall.getCalleeName() = "write" and dollarCall.getReceiver().toString() = "document" and nd.asExpr() = dollarCall.getArgument(0) ) } } from XSSTracker pt, DataFlow::Node source, DataFlow::Node sink where pt.hasFlow(source, sink) select source,sink
將source和sink輸出,就能找到它們具體的定義。
我們找到查詢到的樣本
可以發現它的回溯是會根據變數,函式的返回值一起走的。
當然從source到sink也不可能是一馬平川的,中間肯定也會有阻擋的條件,ql官方有給出解決方案。總之就是要求我們更加細化完善ql查詢程式碼。
接下來放出幾個查詢還不精確的樣本,大家可以自己嘗試如何進行查詢變得精確。
var custoom = location.hash.split("#")[1]; var param = ''; param = " custoom:" + custoom; param = param.replace('<',''); param = param.replace('"',''); document.write("Hello " + param + "!");
quora = { zebra: function (apple) { document.write(this.params); }, params:function(){ return location.hash.split('#')[1]; } }; quora.zebra();
最後
CodeQL將語法樹抽離出來,提供了一種用程式碼查詢程式碼的方案,更增強了基於資料分析的靈活度。唯一的遺憾是它並沒有提供很多查詢漏洞的規則,它讓我們自己寫。這也不由得讓我想起另一款強大的基於語義的程式碼審計工具fortify,它的規則庫是公開的,將這兩者結合一下說不定會有不一樣的火花。
Github公告說將用它來搜尋開源專案中的問題,而作為安全研究員的我們來說,也可以用它來做類似的事情?
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69912109/viewspace-2664799/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- html程式碼初體驗HTML
- 表分析初體驗
- Shell指令碼程式設計初體驗指令碼程式設計
- 小程式的初體驗
- 微信小程式初體驗微信小程式
- XD to Flutter 設計圖轉程式碼 初體驗Flutter
- 微信小程式元件初體驗微信小程式元件
- 小程式開發初體驗
- 攜程小程式初體驗
- 02.Vue初體驗程式碼(HelloVue和列表展示)Vue
- 1初學程式碼分析
- 微信小程式開發初體驗微信小程式
- 小程式雲開發初體驗
- 微信小程式初體驗--玩安卓小程式微信小程式安卓
- 【驗證碼逆向專欄】某驗“初代”滑塊驗證碼逆向分析
- SQL隱碼攻擊:Sqlmap初體驗SQL
- CodeQL的自動化程式碼審計之路(上篇)
- CodeQL的自動化程式碼審計之路(中篇)
- QuarkUI基於AntDesignPro的低程式碼引擎,歡迎大家體驗!UI
- Angular 初體驗Angular
- http初體驗HTTP
- AQS初體驗AQS
- golang 初體驗Golang
- OpenCV 初體驗OpenCV
- Nuxt 初體驗UX
- indexedDB 初體驗Index
- laravel初體驗Laravel
- Flutter初體驗Flutter
- jQuery初體驗jQuery
- ollama 初體驗
- Electron初體驗
- 實驗1 現代C++程式設計初體驗C++程式設計
- go modules 初體驗Go
- ReactNative初體驗React
- react hooks初體驗ReactHook
- Laravel Octane 初體驗Laravel
- gRPC初體驗RPC
- Mybatis初體驗(二)MyBatis