新聞中心
0x01 前言
最近關(guān)于CodeQL的概念很火,大家普遍認(rèn)為這會(huì)是下一代的代碼審計(jì)神器。網(wǎng)上關(guān)于CodeQL的文章已經(jīng)有不少,但是多數(shù)文章還是在分析CodeQL的安裝和簡(jiǎn)單使用用例。真正使用CodeQL來(lái)進(jìn)行自動(dòng)化代碼審計(jì)的文章較少,本文主要研究基于CodeQL實(shí)現(xiàn)全自動(dòng)的代碼審計(jì)工具實(shí)現(xiàn)思路,預(yù)計(jì)文章分成三部分完成,目前是第一部分內(nèi)容。

創(chuàng)新互聯(lián)建站主要從事成都網(wǎng)站建設(shè)、成都做網(wǎng)站、網(wǎng)頁(yè)設(shè)計(jì)、企業(yè)做網(wǎng)站、公司建網(wǎng)站等業(yè)務(wù)。立足成都服務(wù)石拐,十余年網(wǎng)站建設(shè)經(jīng)驗(yàn),價(jià)格優(yōu)惠、服務(wù)專業(yè),歡迎來(lái)電咨詢建站服務(wù):028-86922220
CodeQL(全稱Code Query Language),從其英文名稱中可以看出這是一種基于代碼的查詢語(yǔ)言,其作用主要是通過(guò)編寫好的語(yǔ)句查詢代碼中可能存在的安全隱患。學(xué)習(xí)CodeQL類似于學(xué)習(xí)一門全新的編程語(yǔ)言,語(yǔ)法類似于SQL,但是比傳統(tǒng)SQL還是要難很多。目前CodeQL支持對(duì)多種語(yǔ)言,包括java、javascript、go、python、C、Csharp等,但是很遺憾的是不支持“世界上最好的語(yǔ)言”PHP。這大概是因?yàn)镻HP實(shí)在是太靈活了,函數(shù)名是字符串變量這種調(diào)用方式確實(shí)很難從AST語(yǔ)法樹中靜態(tài)分析出問(wèn)題,但這并不能阻礙我們學(xué)習(xí)CodeQL的興趣。文章所有內(nèi)容基本上圍繞java語(yǔ)言展開,其他語(yǔ)言操作基本類似。
0x02 環(huán)境準(zhǔn)備
網(wǎng)上關(guān)于CodeQL安裝的文章已經(jīng)很多了,本來(lái)不打算再說(shuō)這個(gè)事情,但是因?yàn)楸救嗽贑odeQL安裝過(guò)程中遇到不兼容mac m1架構(gòu)的情況,我想還有很多小伙伴也會(huì)遇到這個(gè)問(wèn)題的,這里主要以MAC的環(huán)境來(lái)說(shuō)明安裝過(guò)程。
CodeQL的安裝主要分成引擎和SDK,新建一個(gè)目錄CodeQL(~/CodeQL/)來(lái)保存后續(xù)所有的相關(guān)的工具和代碼。
首先下載最新的引擎包,下載地址是:https://github.com/github/codeql-cli-binaries/releases
下載之后解壓把codeql文件夾放在剛才新建的文件夾CodeQL中,添加環(huán)境變量。
vim ~/.profile
export PATH=/Users/用戶名/CodeQL/codeql:${PATH}
使用source命令是環(huán)境變量生效,然后命令行中運(yùn)行codeql,如圖2.1所示。
圖2.1 CodeQL引擎安裝
然后需要下載CodeQL對(duì)應(yīng)的sdk包,下載地址是:
https://github.com/Semmle/ql
下載之后也需要把ql文件夾復(fù)制到~/CodeQL文件夾中。
在CodeQL文件夾中新建databases文件夾,用于存放后續(xù)使用codeql生成的數(shù)據(jù)庫(kù),那么一切準(zhǔn)備好了之后我們的CodeQL目錄之下就會(huì)是三個(gè)文件夾,如圖2.2所示。
圖2.2 CodeQL安裝
后續(xù)我們就可以使用codeql database create命令來(lái)創(chuàng)建查詢數(shù)據(jù)庫(kù),命令如下所示。
codeql database create /Users/xxx/CodeQL/databases/project_db_name --language=java --source-root=/Users/xxx/cms/project_path --overwrite
在windows環(huán)境中和以前的mac環(huán)境中確實(shí)沒有問(wèn)題,但是如果是在m1的環(huán)境中會(huì)報(bào)錯(cuò),報(bào)錯(cuò)信息如圖2.3所示。錯(cuò)誤的原因是codeql官方提供的工具是x86架構(gòu)的,不能直接在arm中使用。
圖2.3 在MAC M1環(huán)境中codeql運(yùn)行錯(cuò)誤
從官網(wǎng)中找到了codeql對(duì)m1的支持情況,如圖2.4所示。從圖中可以明確看出codeql確實(shí)是支持m1架構(gòu)的,但是需要依賴rosetta2和xcode。但是并沒有給出具體的安裝和使用步驟,必須吐槽官方一點(diǎn)也不人性化,說(shuō)話說(shuō)一半。
圖2.4 CodeQL支持M1架構(gòu)
后來(lái)慢慢摸索著裝xcode和rosetta2,安裝xcode是直接通過(guò)appstore來(lái)裝的,安裝rosetta2是使用下面的命令。
softwareupdate –install-rosetta
安裝好了之后就可以使用下面的命令來(lái)生成數(shù)據(jù)庫(kù),與傳統(tǒng)方式不同的是需要在命令前面增加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生成數(shù)據(jù)庫(kù)
0x03 語(yǔ)法基礎(chǔ)
CodeQL是一門全新的語(yǔ)言,基礎(chǔ)的CodeQL語(yǔ)法網(wǎng)上已經(jīng)有很多文章。大家在學(xué)習(xí)之前可以首先參考鏈接,了解關(guān)于CodeQL的基礎(chǔ)語(yǔ)法,重點(diǎn)掌握關(guān)于類和謂詞的概念。
參考鏈接: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/
直接來(lái)學(xué)習(xí)語(yǔ)法是一件很枯燥的事情,我們這里只是總結(jié)一些CodeQL中重點(diǎn)的概念。關(guān)于語(yǔ)法詳情在后續(xù)的實(shí)際案例分析中會(huì)有更深刻的體會(huì)。
1) 與Class相關(guān)的概念
與類直接相關(guān)的概念包括Class、Method、Field、Constructor,其代表的意義與java語(yǔ)言一致,通過(guò)其相互組合可以從數(shù)據(jù)庫(kù)中篩選出符合條件的類和方法。
Demo1: 查詢類的全限定名中包含Person的類,其中方法getQualifiedName代表獲取類對(duì)應(yīng)的全限定類名。
import java
from Class c
where c.getQualifiedName().indexOf("Person") >=0
select c.getQualifiedName()
Demo2: 查詢所有字段Field,滿足條件是字段類型是public,并且字段類型繼承java.lang.Throwable。(Fastjson1.2.80漏洞利用鏈的查找方式)。
其中g(shù)etASupertype代表獲取類對(duì)應(yīng)的父類,*代表遞歸查找所有父類。
getDeclaringType代表獲取字段對(duì)應(yīng)的定義類型。
getAModifier代表獲取字段對(duì)應(yīng)的修飾符。
import java
from Class c, Field f
where c.getASupertype*().hasQualifiedName("java.lang", "Throwable") and
f.getDeclaringType() = c and
f.getAModifier().getName() = "public"
select c.getQualifiedName(),f.getName()
2) 與Access相關(guān)的概念
access代表對(duì)變量或者方法的調(diào)用,主要有VarAccess和MethodAccess。
Demo1:查詢所有繼承自java.util.list的變量及變量的引用。
import java
from RefType t,Variable v,VarAccess va
where t.getSourceDeclaration().getASourceSupertype*().hasQualifiedName("java.util", "List") and
v.getType() = t and
va.getVariable() = v
select v,va
Demo2:查詢所有InputStream類對(duì)應(yīng)的readObject方法調(diào)用(遍歷反序列化漏洞的基礎(chǔ))。
import java
from MethodAccess ma,Class c
where ma.getMethod().hasName("readObject") and
ma.getQualifier().getType() = c and
c.getASupertype*().hasQualifiedName("java.io", "InputStream")
select ma,ma.getEnclosingCallable()
3)與Type相關(guān)的概念
Type代表類型,是屬于CodeQL中一個(gè)很重要的概念,Type類有倆個(gè)直接派生類PrimitiveType,RefType。
PrimitiveType代表Java中的基礎(chǔ)數(shù)據(jù)類型,派生類有boolean, byte, char, double, float, int, long, short, void,, null。
RefType代表Java中的引用類型,有派生類Class、Interface、EnumType、Array。
Type多數(shù)情況下是和Acess相互使用的,其實(shí)在上面Acess的例子中幾乎都用到了Type相關(guān)的類。
4)與Flow相關(guān)的概念
Flow是CodeQL中最重要的概念,代表數(shù)據(jù)流,與此對(duì)應(yīng)的概念包括source和sink。
source代表可控的用戶輸入點(diǎn),通常是指WEB站點(diǎn)中的URL中參數(shù),例如
request.getParameter("name")。其他例如命令行參數(shù)args也屬于source。在CodeQL中已經(jīng)存在RemoteFlowSource類,在類中已經(jīng)定義了很多常見的source點(diǎn),可以滿足我們做一般性代碼審計(jì)的需要。但是如果我們是要做特定jar包漏洞挖掘,例如復(fù)現(xiàn)log4j2的遠(yuǎn)程命令執(zhí)行漏洞,由于log4j2包中不包含常規(guī)的source點(diǎn),就需要用戶自定義source。
sink代表危險(xiǎn)的函數(shù),通常是指一些危險(xiǎn)的操作,包括命令執(zhí)行、代碼執(zhí)行、jndi注入、SQL注入、XML注入等。CodeQL雖然也預(yù)置了部分的sink點(diǎn),但是遠(yuǎn)不能滿足實(shí)際的需求,需要我們?cè)诓煌穆┒喘h(huán)境中自定義sink點(diǎn)。
在有了source和sink之后我們可以基于CodeQL提供的查詢機(jī)制,自動(dòng)判斷是否存在flow可以連接source和sink,一個(gè)典型的用法如下,如圖3.1所示。
圖3.1 典型的flow利用方式
在圖3.1所示的Flow中,自定義類繼承自TaintTracking::Configuration,并且覆蓋其中的isSource個(gè)isSink方法。這個(gè)是固定寫法,后續(xù)的絕大部分的ql腳本都包含這樣的代碼。
其中isAdditionalTaintStep方法是CodeQL的類TaintTracking::Configuration提供的的方法,它的原型是:override predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) {}。它的作用是將一個(gè)可控節(jié)點(diǎn)A強(qiáng)制傳遞給另外一個(gè)節(jié)點(diǎn)B,那么節(jié)點(diǎn)B也就成了可控節(jié)點(diǎn),如圖3.2所示。
圖3.2 isAdditionalTaintStep方法的連接作用
如果CodeQL不能自動(dòng)連接node1和node2節(jié)點(diǎn),就需要手動(dòng)通過(guò)isAdditionalTaintStep來(lái)指定連接。
除此之外,在Flow中還有一個(gè)方法經(jīng)常用到isSanitizer,用法如圖3.3所示。這是官方提供的關(guān)于log4j2漏洞的查詢腳本,其中定義了isSanitizer方法來(lái)限制flow流中的數(shù)據(jù)不能是基本數(shù)據(jù)類型PrimitiveType和BoxedType類型。這是一個(gè)特別常用的過(guò)濾機(jī)制,代表只要是常規(guī)的字符類型(Bool、int這些)則不再進(jìn)行傳遞。
圖3.3 isSanitizer方法的過(guò)濾作用
0x04 案例實(shí)踐
作為新手來(lái)說(shuō),要自己編寫有效的CodeQL查詢腳本是一件很難的事情,幸運(yùn)的是CodeQL官方為我們提供了大量的demo。
參考地址:https://github.com/github/codeql/tree/main/java/ql/src/experimental/Security/CWE
我們可以直接使用這些demo來(lái)完成部分漏洞發(fā)現(xiàn)功能。
為了更加清晰的理解關(guān)于CodeQL的使用,通過(guò)具體案例來(lái)演示CodeQL的作用。若依RuoYi是國(guó)內(nèi)使用量較大的后臺(tái)管理系統(tǒng),從網(wǎng)上下載到某版本的RuoYi的源碼。
1)基于RuoYi的源碼生成數(shù)據(jù)庫(kù)
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
成功生成數(shù)據(jù)庫(kù)之后,會(huì)返回類似的success界面,如圖4.1所示。
圖4.1 創(chuàng)建基于RuoYI的數(shù)據(jù)庫(kù)
2)使用官方demo查詢漏洞
官網(wǎng)提供了很多查詢的ql腳本,其中能直接找到若依相關(guān)漏洞的有兩個(gè)腳本,其中第一個(gè)腳本是spel表達(dá)式注入的查詢腳本。
參考地址:https://github.com/github/codeql/blob/main/java/ql/src/experimental/Security/CWE/CWE-094/SpringViewManipulation.ql)
查詢結(jié)果如圖4.2所示。
圖4.2 基于SpringViewManipulation的查詢結(jié)果
查看sink點(diǎn)詳情可知這個(gè)漏洞是用戶輸入的fragment直接傳入了模版引擎中,如圖4.3所示。
圖4.3 跟蹤sink點(diǎn)之后的結(jié)果
這個(gè)漏洞其實(shí)是屬于若依的一個(gè)已知的安全問(wèn)題,詳情見:https://blog.csdn.net/qq_33608000/article/details/124375219#Thymeleaf_184
雖然在最新版的若依中已經(jīng)因?yàn)樯?jí)了thymeleaf版本導(dǎo)致無(wú)法利用,但是站在CodeQL的角度還是可以發(fā)現(xiàn)這種問(wèn)題。
另一個(gè)可用的CodeQL的查詢腳本是基于mybatis的SQL注入查詢腳本,詳情見:https://github.com/github/codeql/blob/main/java/ql/src/experimental/Security/CWE/CWE-089/MyBatisMapperXmlSqlInjection.ql
查詢結(jié)果如圖4.4所示。
圖4.4 基于MyBatisMapperXmlSqlInjection的查詢結(jié)果
可以看到CodeQL找到了若依可能存在的SQL注入漏洞,跟進(jìn)sink點(diǎn)看一下,如圖4.5所示。每一個(gè)都是類似的問(wèn)題,我們隨便打開看一個(gè)就可以了。
這個(gè)可以看到這里的參數(shù)傳遞到SQL語(yǔ)句中,造成了SQL注入漏洞。這個(gè)漏洞在網(wǎng)上也有大佬已經(jīng)提到了漏洞細(xì)節(jié)信息,詳情見:
https://juejin.cn/post/7001087308510265352
從上面的兩次查詢中我們可以看到CodeQL在代碼審計(jì)過(guò)程中帶來(lái)的便利,可以方便的幫助我們定位可能存在的漏洞點(diǎn)。
0x05 結(jié)論
CodeQL給我們提供的查詢ql腳本有很多,如果是通過(guò)手工一個(gè)一個(gè)試的話并不是一個(gè)好的解決辦法,并且官方的ql腳本并不完善,還有很大的完善空間。
如何利用大量的ql腳本完成自動(dòng)化的代碼掃描,我們會(huì)在下一篇文章中進(jìn)行講解。
本文作者:盛邦安全WebRAY, 轉(zhuǎn)載請(qǐng)注明來(lái)自FreeBuf.COM
當(dāng)前標(biāo)題:CodeQL的自動(dòng)化代碼審計(jì)之路
轉(zhuǎn)載來(lái)于:http://m.fisionsoft.com.cn/article/dpogjso.html


咨詢
建站咨詢
