<menu id="guoca"></menu>
<nav id="guoca"></nav><xmp id="guoca">
  • <xmp id="guoca">
  • <nav id="guoca"><code id="guoca"></code></nav>
  • <nav id="guoca"><code id="guoca"></code></nav>

    CodeQL上手筆記

    VSole2021-11-18 14:54:52

    前言

    在挖了一段時間的漏洞后,逐漸感覺挖洞變成了一個體力活,雖然也使用正則匹配的方式減少了部分工作量,但這種方式還是有很大的缺陷,準確率比較低,因此希望找到一種新的方式來輔助挖洞,最近CodeQL比較火,很多師傅也寫了相應的文章,相對來說學習成本已經算比較低了。盡管看了很多師傅的文章,但感覺上自己對原理或者語法的學習還是比較遲鈍,因此打算去分析師傅們已經寫好的一些query語法,輔助理解。

    java-sec-code

    第一個Demo來自文章Codeql 入門,師傅以java-sec-code項目為例編寫了多個query語句。

    查詢所有內容為空的方法

    import java
    from Method m, BlockStmt block
    where
      block = m.getBody() and
      block.getNumStmt() = 0
    select m
    

    from語句為變量定義,where語句相當于數據庫查詢中的搜索條件的限制語句,select為查詢語句。在QL中,方法稱作謂詞

    Method類型是方法類,表示獲取當前項目中所有的方法。getBody謂詞返回body體,BlockStmt代表一個語句塊。getNumStmt謂詞獲取塊child statements的數量。關于BlockStmt這部分應該是和AST有一些關系。

    Local Data Flow分析SPEL

    import java
    import semmle.code.java.frameworks.spring.SpringController
    import semmle.code.java.dataflow.TaintTracking
    from Call call,Callable parseExpression,SpringRequestMappingMethod route
    where
        call.getCallee() = parseExpression and 
        parseExpression.getDeclaringType().hasQualifiedName("org.springframework.expression", "ExpressionParser") and
        parseExpression.hasName("parseExpression") and 
       TaintTracking::localTaint(DataFlow::parameterNode(route.getARequestParameter()),DataFlow::exprNode(call.getArgument(0))) 
    select route.getARequestParameter(),call
    

    本地數據流

    本地數據流是單個方法可調用對象中的數據流。本地數據流通常比全局數據流更容易、更快、更精確。

    本地數據流庫位于模塊 DataFlow 中,該模塊定義了表示數據可以流經的任何元素的類。Node節點分為表達式節點(ExprNode)和參數節點(ParameterNode)。您可以使用成員謂詞 asExpr 和 asParameter在數據流節點和表達式/參數之間進行映射或使用謂詞 exprNode 和 parameterNode。

    如果存在從節點nodeFrom到節點nodeTo的即時數據流邊,則謂詞localFlowStep(Node nodeFrom, Node nodeTo)成立。您可以通過使用+和運算符*,或者通過使用定義的遞歸謂詞localFlow(相當于localFlowStep)來遞歸應用該謂詞。

    例如,可以在零個或多個本地步驟中找到從參數源到表達式接收器的污點傳

    播:DataFlow::localFlow(DataFlow::parameterNode(source), DataFlow::exprNode(sink))

    本地污點跟蹤

    本地污點跟蹤通過包括非保留值步驟來擴展本地數據流。例如:

    String temp = x;
    String y = temp + ", " + temp;
    

    如果x是一個污點字符串,那么y也是污點。

    本地污染跟蹤庫位于TaintTracking模塊中。像本地數據流一樣,如果從nodeFrom

    節點到nodeTo節點之間存在直接的污點傳播邊線,則謂詞localTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo)成立。您可以使用+和``*運算符,也可以使用預定義的遞歸謂詞localTaint(等效于localTaintStep*)來遞歸地應用謂詞。

    所以我們再來看下面的代碼,是不是就可以理解了。即使用本地污點跟蹤的方式查詢從參數節點route.getARequestParameter()到表達式節點call.getArgument(0)的數據流是否成立。

    TaintTracking::localTaint(DataFlow::parameterNode(route.getARequestParameter()),DataFlow::exprNode(call.getArgument(0)))
    

    Call和Callable

    Callable表示可調用的方法或構造器的集合。

    Call表示調用Callable的這個過程(方法調用,構造器調用等等)

    那么call.getCallee() = parseExpression就代表獲取方法調用為parseExpression

    再通過下面的語句對parseExpression進行限制,也就是org.springframework.expression包下的ExpressionParser類的parseExpression方法,我們可以記住這個語句,用到的時候直接套也可以。

    parseExpression.getDeclaringType().hasQualifiedName("org.springframework.expression", "ExpressionParser") and
        parseExpression.hasName("parseExpression")
    

    SpringRequestMappingMethod

    SpringRequestMappingMethod可以獲取所有的Spring Controller的方法。

    getARequestParameter 獲取請求的參數

    getArgument 獲取方法調用時的參數

    所以再來看下面的代碼,意思就是獲取所有RequestMapping方法的參數到調用parseExpression方法第一個參數的數據流。

    TaintTracking::localTaint(DataFlow::parameterNode(route.getARequestParameter()),DataFlow::exprNode(call.getArgument(0)))
    

    照貓畫虎

    理解了上面代碼的意思,我們完全可以照貓畫虎,追蹤所有Controller中的命令執行。

    import java
    import semmle.code.java.frameworks.spring.SpringController
    import semmle.code.java.dataflow.TaintTracking
    from Call call,Callable parseExpression,SpringRequestMappingMethod route
    where
        call.getCallee() = parseExpression and 
        parseExpression.getDeclaringType().hasQualifiedName("java.lang", "Runtime") and
        parseExpression.hasName("exec") and 
       TaintTracking::localTaint(DataFlow::parameterNode(route.getARequestParameter()),DataFlow::exprNode(call.getArgument(0))) 
    select route.getARequestParameter(),call
    

    全局數據流

    本地數據流雖然分析效率比較高,但是會存在一些遺漏,舉個栗子。我想分析SSRF漏洞,假如我找到的Sink是new URL("xx"),但是在下面的Controller中并沒有直接調用,而是調用了HttpUtils.URLConnection(url);。而在URLConnection創建了URL對象,那么我使用本地數據流分析是分析不到的,因為他只能在單個方法中分析,跨方法的調用就不行了,這個時候就需要全局數據流。

    可以通過繼承類DataFlow::Configuration來使用全局數據流庫。如下所示:

    import semmle.code.java.dataflow.DataFlow
    class MyDataFlowConfiguration extends DataFlow::Configuration {
      MyDataFlowConfiguration() { this = "MyDataFlowConfiguration" }
      override predicate isSource(DataFlow::Node source) {
        ...
      }
      override predicate isSink(DataFlow::Node sink) {
        ...
      }
    }
    

    可能對QL中的Class比較陌生,Class簡單介紹如下。

    所以上例中的isSourceisSink都是父類DataFlow::Configuration的非私有謂詞。predicate代表當前的謂詞沒有返回值。下面是關于DataFlow::Configuration謂詞的介紹。

    isSource-定義數據可能來源

    isSink-定義數據可能流向的位置

    isBarrier—可選,限制數據流

    isAdditionalFlowStep—可選,添加額外的數據流步驟

    這里的Source代表輸入點,Sink代表執行點,isAdditionalFlowStep它的作用是將一個可控節點A強制傳遞給另外一個節點B,那么節點B也就成了可控節點。

    全局污點追蹤

    全局污點跟蹤是針對全局數據流而言,就像本地污點跟蹤是針對本地數據流一樣。也就是說,全局污點跟蹤通過額外的non-value-preserving步驟擴展了全局數據流。我們可以通過擴展類TaintTracking::Configuration來使用全局污點跟蹤庫:

    import semmle.code.java.dataflow.TaintTracking
    class MyTaintTrackingConfiguration extends TaintTracking::Configuration {
      MyTaintTrackingConfiguration() { this = "MyTaintTrackingConfiguration" }
      override predicate isSource(DataFlow::Node source) {
        ...
      }
      override predicate isSink(DataFlow::Node sink) {
        ...
      }
    }
    

    isSource-定義污點的可能來源

    isSink-定義污點可能流向的位置

    isSanitizer—可選,限制污點流

    isAdditionalTaintStep—可選,添加額外污點步驟

    這里解釋下isSanitizer也就是凈化函數,代表污點傳播到這里就會被阻斷。

    下面我們用全局污點追蹤分析SSRF漏洞,就可以分析到HttpUtils.URLConnection中的URL請求了。

    import semmle.code.java.dataflow.DataFlow
    import semmle.code.java.frameworks.spring.SpringController
    import semmle.code.java.dataflow.TaintTracking
    class Configuration extends DataFlow::Configuration {
      Configuration() {
        this = "Configer"
      }
      override predicate isSource(DataFlow::Node source) {
        exists( SpringRequestMappingMethod route| source.asParameter()=route.getARequestParameter() )
      }
      override predicate isSink(DataFlow::Node sink) {
        exists(Call call ,Callable parseExpression|
          sink.asExpr() = call.getArgument(0) and
          call.getCallee()=parseExpression and 
       parseExpression.getDeclaringType().hasQualifiedName("org.springframework.expression", "ExpressionParser") and
          parseExpression.hasName("parseExpression")
        )
      }
    }
    from  DataFlow::Node src, DataFlow::Node sink, Configuration config
    where config.hasFlow(src, sink)
    select src,sink
    

    這里有必要講下exists語句

    它根據內部的子查詢返回true or false,來決定篩選出哪些數據。格式為exists(Obj obj| somthing)

    Shiro反序列化漏洞

    之前有師傅也講了如何使用CodeQL分析Shiro的反序列化漏洞,我們也學習一下思路,首先根據之前我們對Shiro反序列化漏洞的了解,這個洞還是稍微有點復雜的,所以肯定是要使用全局污點追蹤的方法分析的。

    數據庫構建

    首先從github下載shiro的源碼并且切換到1.2.4版本。

    git clone https://github.com/apache/shiro.git
    cd shiro
    git checkout 9549384
    

    構建數據庫

    CodeQL database create shiro1.2.4 --language=java --overwrite --command="mvn clean install -Dmaven.test.skip=true-Dmaven.test.skip=true"
    

    直接構建會有一個錯誤。

    經過查找資料,主要是aspectj依賴包的問題。

    aspectjweaver 1.8.9之前的版本不支持JDK1.8, aspectjweaver 1.8.9是在使用JDK1.8時的最低版本。

    所以對于此有兩種方法進行解決:
    
       一:降低JDK的版本,如果aspectjweaver的版本是1.8.9之前的,那么可以使用JDK1.7
    
       二:升級aspectjweaver的版本, 如果aspectjweaver的版本是1.8.9之前的,那么可以使用1.8.9來解決這個問題。
    

    雖然說是這么說,但是我改了pom.xml里的版本后并沒有解決問題,切換JDK版本為1.7可以順利構建成功。

    導入數據庫后就可以通過下面的查詢語句分析啦

    代碼分析

    import java
    import semmle.code.java.dataflow.FlowSources
    import DataFlow::PathGraph
    predicate isCookiegetValue(Expr expSrc, Expr expDest) {
    exists(Method method, MethodAccess call|
    expSrc.getType().toString()="Cookie" and
    expDest=call and
    call.getMethod() = method and
    method.hasName("getValue") and
    method.getDeclaringType().toString() = "Cookie"
    )
    }
    predicate isReadObject(Expr expSrc, Expr expDest) {
    exists(Method method, MethodAccess call|
    expSrc.getType().toString()="ObjectInputStream" and
    expDest=call and
    call.getMethod() = method and
    method.hasName("readObject") and
    method.getDeclaringType().toString() = "ObjectInputStream"
    )
    }
    predicate isBase64(Expr expSrc, Expr expDest) {
    exists(Method method, MethodAccess call|
    expSrc.getType().toString()="String" and
    expDest=call and
    call.getMethod() = method and
    method.hasName("decode") and
    method.getDeclaringType().toString() = "Base64"
    )
    }
    predicate isdecrypt(Expr expSrc, Expr expDest) {
    exists(Method method, MethodAccess call|
    expSrc.getType().toString()="byte" and
    expDest=call and
    call.getArgument(0)=expSrc and
    call.getMethod() = method and
    method.hasName("decrypt") and
    method.getDeclaringType().toString() = "CipherService"
    )
    }
    class VulConfig extends TaintTracking::Configuration {
    VulConfig() { this = "shiroConfig" }
    override predicate isSource(DataFlow::Node source) {
    exists(MethodAccess call |
    call.getMethod().getName()="getCookies" and
    source.asExpr()=call
    )
    }
    override predicate isSink(DataFlow::Node sink) {
    exists(MethodAccess call |
    call.getMethod().getName()="readObject" and
    sink.asExpr()=call
    )
    }
    override predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) {
    isCookiegetValue(node1.asExpr(), node2.asExpr()) or
    isReadObject(node1.asExpr(), node2.asExpr()) or
    isBase64(node1.asExpr(), node2.asExpr()) or
    isdecrypt(node1.asExpr(), node2.asExpr())
    }
    }
    from VulConfig config, DataFlow::PathNode source, DataFlow::PathNode sink
    where config.hasFlowPath(source, sink)
    select sink.getNode(), source, sink, "source are"
    

    但是還有一個坑,直接使用上述代碼是切換不到alert中查看污點傳播路徑的,需要在開始加上下面的內容。

    /**
     * @kind path-problem
     */
    

    上面的內容雖然可以出現結果,但是好像比較麻煩,我們可以直接將readValue的返回值當作Source,將readObject當作Sink。

    /**
     * @kind path-problem
     */
    import java
    import semmle.code.java.dataflow.FlowSources
    import DataFlow::PathGraph
    class VulConfig extends TaintTracking::Configuration {
        VulConfig() { this = "shiroConfig" }
    
        override predicate isSource(DataFlow::Node source) {
        exists(MethodAccess call |
        call.getMethod().getName()="readValue" and
        source.asExpr()=call
        )
        }
    
        override predicate isSink(DataFlow::Node sink) {
        exists(MethodAccess call |
        call.getMethod().getName()="readObject" and
        sink.asExpr()=call
        )
        }
        }
    
        from VulConfig config, DataFlow::PathNode source, DataFlow::PathNode sink
        where config.hasFlowPath(source, sink)
        select sink.getNode(), source, sink, "source are"
    

    如果將getCookie的返回值當作Source并不能獲取結果,所以我們要將getCookiereadValue鏈接起來。這里用到了isAdditionalTaintStep謂詞。

    predicate isCookiegetValue(Expr expSrc, Expr expDest) {
        exists(Method method, MethodAccess call|
        expSrc.getType().toString()="Cookie" and
        expDest=call and
        call.getMethod() = method and
        method.hasName("readValue") and
        method.getDeclaringType().toString() = "Cookie"
        )
        }
        override predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) {
            isCookiegetValue(node1.asExpr(), node2.asExpr()) 
            }
    

    主要分析下exists中的語句,

    • expSrc.getType().toString()="Cookie"主要是獲取第一個表達式的類型為Cookie的。

    • expDest=call第二個表達式為方法調用,后面是對調用的限制

    • call.getMethod() = method and method.hasName("readValue")限制調用的方法為readValue

    • method.getDeclaringType().toString() = "Cookie"限制方法類型為Cookie。也就是調用Cookie類里的readValue方法。

    上面代碼實現的功能是將所有返回類型為Cookie的表達式和readValue表達式連接起來。但是假如我只想把getCookie表達式和readValue表達式鏈接起來呢?

    再次照貓畫虎

    predicate isCookiegetValue(Expr expSrc, Expr expDest) {
        exists(Method method,Method methodsrc, MethodAccess call,MethodAccess callsrc|
            expSrc=callsrc and callsrc.getMethod() = methodsrc and methodsrc.hasName("getCookie")  and methodsrc.getDeclaringType().toString() = "CookieRememberMeManager" and
        expDest=call and
        call.getMethod() = method and
        method.hasName("readValue") and
        method.getDeclaringType().toString() = "Cookie"
        )
        }
    

    apache kylin命令執行漏洞

    這個項目比較大,我們就不自己構建數據庫了,可以從https://lgtm.com/projects/g/apache/kylin/直接下載現成的數據庫

    甚至可以直接在lgtm.com中直接編寫查詢語句。

    這個漏洞的Source是SpringMapping中獲取的,Sink是ProcessBuilder,編寫查詢語句也比較簡單

    /**
     * @kind path-problem
     */
    import semmle.code.java.frameworks.spring.SpringController
    import semmle.code.java.dataflow.TaintTracking
    import DataFlow::PathGraph
    import semmle.code.java.dataflow.FlowSources
    class Configuration extends TaintTracking::Configuration {
      Configuration() {
        this = "Configer"
      }
      override predicate isSource(DataFlow::Node source) {
        exists( SpringRequestMappingMethod route| source.asParameter()=route.getARequestParameter() )
      }
      override predicate isSink(DataFlow::Node sink) {
        exists(Call call ,Callable parseExpression|
            sink.asExpr() = call.getArgument(0) and
            call.getCallee()=parseExpression and 
         parseExpression.getDeclaringType().hasQualifiedName("java.lang", "ProcessBuilder") and
            parseExpression.hasName("ProcessBuilder")
          )
      }
    }
    from  DataFlow::PathNode src, DataFlow::PathNode sink, Configuration config
    where config.hasFlowPath(src, sink)
    select sink.getNode(), src, sink, "source are"
    

    除了上面的方式獲取source還可以使用下面的方式。

    復制override predicate isSource(DataFlow::Node source) {
        source instanceof RemoteFlowSource 
      }
    

    RemoteFlowSource類內置了主流的獲取參數的方式,因此也可以使用這種方式獲取source。

    總結

    通過上面的學習,已經可以編寫一些簡單的查詢語句分析了,CodeQL上手雖然并不復雜,而且比較高效,而且官方也對很多漏洞提供了相應的查詢語法,用來分析一些有源碼的項目還是可以的,但是似乎目前不能分析jar包里的代碼,目前也沒有比較好的解決方法。

    參考

    • CodeQL從0到1(內附Shiro檢測demo)

    • Codeql 入門
    • 淺談利用codeql進行java代碼審計分析(1)

    • CodeQL從入門到放棄
    cookienode
    本作品采用《CC 協議》,轉載必須注明作者和本文鏈接
    Netskope 的研究人員正在跟蹤一個使用惡意 Python 腳本竊取 Facebook 用戶憑據與瀏覽器數據的攻擊行動。攻擊針對 Facebook 企業賬戶,包含虛假 Facebook 消息并帶有惡意文件。攻擊的受害者主要集中在南歐與北美,以制造業和技術服務行業為主。
    Netskope 的研究人員正在跟蹤一個使用惡意 Python 腳本竊取 Facebook 用戶憑據與瀏覽器數據的攻擊行動。攻擊針對 Facebook 企業賬戶,包含虛假 Facebook 消息并帶有惡意文件。攻擊的受害者主要集中在南歐與北美,以制造業和技術服務行業為主。
    Ducktail 竊取惡意軟件背后的越南威脅行為者與 2023 年 3 月至 10 月初開展的一項新活動有關,該活動針對印度的營銷專業人士,旨在劫持 Facebook 企業帳戶。
    Bleeping Computer 網站披露,某黑客組織通過一個偽造和受損的 Facebook 賬戶網絡,發送數百萬條 Messenger 釣魚信息,利用密碼竊取惡意軟件攻擊 Facebook 企業賬戶。
    jsproxy功能特點服務端開銷低傳統在線代理幾乎都是在服務端替換 HTML/JS/CSS 等資源中的 URL。它能讓 JS 攔截網頁產生的請求,并能自定義返回內容,相當于在瀏覽器內部實現一個反向代理。API 虛擬化傳統在線代理大多只針對靜態 URL 的替換,忽視了動態 URL 以及和 URL 相關的網頁 API。這可能導致某些業務邏輯出現問題。同時得益于 nginx 豐富的功能,很多常用需求無需重新造輪子,通過簡單配置即可實現。并且無論性能還是穩定性,都遠高于自己實現。
    淺談DNS-rebinding
    2023-04-13 09:34:13
    在這種攻擊中,惡意網頁會導致訪問者運行客戶端腳本,攻擊網絡上其他地方的計算機。同源策略對Web應用程序具有特殊意義,因為Web應用程序廣泛依賴于HTTP cookie來維持用戶會話,所以必須將不相關網站嚴格分隔,以防止丟失數據泄露。值得注意的是同源策略僅適用于腳本,這意味著某網站可以通過相應的HTML標簽訪問不同來源網站上的圖像、CSS和動態加載 腳本等資源。即TTL的數值越小,修改記錄后所受的影響生效越快。等惡意腳本響應受害者。
    docker run -it -d -p 13443:3443 -p 8834:8834 leishianquan/awvs-nessus:v1. 如XSS,XSRF,sql注入,代碼執行,命令執行,越權訪問,目錄讀取,任意文件讀取,下載,文件包含,遠程命令執行,弱口令,上傳,編輯器漏洞,暴力破解等驗證碼與郵箱以及token的返回泄露,以及后臺為校驗從而可刪除的參數。從某個成功請求中捕獲數據包觀察cookie或者token是否存在規律或加密。token的key參數解密構建獲取真實user密鑰,可拼接、規律、時間戳……winodws桌面:TeamViewerQS單文件windows下載文件;certutil -urlcache -split -f?
    Security Joes在分析了來自去年9月份發生的一起事件的數據后,搶在這伙黑客攻陷目標之前成功應對了另外三起攻擊。研究人員已經發布了一份技術報告,描述了這伙威脅分子的作案手法及其后門的工作機理。YARA規則也已發布,以幫助組織檢測惡意軟件。
    在分析了 9 月份發生的一起事件的數據后,Security Joes 能夠在黑客攻破目標之前對其他三起攻擊做出響應。研究人員表示,他們能找到的 IceBreaker 威脅演員的唯一公開證據是10 月份來自 MalwareHunterTeam的一條推文。研究人員發布了一份技術報告,描述了威脅行為者的作案手法及其后門的工作原理。YARA 規則也已發布,以幫助組織檢測惡意軟件。此外,Security Joes 建議懷疑 IceBreaker 存在漏洞的公司查找在啟動文件夾中創建的快捷方式文件,并檢查開源工具 tsocks.exe 是否未經授權執行。
    一種新的信息竊取惡意軟件正在迅速蔓延,因為其背后的開發人員繼續添加功能并最近在 GitHub 上發布了源代碼。
    VSole
    網絡安全專家
      亚洲 欧美 自拍 唯美 另类