Codeql如何分析cookie未啟用httponly的問題

今天我們利用codeql分析下“cookie未啟用httponly“這類的安全問題,由此加深自己對(duì)codeql的使用。如果效果良好,可以考慮修復(fù)vulnerability-goapp的其他漏洞。

分析go程序時(shí)必須額外下載codeql-go

說明

審計(jì)對(duì)象

Vulnerability-goapp:Vulnerable golang Web application for education。

修改

由于在該項(xiàng)目中所有的 Cookie 都沒有設(shè)置 http-only 屬性,因此我們需要先對(duì)其進(jìn)行修改才能進(jìn)行比較。以下是對(duì)原句的重寫: 記錄修改:在某些cookie設(shè)置中增加http-only選項(xiàng)。

pkgadminadmin.go修改如下。
Codeql如何分析cookie未啟用httponly的問題

pkgloginlogin.go修改如下。
Codeql如何分析cookie未啟用httponly的問題

pkgregisterregister.go修改如下。
Codeql如何分析cookie未啟用httponly的問題

修改后記得重新生成一次database(如果需要覆蓋舊的DATabase的話,則需要先刪除舊的再生成新的。

目的

就是通過codeql腳本來發(fā)現(xiàn)其中未設(shè)置httponly和設(shè)置了httponly的但httponly的值為false(一般不會(huì)這樣,但保不齊有)的這樣存在漏洞的點(diǎn)。

確定Source和Sink

Sink定義

Sink很簡(jiǎn)單,設(shè)置Cookie時(shí),需要用到http.SetCookie方法,而需要設(shè)置的Cookie值是這個(gè)函數(shù)的第二個(gè)參數(shù),然后我們可以寫出找到類似這樣Sink的查詢語句。

import go from DataFlow::Node sink where exists(DataFlow::CallNode c |       c.getTarget().hasQualifiedName("net/http", "SetCookie") and c.getArgument(1) = sink     ) select sink

運(yùn)行后可獲得以下結(jié)果,點(diǎn)擊任意條目都會(huì)跳轉(zhuǎn)到復(fù)合要求的代碼段下。
Codeql如何分析cookie未啟用httponly的問題

我們將其轉(zhuǎn)換成一個(gè)Sink類,如下。

private class Sink extends DataFlow::Node {   Sink() {     exists(DataFlow::CallNode c |       c.getTarget().hasQualifiedName("net/http", "SetCookie") and c.getArgument(1) = this     )   } }

這樣之后我們通過將一個(gè)變量定義成Sink的話,就是指符合條件的所有代碼片段,例如:

import go  private class Sink extends DataFlow::Node {   Sink() {     exists(DataFlow::CallNode c |       c.getTarget().hasQualifiedName("net/http", "SetCookie") and c.getArgument(1) = this     )   } }  from Sink s select s

運(yùn)行后會(huì)獲得同樣的結(jié)果。

Source定義

然后我們?cè)賮泶_定Source,從http.SetCookie方法接收的參數(shù)來看,實(shí)際第二個(gè)參數(shù)是接收一個(gè)Cookie的結(jié)構(gòu)體的指針。
Codeql如何分析cookie未啟用httponly的問題Codeql如何分析cookie未啟用httponly的問題

所以我們先要找到這樣一個(gè)結(jié)構(gòu)體,我們可以先把項(xiàng)目中所有的結(jié)構(gòu)體列出來。

codeql-go中關(guān)于結(jié)構(gòu)體的定義如下。
Codeql如何分析cookie未啟用httponly的問題

所以我們的查詢腳本例如。

import go  from StructLit source select source

也如我們預(yù)期的一樣列出了所有的結(jié)構(gòu)體。
Codeql如何分析cookie未啟用httponly的問題

然后接下來就是剔除其他不相干的內(nèi)容,對(duì)類型做限制。

關(guān)于hasQualifiedName方法,在各種Codeql-go中的各種類型都有相同的方法,定義如下,標(biāo)記對(duì)象的是在屬于哪個(gè)包,叫什么名。
Codeql如何分析cookie未啟用httponly的問題

如果不確定的話,可以通過,getPackage和getName打印相關(guān)字段,例如。

import go  from StructLit source // where source.getType().hasQualifiedName("net/http", "Cookie") select source.getType().getPackage(), source.getType().getName()

結(jié)果如下。
Codeql如何分析cookie未啟用httponly的問題

我們可以找到source定義,例如。

import go  from StructLit source where source.getType().hasQualifiedName("net/http", "Cookie") select source

Codeql如何分析cookie未啟用httponly的問題同樣轉(zhuǎn)換成DataFlow::Node的子類。

private class Source extends DataFlow::Node {   Source() {     exists(StructLit s | s.getType().hasQualifiedName("net/http", "Cookie") and this.asExpr() = s)   } }

TaintConfig定義

簡(jiǎn)單的數(shù)據(jù)流

有了Source和Sink,簡(jiǎn)單定義TaintConfig,就能獲得所有從Source到Sink的數(shù)據(jù)流。

import go  private class Source extends DataFlow::Node {   Source() {     exists(StructLit s | s.getType().hasQualifiedName("net/http", "Cookie") and this.asExpr() = s)   } }  private class Sink extends DataFlow::Node {   Sink() {     exists(DataFlow::CallNode c |       c.getTarget().hasQualifiedName("net/http", "SetCookie") and c.getArgument(1) = this     )   } }  class Configuration extends TaintTracking::Configuration {   Configuration() { this = "HttpOnly" }    override predicate isSource(DataFlow::Node source) { source instanceof Source }    override predicate isSink(DataFlow::Node sink) { sink instanceof Sink } }  from Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink where cfg.hasFlowPath(source, sink) select source, sink

結(jié)果如下:
Codeql如何分析cookie未啟用httponly的問題

剔除

然而,我們?nèi)晕匆瞥O(shè)置了httponly=true的部分。因此需要加入限定條件,即將設(shè)有HttpOnly屬性為true的數(shù)據(jù)流從結(jié)果中排除。

我們可以 CodeQL 提供的 TaintTracking::isSanitizer,來過濾無害節(jié)點(diǎn):

override predicate isSanitizer(DataFlow::Node node) {     exists(Write w, Field f, DataFlow::Node rhs |       f.hasQualifiedName("net/http", "Cookie", "HttpOnly") and       w.writesField(node, f, rhs) and       rhs.getBoolValue() = true     )   }

運(yùn)行結(jié)果如下,但有一處地方需要注意。
Codeql如何分析cookie未啟用httponly的問題紅框中實(shí)際有對(duì)HttpOnly進(jìn)行設(shè)置,但我們的腳本并不能識(shí)別這樣的一個(gè)數(shù)據(jù)流。后面試了各種方法,最終找到一種解決方式,將isSanitizer修改成以下內(nèi)容。

override predicate isSanitizer(DataFlow::Node node) {     exists(Write w, Field f, DataFlow::Node n, DataFlow::Node rhs |       f.hasQualifiedName("net/http", "Cookie", "HttpOnly") and       w.writesField(n, f, rhs) and       rhs.getBoolValue() = true and       node = n.getAPredecessor*()n     )   }

其中node=n.getAPredecessor*()是說node是n的前置數(shù)據(jù)流節(jié)點(diǎn),數(shù)據(jù)可以在0個(gè)或多個(gè)步驟中從node流到n。

最終腳本

加上一些信息,模仿官方的示例,最終腳本如下。

/**  * @name Cookie未設(shè)置httponly  * @description Cookies包含一個(gè)HTTPOnly的設(shè)置選項(xiàng),可以使此cookie不能被js讀取,而只能用于HTTP請(qǐng)求。  * @kind path-problem  * @problem.severity error  * @precision low  * @id go/Cookie-not-set-httponly  * @tags security  */  import go import DataFlow::PathGraph  private class Source extends DataFlow::Node {   Source() {     exists(StructLit s | s.getType().hasQualifiedName("net/http", "Cookie") and this.asExpr() = s)   } }  private class Sink extends DataFlow::Node {   Sink() {     exists(DataFlow::CallNode c |       c.getTarget().hasQualifiedName("net/http", "SetCookie") and c.getArgument(1) = this     )   } }  class Configuration extends TaintTracking::Configuration {   Configuration() { this = "HttpOnly" }    override predicate isSource(DataFlow::Node source) { source instanceof Source }    override predicate isSink(DataFlow::Node sink) { sink instanceof Sink }    override predicate isSanitizer(DataFlow::Node node) {     exists(Write w, Field f, DataFlow::Node n, DataFlow::Node rhs |       f.hasQualifiedName("net/http", "Cookie", "HttpOnly") and       w.writesField(n, f, rhs) and       rhs.getBoolValue() = true and       node = n.getAPredecessor*()     )   } }  from Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink where cfg.hasFlowPath(source, sink) select sink.getNode(), source, sink, "Cookie-not-set-httponly in $@.", source.getNode(), "here"

最終篩選出存在問題的內(nèi)容。
Codeql如何分析cookie未啟用httponly的問題

? 版權(quán)聲明
THE END
喜歡就支持一下吧
點(diǎn)贊5 分享