你引用的開源代碼,可能夾帶了漏洞
0. 概述
近一兩年,供應(yīng)鏈攻擊已不只是白帽子的實(shí)驗(yàn)試水,轉(zhuǎn)變成黑客和黑產(chǎn)的真實(shí)攻擊手段,“軟件供應(yīng)鏈安全”概念,重新成為了炙手可熱的話題。
當(dāng)前對(duì)供應(yīng)鏈安全的探討多是關(guān)于機(jī)制的,例如企業(yè)上下游公司的攻擊面,或者各種開發(fā)語(yǔ)言軟件包管理引用的投毒欺騙。但是對(duì)于一線的開發(fā)實(shí)踐中的風(fēng)險(xiǎn),目前鮮有分析。
試想,在一個(gè)多人協(xié)作開發(fā)的項(xiàng)目中,如果:
有一個(gè)偷懶的開發(fā)者復(fù)制很多網(wǎng)上貼的示例代碼或錯(cuò)誤代碼;
或者一個(gè)新加入的開發(fā)者,復(fù)制了該項(xiàng)目的某些舊代碼,其中有一些帶有已修復(fù)的bug;
甚至如果有一個(gè)惡意開發(fā)者,故意寫了一個(gè)形似手誤的bug、但實(shí)際是可以被遠(yuǎn)程利用的隱蔽后門。
那么項(xiàng)目的擁有者要如何分辨這些有風(fēng)險(xiǎn)的代碼?只要把這些潛在的“壞的”開發(fā)角色換為上游開源代碼供應(yīng)方,一個(gè)完全可能的“開源供應(yīng)鏈漏洞”場(chǎng)景就很好理解了。
從這一個(gè)簡(jiǎn)單的假想出發(fā),本文將帶領(lǐng)讀者看到,設(shè)計(jì)開源代碼使用的開發(fā)實(shí)踐中的真實(shí)威脅,以及我們構(gòu)建好的一個(gè)解決方案。
1. “代碼復(fù)用”引入的深層次供應(yīng)鏈問(wèn)題
目前為止,受到廣泛關(guān)注的供應(yīng)鏈風(fēng)險(xiǎn),集中于兩類:以名稱易于混淆的惡意軟件包倉(cāng)庫(kù)投毒,如PyPi、NPM倉(cāng)庫(kù)的投毒攻擊;以及滲透軟件廠商的上下游供應(yīng)商,以竊取其掌握的敏感信息。但源代碼的流動(dòng)和依賴,遠(yuǎn)非庫(kù)依賴那么簡(jiǎn)單。
這里,我們抽象出一個(gè)開發(fā)實(shí)踐中普遍存在,但內(nèi)中安全風(fēng)險(xiǎn)并未引起廣泛注意和分析的供應(yīng)鏈威脅向量——代碼復(fù)用。
1.1. 代碼復(fù)用與漏洞:Google P0分析視角
今年6月,Google Project Zero下屬的漏洞根因分析小組發(fā)出半年度在野利用漏洞報(bào)告。報(bào)告分析2022年上半年內(nèi),發(fā)現(xiàn)被在野利用的系統(tǒng)或關(guān)鍵基礎(chǔ)軟件0day漏洞中,50%(9/18)并非全新孤立的漏洞,而是某個(gè)歷史漏洞的關(guān)聯(lián)“變種”,主要分為三類:
歷史被修復(fù)的漏洞,補(bǔ)丁代碼被回滾造成漏洞重現(xiàn),如CVE-2022-22620,是WebKit引擎在2013年已經(jīng)修復(fù)的漏洞,于2016年代碼重構(gòu)中被原樣改了回去;
某個(gè)此前被修復(fù)的漏洞,在有關(guān)聯(lián)和相似關(guān)系的其它功能模塊中發(fā)現(xiàn)類似漏洞,如CVE-2021-39793,是Linux etnaviv驅(qū)動(dòng)程序修補(bǔ)的bug,在Google Pixel Mali GPU私有驅(qū)動(dòng)代碼中的同源漏洞;
對(duì)于漏洞根因,此前的修復(fù)方案不完整,造成可在新的漏洞觸發(fā)路徑利用,如Windows系統(tǒng)win32k驅(qū)動(dòng)中,CVE-2022-21882是CVE-2021-1732的相同用戶模式回調(diào)bug。
這樣的觀察側(cè)面表明,自體或繼承的同源代碼,帶有復(fù)雜的風(fēng)險(xiǎn),特別是對(duì)黑客黑產(chǎn)而言,挖掘利用這些“炒冷飯”的漏洞,實(shí)際是很經(jīng)濟(jì)的實(shí)踐。
1.2. 各種形式的“代碼復(fù)用”及對(duì)應(yīng)的風(fēng)險(xiǎn)案例
結(jié)合開源生態(tài)以及企業(yè)級(jí)產(chǎn)品開發(fā)實(shí)踐,我們歸結(jié)除了簡(jiǎn)單的軟件包引用形式外,至少有三個(gè)顆粒度的代碼復(fù)用形式,特別是在C/C++這類沒(méi)有統(tǒng)一的包依賴管理機(jī)制的主流開發(fā)語(yǔ)言中存在。
1.2.1. 開源項(xiàng)目子目錄或文件形式包含
某些項(xiàng)目依賴于特定開源組件功能,但出于維持明確的依賴關(guān)系、清晰的制品形式的目的,并不會(huì)動(dòng)態(tài)鏈接(依賴)獨(dú)立的軟件包或動(dòng)態(tài)庫(kù),而是將固定版本的開源代碼,完整目錄或者裁剪的部分文件形式,直接包含在代碼目錄下,同步編譯靜態(tài)鏈接在制品二進(jìn)制中。
這種情況下,開發(fā)者的訴求一般是追求“穩(wěn)定可用即可”,并規(guī)避依賴子模塊接口更新帶來(lái)的維護(hù)成本,更不會(huì)主動(dòng)關(guān)注子模塊是否有安全更新并跟進(jìn),因此很容易存在有歷史漏洞的成分。
例如,Java開發(fā)的移動(dòng)端通信SDK和軟件Telegram,以jni調(diào)用形式包含了boringssl,而后者是Google自O(shè)penSSL 1.1.0歷史分支fork并維護(hù)的二次開發(fā)項(xiàng)目。雖然boringssl以一定機(jī)制跟進(jìn)了上游的漏洞修復(fù),但Telegram對(duì)這些依賴的更新沒(méi)有確定策略,最后一次更新boringssl停留在2020.8.15,因此近兩年多的漏洞都可能影響著Telegram(如果構(gòu)造出合理的調(diào)用路徑),并進(jìn)一步影響封裝Telegram的更下游應(yīng)用,如Nekogram。
1.2.2. 函數(shù)和片段級(jí)別代碼復(fù)用(復(fù)制)
開源代碼往往也成為開發(fā)實(shí)踐中取之不竭的代碼模板和材料。對(duì)于某些典型原子功能的實(shí)現(xiàn),開發(fā)者“借用”開源代碼片段并依據(jù)自己項(xiàng)目的上下文做適當(dāng)修改變形,是較為普遍,但又無(wú)法評(píng)估存在占比的實(shí)踐。根據(jù)Synopsys今年在17個(gè)商業(yè)體超過(guò)2400個(gè)商用軟件代碼倉(cāng)庫(kù)中分析后,形成的開源安全與風(fēng)險(xiǎn)分析報(bào)告認(rèn)為,商業(yè)軟件中78%比例的代碼實(shí)際是開源代碼。
在我們對(duì)開源代碼的分析中,也有一定量的代碼復(fù)用,并隨著復(fù)用的舊版本開源代碼引入潛在脆弱代碼的案例。例如,某款韓國(guó) 自研 的IoT專用安全庫(kù)中的密碼算法實(shí)現(xiàn)中,即發(fā)現(xiàn)了疑似復(fù)制自O(shè)penSSL密碼算法庫(kù)中中國(guó)SM2算法的功能實(shí)現(xiàn),且復(fù)制版本為被爆出高危漏洞CVE-2021-3711的修補(bǔ)前函數(shù)。
1.2.3. 數(shù)據(jù)結(jié)構(gòu)和接口調(diào)用的語(yǔ)法復(fù)用
另一種典型的問(wèn)題是數(shù)據(jù)結(jié)構(gòu)和接口的誤用。對(duì)某些未充分文檔化的數(shù)據(jù)結(jié)構(gòu)和功能接口,項(xiàng)目?jī)?nèi)部協(xié)作的開發(fā)者可能存在共通的錯(cuò)誤理解和誤用情況;作為SDK導(dǎo)出的接口被下游開發(fā)者誤用的情況更是屢見(jiàn)不鮮。此時(shí)就會(huì)看到針對(duì)一個(gè)漏洞根因的多處位置、語(yǔ)法各異但語(yǔ)用同源的漏洞,這樣的案例屢見(jiàn)不鮮。
一個(gè)很容易理解的案例是,OpenSSL漏洞CVE-2021-3712,根因在于定義的結(jié)構(gòu)體ASN1_IA5STRING,其中帶有一個(gè)非'\0'結(jié)尾的緩沖區(qū)指針,和緩沖區(qū)有效數(shù)據(jù)長(zhǎng)度字段;但大量開源協(xié)作開發(fā)者未意識(shí)到緩沖區(qū)并非標(biāo)準(zhǔn)C樣式字符串,而使用了不安全的字符串操作函數(shù),例如sprintf, strdup, strcat, strchr等以及其在OpenSSL中的封裝版本,從而造成了多處緩沖區(qū)越界訪問(wèn)或?qū)懸绯龅?。進(jìn)一步地,這個(gè)數(shù)據(jù)結(jié)構(gòu)也被導(dǎo)出,從而被一些依賴的下游項(xiàng)目同樣誤用,造成漏洞的修復(fù)無(wú)法收斂。
2. 從問(wèn)題到解決方案之間的鴻溝
考慮到代碼開發(fā)中人的因素的復(fù)雜性,可以預(yù)見(jiàn)以上代碼復(fù)用引入漏洞威脅在未來(lái)可能的增長(zhǎng),特別是在商業(yè)對(duì)抗領(lǐng)域。但現(xiàn)有的程序分析技術(shù),還不足以解決這類看似簡(jiǎn)單的問(wèn)題。
2.1. 軟件成分分析(SCA)的掣肘
看起來(lái)源代碼復(fù)用的問(wèn)題,剛好是軟件成分分析(SCA)的目標(biāo)領(lǐng)域,因?yàn)榭梢猿橄鬄閽呙璋l(fā)現(xiàn)帶有脆弱性的開源或自體代碼成分——但是當(dāng)前的SCA或許還并不是我們期待的模樣。
現(xiàn)有實(shí)際投產(chǎn)的主流SCA技術(shù),主要面向有統(tǒng)一包管理機(jī)制的開發(fā)語(yǔ)言生態(tài),借助解析依賴關(guān)系配置,獲得直接和間接的軟件包依賴圖譜,亦即“成分”,典型如對(duì)Maven、PyPi、NPM等語(yǔ)言包倉(cāng)庫(kù)的依賴。某些頭部軟件分析工具提供商也在將高校和科研院所的研究成果轉(zhuǎn)化為可用的產(chǎn)品,通過(guò)對(duì)開源代碼設(shè)計(jì)計(jì)算一種類似模糊哈希的指紋,或者對(duì)關(guān)鍵代碼做token化后形成模糊查找模板,從而具備對(duì)開源代碼引用的一定程度的匹配檢測(cè)能力。
但是,SCA的技術(shù)路線本身就制約了可分析問(wèn)題的方向和顆粒度。顯然,以上討論的代碼復(fù)用引入的風(fēng)險(xiǎn),均歸類為無(wú)顯式依賴關(guān)系的代碼復(fù)用,這決定難以通過(guò)當(dāng)下成熟的SCA技術(shù)覆蓋到。而對(duì)于通過(guò)代碼指紋方式掃描代碼存在性,考慮到代碼搜索空間,采集計(jì)算指紋的代碼顆粒度越細(xì),總和指紋庫(kù)越會(huì)嚴(yán)重膨脹,且指紋匹配搜索的運(yùn)算量也可能提升到算力瓶頸以上,從而反向約束指紋設(shè)計(jì)的精度上限;根據(jù)了解,當(dāng)前具備可用性前景的技術(shù),也僅能做文件粒度的簽名和匹配查找,距離函數(shù)粒度有難以逾越的鴻溝。
此外,如上所述,一些代碼復(fù)用,并不保證文本層面相似特征的保留。這種情況下,需要更偏向語(yǔ)法甚至語(yǔ)義層面的抽象和分析,這也是SCA所不具備的。
2.2. 靜態(tài)源碼分析測(cè)試(SAST)的缺失
考慮到成分分析方法最難覆蓋到偏向語(yǔ)法模式相似的領(lǐng)域,很自然的,我們需要尋求靜態(tài)源代碼安全分析測(cè)試(SAST)的幫助。針對(duì)開源代碼的測(cè)試,我們已經(jīng)有了一個(gè)得力的工具,GitHub面向開源開放使用的CodeQL。對(duì)CodeQL的用法介紹我們?cè)诖瞬辉儋樖?,它以類似SQL的規(guī)則樣式對(duì)語(yǔ)法數(shù)據(jù)庫(kù)做查詢的形式、對(duì)多語(yǔ)言的支持、豐富的API接口都保證了作為SAST的靈活可用性。它對(duì)已知漏洞的覆蓋和可掃描性又如何?
2.2.1. 實(shí)例引入
為簡(jiǎn)單地說(shuō)明問(wèn)題,我們不妨“臆造”一個(gè)與歷史漏洞同源的代碼bug。OpenSSL的一個(gè)高危歷史漏洞CVE-2020-1967,是典型的空指針解引用問(wèn)題。我們選擇在一個(gè)由OpenSSL二次開發(fā)的開源項(xiàng)目BabaSSL中,將該漏洞移植到另一個(gè)上下文,移植的代碼變更為:

這里,tls1_lookup_sigalg()函數(shù)返回值為一個(gè)可能取值為NULL的指針,這里去掉了一個(gè)NULL的檢查。實(shí)際上,如果將這處改動(dòng)反過(guò)來(lái)看,那么就可以當(dāng)做一個(gè)預(yù)先存在的漏洞修復(fù)的patch。針對(duì)這個(gè)“漏洞”,根據(jù)patch寫ql規(guī)則覆蓋漏洞成因,關(guān)鍵點(diǎn)如下:
需要定位的目標(biāo)是一個(gè)SIGALG_LOOKUP *類型變量和一個(gè)if塊;
變量在if同一層代碼塊中定義或賦值;
之后在if的條件語(yǔ)句中直接解引用其成員變量做判斷;
當(dāng)前代碼塊以及if的判斷條件中,沒(méi)有對(duì)指針與NULL的比較判斷。
據(jù)此可以簡(jiǎn)單編寫規(guī)則,覆蓋這個(gè)漏洞的上下文:

2.2.2. 問(wèn)題和需求分析
CodeQL可有效用于特定編程語(yǔ)言中,典型漏洞(bug)成因模式的檢測(cè)。但真實(shí)的漏洞,只有一部分能夠被已有的規(guī)則覆蓋檢出;主要原因之一,是已有規(guī)則是通用問(wèn)題導(dǎo)向的,而其實(shí)現(xiàn),又僅覆蓋特定問(wèn)題場(chǎng)景。
例如,對(duì)于C/C++語(yǔ)言中,空指針解引用這個(gè)經(jīng)典問(wèn)題(CWE-476),CodeQL用幾條通用規(guī)則覆蓋若干個(gè)典型問(wèn)題場(chǎng)景,如特定指針類型變量先解引用后檢查NULL,或判斷某個(gè)返回值為指針類型的函數(shù),是否在多數(shù)調(diào)用時(shí)檢查了返回值是否為NULL而在某些地方未做檢查。而如果某指針變量先作為參數(shù)傳遞到了一個(gè)用戶函數(shù),之后再做了解引用,那么受限于過(guò)程間分析的能力,無(wú)法判斷傳遞到的函數(shù)是否是一個(gè)sanitizer,由此可能引入漏誤報(bào)。
對(duì)歷史漏洞編寫具有一定針對(duì)性的ql規(guī)則,在現(xiàn)實(shí)場(chǎng)景有特殊價(jià)值:
一方面,帶有漏洞的開源代碼,可能被以源碼形式包含在下游工程中,甚至是以代碼片段形式引用,但代碼結(jié)構(gòu)、符號(hào)命名可能存在重寫;而這種情況,一般沒(méi)有統(tǒng)一的代碼成分管理措施,這種陳舊代碼引入了歷史漏洞風(fēng)險(xiǎn),卻無(wú)法有效被意識(shí)到、檢測(cè)出來(lái);
一方面,某些漏洞的成因,可能在該項(xiàng)目的其它類似功能模塊中同樣存在,也可能在提供相似功能的其它開源項(xiàng)目里存在。此時(shí),對(duì)于這些漏洞,我們需要有具備一定泛化能力的規(guī)則來(lái)做掃描,避免先被其它黑客在獲取0day信息之后,率先舉一反三地挖掘到。
但既然是高針對(duì)性規(guī)則,必然與漏洞具有基本一比一的數(shù)量規(guī)模,所以自動(dòng)化生成,有必要性。
3. patch2ql:分析技術(shù)前瞻
patch2ql是云鼎實(shí)驗(yàn)室針對(duì)上述需要自動(dòng)化生成掃描歷史漏洞規(guī)則的問(wèn)題,以CodeQL作為示例性質(zhì)的SAST基底,給出的從代碼補(bǔ)丁(patch)自動(dòng)生成規(guī)則(ql)的技術(shù)方案。該技術(shù)當(dāng)前正在逐步完善和轉(zhuǎn)化中(由在途的專利申請(qǐng)保護(hù)),此處僅對(duì)其關(guān)鍵技術(shù)框架和節(jié)點(diǎn)做簡(jiǎn)要說(shuō)明。
3.1. 關(guān)鍵實(shí)現(xiàn)路徑
首先明確,嘗試用自動(dòng)化的方法,“理解”漏洞的成因,特別是語(yǔ)義、邏輯層面的原因,現(xiàn)階段跨度過(guò)大;合理的方式是,由patch的代碼增刪改,刻畫出語(yǔ)法層面的代碼上下文,尋找相似代碼上下文;這種相似性的大原則是:
具備patch中未改變的必備上下文語(yǔ)法要素,在上文例子中,主要包括特定類型的變量,對(duì)應(yīng)的初始化語(yǔ)句,if語(yǔ)句,對(duì)變量的解引用,次要包括具體解引用訪問(wèn)的成員變量名,當(dāng)前代碼塊上一層的代碼塊類型(如是for循環(huán)的body),當(dāng)前語(yǔ)句下一層的語(yǔ)句類型(如是break);
包含patch所去掉的代碼要素,包括刪除的以及修改的原始代碼,這在上文沒(méi)有體現(xiàn);
不包含patch新增的代碼要素,對(duì)應(yīng)上文中修復(fù)該處漏洞額外引入的NULL比較。
為實(shí)現(xiàn)以上目標(biāo),考慮包含三個(gè)關(guān)鍵步驟:
代碼要素的圈定,包括上述的三類要素??紤]到要有對(duì)代碼文本層面變動(dòng)的抗性,這里考慮從AST層面實(shí)現(xiàn)比對(duì);
根據(jù)所有主要和次要的AST節(jié)點(diǎn)要素,按照一定模式,生成對(duì)應(yīng)的查詢限定條件語(yǔ)句集合,主要有exist()語(yǔ)句限定需要包含的AST節(jié)點(diǎn),以及not exist()從句限定不應(yīng)存在的節(jié)點(diǎn);
從上述條件語(yǔ)句集合中組合條件形成ql,在目標(biāo)項(xiàng)目上做類似回歸測(cè)試,保證對(duì)目標(biāo)漏洞代碼上下文可檢出、盡量保證低誤報(bào)數(shù)量,反復(fù)測(cè)試從而整理得到最優(yōu)的集合。
3.2. AST層面的patch前后差異比對(duì)刻畫
選擇在AST層面圈定代碼差異,主要有兩方面考慮:一是AST是語(yǔ)法要素節(jié)點(diǎn)構(gòu)成的樹狀結(jié)構(gòu),可以有效做語(yǔ)法差異的比對(duì);二是由AST節(jié)點(diǎn)之間的“邊”,亦即語(yǔ)法調(diào)用關(guān)系,可直接串連為生成規(guī)則語(yǔ)句的對(duì)象。
針對(duì)AST這樣的樹形圖,常規(guī)文本序列形式的diff難以符合需求,而需要考慮到層次結(jié)構(gòu),做結(jié)構(gòu)化比對(duì)。我們改進(jìn)了編輯腳本生成算法,針對(duì)代碼特征,設(shè)計(jì)針對(duì)代碼顆粒度、自然的語(yǔ)法遷移關(guān)系的節(jié)點(diǎn)匹配條件,針對(duì)補(bǔ)丁前后目標(biāo)函數(shù)的代碼AST,分別掃描得到其中的插入、刪除、更新、移動(dòng)的語(yǔ)法樹節(jié)點(diǎn)和子樹。
舉例說(shuō)明,對(duì)于如下漏洞修復(fù)的代碼補(bǔ)丁:

可觀察到其中的關(guān)鍵修復(fù)邏輯,是針對(duì)非安全的緩沖區(qū)對(duì)象,增加額外NULL判斷,并使用安全的字符串操作函數(shù)。在AST層面使用算法,可得到差異的語(yǔ)法節(jié)點(diǎn):

3.3. 關(guān)鍵語(yǔ)法節(jié)點(diǎn)的查詢條件表述
之后的任務(wù)是,將打補(bǔ)丁前后,差異的語(yǔ)法節(jié)點(diǎn),以及未變動(dòng)的、對(duì)函數(shù)邏輯而言起關(guān)鍵標(biāo)識(shí)作用的語(yǔ)法節(jié)點(diǎn),“翻譯”為ql查詢規(guī)則中的查詢條件,從而獲得多維度、盡可能全面的查詢條件集合。
這樣的翻譯工作需要考慮到目標(biāo)語(yǔ)言的語(yǔ)法,特定節(jié)點(diǎn)關(guān)系,典型的補(bǔ)丁修補(bǔ)形式,以及必要的代碼上下文描述。例如,如果修補(bǔ)的代碼,是對(duì)一個(gè)if語(yǔ)句中的條件增加或改寫了條件謂詞,那么也需要對(duì)對(duì)應(yīng)的then語(yǔ)句塊做必要的查詢說(shuō)明,從而描述出來(lái)變更的條件判斷語(yǔ)句所“控制影響”的是對(duì)哪些關(guān)鍵變量的操作;又比如一個(gè)賦值語(yǔ)句的右值表達(dá)式中,將部分操作數(shù)做了更新,那么有必要進(jìn)一步描述整個(gè)右值操作數(shù)、被賦值的變量以及變量之后的使用場(chǎng)景(數(shù)據(jù)流)。
上一節(jié)的差異AST,經(jīng)此一步,轉(zhuǎn)化為如下的繁復(fù)的查詢語(yǔ)句集合(所顯示的為局部):

3.4. 查詢條件篩選與規(guī)則優(yōu)化
最終在以上的規(guī)則集合上做加工,選取必要的查詢對(duì)象和條件,合并冗余語(yǔ)句,去除有沖突和錯(cuò)誤的條件,并將某些對(duì)象根據(jù)語(yǔ)法關(guān)聯(lián)整合得到抽象的語(yǔ)法對(duì)象描述,從而可以得到兩類查詢規(guī)則:
一類精準(zhǔn)匹配目標(biāo)唯一漏洞,描述所有必要上下文和代碼語(yǔ)法,以檢測(cè)存在適度的代碼變形的原始?xì)v史漏洞為目標(biāo),即準(zhǔn)確規(guī)則;
一類嘗試通過(guò)忽略部分查詢條件,并對(duì)某些查詢條件做泛化(如從準(zhǔn)確判斷目標(biāo)變量類型,調(diào)整為描述目標(biāo)類類型的父類型,或忽略類型、直接描述其訪問(wèn)的成員),從而得到可能允許有一定程度的“誤報(bào)”,但具備能夠檢測(cè)同源、同語(yǔ)法漏洞類型的衍生、相似漏洞的能力,即泛化規(guī)則。
這樣得到的一份最精簡(jiǎn)優(yōu)化規(guī)則如下:

4. 典型案例
借助CodeQL本地測(cè)試環(huán)境,可以利用patch2ql工具生成的特定項(xiàng)目的規(guī)則,方便地掃描檢測(cè)某些與該項(xiàng)目存在潛在關(guān)聯(lián)性的開源工程是否有引入性質(zhì)的同源漏洞。
值得注意的是:由CodeQL的許可限制,工具僅可以用于對(duì)開源代碼的掃描,請(qǐng)勿用于商業(yè)私有代碼的掃描和CI/CD流程的集成;此外,此前曾有的LGTM.com網(wǎng)站提供了一套在線編寫規(guī)則掃描已預(yù)置編譯后代碼數(shù)據(jù)庫(kù)的平臺(tái),可以不必本地重新構(gòu)建數(shù)據(jù)庫(kù),但該網(wǎng)站即將下線停止服務(wù),因此規(guī)則的試用復(fù)現(xiàn)也請(qǐng)遵循指引,搭建CodeQL的本地環(huán)境。
4.1. 案例說(shuō)明:子項(xiàng)目級(jí)靜態(tài)包含掃描
分析中首先選取C/C++項(xiàng)目中,將其它開源代碼某個(gè)快照版本靜態(tài)包含,并在生成時(shí)編譯為靜態(tài)庫(kù)或直接.o形式鏈接引用的開源項(xiàng)目。一個(gè)很好的例子是CMake,其中在Utilities子目錄下附帶二方開源代碼,基本進(jìn)行代碼剪裁后添加“cm”前綴存在,并由開發(fā)者做版本的同步和維護(hù)。
其中存在有“cmcurl”目錄為靜態(tài)包含的curl,剪裁僅保留了libcurl共享庫(kù)部分代碼和編譯邏輯;由提交歷史分析,CMake開發(fā)者有一定的適配定制,采取不定期人工從上游(upstream)項(xiàng)目合并變動(dòng)的同步策略,最后一次同步為2022.05.16。根據(jù)curlver.h頭文件指示,同步的libcurl版本為7.83.1。
使用一款具有函數(shù)/片段顆粒度相似度和版本判斷的軟件成分分析商業(yè)軟件FossID,對(duì)最新的CMake進(jìn)行掃描,掃描共耗時(shí)約2小時(shí)??梢宰R(shí)別到curl的代碼成分,選取其中一個(gè)cmcurl目錄下文件,掃描成分判定結(jié)果如下:

由此可見(jiàn),該商業(yè)方案確實(shí)是采用按片段相似度確定與某些歷史版本的最大似然成分的。但可能由于知識(shí)庫(kù)(KB)難以持續(xù)、細(xì)粒度更新,參與到相似度判定的代碼片段也并不完整、缺少一定的模糊性,導(dǎo)致判定出的最大似然度的版本也只有1.7%的相似成分比例。而漏洞的提示完全是根據(jù)判定的歷史版本號(hào),給出對(duì)應(yīng)知識(shí)庫(kù)內(nèi)容,與實(shí)際命中代碼無(wú)關(guān)。
而使用patch2ql,針對(duì)某個(gè)歷史漏洞patch,訓(xùn)練生成ql的局部對(duì)應(yīng)關(guān)系如下:
該份ql規(guī)則存在一定量冗余,但可觀察到其中描述了差異的if語(yǔ)句條件元素,以及對(duì)應(yīng)的then分支語(yǔ)句。使用該規(guī)則,可以掃描分析得出,CMake當(dāng)中包含的cmcurl代碼,存在該漏洞的同源(遺留)漏洞:

【網(wǎng)絡(luò)安全學(xué)習(xí)資料領(lǐng)取】

5. 如何獲取
當(dāng)前patch2ql工具本身仍然在持續(xù)開發(fā)演進(jìn)中,功能本身計(jì)劃將在進(jìn)一步成熟后考慮開源。
相比工具本身,該工具生成的規(guī)則本身,能更好地服務(wù)開發(fā)者以及關(guān)注安全的白帽子。因此,patch2ql當(dāng)前的中遠(yuǎn)期目標(biāo),是針對(duì)開源生態(tài)中的基礎(chǔ)開源軟件和組件,生成盡可能覆蓋所有歷史CVE漏洞和bug的分析規(guī)則。
當(dāng)前已開放針對(duì)curl和OpenSSL部分歷史漏洞生成的原始CodeQL規(guī)則集,感興趣的白帽子可移步騰訊云鼎實(shí)驗(yàn)室該項(xiàng)目(https://github.com/YunDingLab/QlRules.git)查看分析。