CodeQL的自動化程式碼審計之路(上篇)

盛邦安全發表於2022-11-14


0x01 前言


最近關於CodeQL的概念很火,大家普遍認為這會是下一代的程式碼審計神器。網上關於CodeQL的文章已經有不少,但是多數文章還是在分析CodeQL的安裝和簡單使用用例。真正使用CodeQL來進行自動化程式碼審計的文章較少,本文主要研究基於CodeQL實現全自動的程式碼審計工具實現思路預計文章分成三部分完成,目前是第一部分內容。


CodeQL(全稱Code Query Language),從其英文名稱中可以看出這是一種基於程式碼的查詢語言,其作用主要是透過編寫好的語句查詢程式碼中可能存在的安全隱患。學習CodeQL類似於學習一門全新的程式語言,語法類似於SQL,但是比傳統SQL還是要難很多。目前CodeQL支援對多種語言,包括java、javascript、go、python、C、Csharp等,但是很遺憾的是不支援“世界上最好的語言”PHP。這大概是因為PHP實在是太靈活了,函式名是字串變數這種呼叫方式確實很難從AST語法樹中靜態分析出問題,但這並不能阻礙我們學習CodeQL的興趣。文章所有內容基本上圍繞java語言展開,其他語言操作基本類似。


0x02 環境準備


網上關於CodeQL安裝的文章已經很多了,本來不打算再說這個事情,但是因為本人在CodeQL安裝過程中遇到不相容mac m1架構的情況,我想還有很多小夥伴也會遇到這個問題的,這裡主要以MAC的環境來說明安裝過程。


CodeQL的安裝主要分成引擎和SDK,新建一個目錄CodeQL(~/CodeQL/)來儲存後續所有的相關的工具和程式碼。


首先下載最新的引擎包,下載地址是:https://github.com/github/codeql-cli-binaries/releases


下載之後解壓把codeql資料夾放在剛才新建的資料夾CodeQL中,新增環境變數。

    vim ~/.profile
    export PATH=/Users/使用者名稱/CodeQL/codeql:${PATH}


    使用source命令是環境變數生效,然後命令列中執行codeql,如圖2.1所示。

    圖片

    圖2.1 CodeQL引擎安裝


    然後需要下載CodeQL對應的sdk包,下載地址是:

    https://github.com/Semmle/ql


    下載之後也需要把ql資料夾複製到~/CodeQL資料夾中。


    在CodeQL資料夾中新建databases資料夾,用於存放後續使用codeql生成的資料庫,那麼一切準備好了之後我們的CodeQL目錄之下就會是三個資料夾,如圖2.2所示。

    圖片

    圖2.2 CodeQL安裝


    後續我們就可以使用codeql database create命令來建立查詢資料庫,命令如下所示。

      codeql database create /Users/xxx/CodeQL/databases/project_db_name --language=java --source-root=/Users/xxx/cms/project_path --overwrite


      在windows環境中和以前的mac環境中確實沒有問題,但是如果是在m1的環境中會報錯,報錯資訊如圖2.3所示。錯誤的原因是codeql官方提供的工具是x86架構的,不能直接在arm中使用。

      圖片

      圖2.3 在MAC M1環境中codeql執行錯誤


      從官網中找到了codeql對m1的支援情況,如圖2.4所示。從圖中可以明確看出codeql確實是支援m1架構的,但是需要依賴rosetta2和xcode。但是並沒有給出具體的安裝和使用步驟,必須吐槽官方一點也不人性化,說話說一半。

      圖片

      圖2.4 CodeQL支援M1架構


      後來慢慢摸索著裝xcode和rosetta2,安裝xcode是直接透過appstore來裝的,安裝rosetta2是使用下面的命令。

        softwareupdate –install-rosetta


        安裝好了之後就可以使用下面的命令來生成資料庫,與傳統方式不同的是需要在命令前面增加arch -x86_64,如圖2.5所示。

          arch -x86_64 codeql database create /Users/xxx/CodeQL/databases/mvn_test --language=java --command='mvn clean install -DskipTests' --source-root=/Users/xxx/java/projects/mvn_test --overwrite


          圖片

          圖2.5 在M1中使用codeql生成資料庫



          0x03 語法基礎


          CodeQL是一門全新的語言,基礎的CodeQL語法網上已經有很多文章。大家在學習之前可以首先參考連結,瞭解關於CodeQL的基礎語法,重點掌握關於類和謂詞的概念。


          參考連結:https://longlone.top/%E5%AE%89%E5%85%A8/%E5%AE%89%E5%85%A8%E7%A0%94%E7%A9%B6/codeql/2.CodeQL%E8%AF%AD%E6%B3%95/


          直接來學習語法是一件很枯燥的事情,我們這裡只是總結一些CodeQL中重點的概念。關於語法詳情在後續的實際案例分析中會有更深刻的體會。


          1)  與Class相關的概念


          與類直接相關的概念包括Class、Method、Field、Constructor,其代表的意義與java語言一致,透過其相互組合可以從資料庫中篩選出符合條件的類和方法。


          Demo1: 查詢類的全限定名中包含Person的類,其中方法getQualifiedName代表獲取類對應的全限定類名。

            import java
            from Class cwhere c.getQualifiedName().indexOf("Person") >=0select c.getQualifiedName()


            Demo2: 查詢所有欄位Field,滿足條件是欄位型別是public,並且欄位型別繼承java.lang.Throwable。(Fastjson1.2.80漏洞利用鏈的查詢方式)。


            其中getASupertype代表獲取類對應的父類,*代表遞迴查詢所有父類。


            getDeclaringType代表獲取欄位對應的定義型別。


            getAModifier代表獲取欄位對應的修飾符。

              import java
              from Class c, Field fwhere c.getASupertype*().hasQualifiedName("java.lang", "Throwable") andf.getDeclaringType() = c and f.getAModifier().getName() = "public"select c.getQualifiedName(),f.getName()


              2)  與Access相關的概念


              access代表對變數或者方法的呼叫,主要有VarAccess和MethodAccess。


              Demo1:查詢所有繼承自java.util.list的變數及變數的引用。

                import java
                from RefType t,Variable v,VarAccess vawhere t.getSourceDeclaration().getASourceSupertype*().hasQualifiedName("java.util", "List") and    v.getType() = t and    va.getVariable() = vselect v,va


                Demo2:查詢所有InputStream類對應的readObject方法呼叫(遍歷反序列化漏洞的基礎)。

                  import java
                  from MethodAccess ma,Class cwhere ma.getMethod().hasName("readObject") and      ma.getQualifier().getType() = c and      c.getASupertype*().hasQualifiedName("java.io", "InputStream")select ma,ma.getEnclosingCallable()


                  3)與Type相關的概念


                  Type代表型別,是屬於CodeQL中一個很重要的概念,Type類有倆個直接派生類PrimitiveType,RefType。


                  PrimitiveType代表Java中的基礎資料型別,派生類有boolean, byte, char, double, float, int, long, short, void,, null。


                  RefType代表Java中的引用型別,有派生類Class、Interface、EnumType、Array。


                  Type多數情況下是和Acess相互使用的,其實在上面Acess的例子中幾乎都用到了Type相關的類。


                  4)與Flow相關的概念


                  Flow是CodeQL中最重要的概念,代表資料流,與此對應的概念包括source和sink。


                  source代表可控的使用者輸入點,通常是指WEB站點中的URL中引數,例如

                  request.getParameter("name")。其他例如命令列引數args也屬於source。在CodeQL中已經存在RemoteFlowSource類,在類中已經定義了很多常見的source點,可以滿足我們做一般性程式碼審計的需要。但是如果我們是要做特定jar包漏洞挖掘,例如復現log4j2的遠端命令執行漏洞,由於log4j2包中不包含常規的source點,就需要使用者自定義source。


                  sink代表危險的函式,通常是指一些危險的操作,包括命令執行、程式碼執行、jndi注入、SQL隱碼攻擊、XML注入等。CodeQL雖然也預置了部分的sink點,但是遠不能滿足實際的需求,需要我們在不同的漏洞環境中自定義sink點。


                  在有了source和sink之後我們可以基於CodeQL提供的查詢機制,自動判斷是否存在flow可以連線source和sink,一個典型的用法如下,如圖3.1所示。

                  圖片

                  圖3.1 典型的flow利用方式


                  在圖3.1所示的Flow中,自定義類繼承自TaintTracking::Configuration,並且覆蓋其中的isSource個isSink方法。這個是固定寫法,後續的絕大部分的ql指令碼都包含這樣的程式碼。


                  其中isAdditionalTaintStep方法是CodeQL的類TaintTracking::Configuration提供的的方法,它的原型是:override predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) {}。它的作用是將一個可控節點A強制傳遞給另外一個節點B,那麼節點B也就成了可控節點,如圖3.2所示。

                  圖片

                  圖3.2 isAdditionalTaintStep方法的連線作用


                  如果CodeQL不能自動連線node1和node2節點,就需要手動透過isAdditionalTaintStep來指定連線。


                  除此之外,在Flow中還有一個方法經常用到isSanitizer,用法如圖3.3所示。這是官方提供的關於log4j2漏洞的查詢指令碼,其中定義了isSanitizer方法來限制flow流中的資料不能是基本資料型別PrimitiveType和BoxedType型別。這是一個特別常用的過濾機制,代表只要是常規的字元型別(Bool、int這些)則不再進行傳遞。

                  圖片

                  圖3.3 isSanitizer方法的過濾作用



                  0x04 案例實踐


                  作為新手來說,要自己編寫有效的CodeQL查詢指令碼是一件很難的事情,幸運的是CodeQL官方為我們提供了大量的demo。


                  參考地址:https://github.com/github/codeql/tree/main/java/ql/src/experimental/Security/CWE


                  我們可以直接使用這些demo來完成部分漏洞發現功能。


                  為了更加清晰的理解關於CodeQL的使用,透過具體案例來演示CodeQL的作用。若依RuoYi是國內使用量較大的後臺管理系統,從網上下載到某版本的RuoYi的原始碼。


                  1)基於RuoYi的原始碼生成資料庫


                    arch -x86_64 codeql database create /Users/pang0lin/CodeQL/databases/RuoYi --language=java --command='mvn clean install -DskipTests' --source-root=/Users/pang0lin/cms/若依RuoYi --overwrite


                    成功生成資料庫之後,會返回類似的success介面,如圖4.1所示。

                    圖片

                    圖4.1 建立基於RuoYI的資料庫


                    2)使用官方demo查詢漏洞


                    官網提供了很多查詢的ql指令碼,其中能直接找到若依相關漏洞的有兩個指令碼,其中第一個指令碼是spel表示式注入的查詢指令碼。


                    參考地址:https://github.com/github/codeql/blob/main/java/ql/src/experimental/Security/CWE/CWE-094/SpringViewManipulation.ql)


                    查詢結果如圖4.2所示。

                    圖片

                    圖4.2 基於SpringViewManipulation的查詢結果


                    檢視sink點詳情可知這個漏洞是使用者輸入的fragment直接傳入了模版引擎中,如圖4.3所示。

                    圖片

                    圖4.3 跟蹤sink點之後的結果


                    這個漏洞其實是屬於若依的一個已知的安全問題,詳情見:https://blog.csdn.net/qq_33608000/article/details/124375219#Thymeleaf_184


                    雖然在最新版的若依中已經因為升級了thymeleaf版本導致無法利用,但是站在CodeQL的角度還是可以發現這種問題。


                    另一個可用的CodeQL的查詢指令碼是基於mybatis的SQL隱碼攻擊查詢指令碼,詳情見:https://github.com/github/codeql/blob/main/java/ql/src/experimental/Security/CWE/CWE-089/MyBatisMapperXmlSqlInjection.ql


                    查詢結果如圖4.4所示。

                    圖片

                    圖4.4 基於MyBatisMapperXmlSqlInjection的查詢結果


                    可以看到CodeQL找到了若依可能存在的SQL隱碼攻擊漏洞,跟進sink點看一下,如圖所示。每一個都是類似的問題,我們隨便開啟看一個就可以了。

                    圖片

                    這個可以看到這裡的引數傳遞到SQL語句中,造成了SQL隱碼攻擊漏洞。這個漏洞在網上也有大佬已經提到了漏洞細節資訊,詳情見:

                    https://juejin.cn/post/7001087308510265352


                    從上面的兩次查詢中我們可以看到CodeQL在程式碼審計過程中帶來的便利,可以方便的幫助我們定位可能存在的漏洞點。


                    0x05 結論


                    CodeQL給我們提供的查詢ql指令碼有很多,如果是透過手工一個一個試的話並不是一個好的解決辦法,並且官方的ql指令碼並不完善,還有很大的完善空間。


                    如何利用大量的ql指令碼完成自動化的程式碼掃描,我們會在下一篇文章中進行講解。


                    原文連結

                    相關文章