鳳凰科技 2024-11-03 01:31:52 13
LLM居然在真實世界的程式碼中,發現了一個漏洞?
想象一下,AI正在默默地守護著我們日常使用的軟體。忽然,它發現了一個你我可能從未察覺的安全隱患,並且悄無聲息地把它修復了!
就在剛剛,谷歌的Big Sleep專案揭示了一個驚人的成果:一個真實世界的安全漏洞,出現在全球廣泛使用的SQLite資料庫中,而這個漏洞竟然被AI成功識別出來了?在真實世界的危機擴散之前,它及時挽回了局面。
隸屬於谷歌Project Zero和Google DeepMind的團隊聲稱,這是AI Agent在廣泛使用的現實軟體中,發現未知可利用記憶體安全問題的第一個公開示例。
要知道,這不僅僅是一個崩潰的測試用例,它是AI首次在真實世界的軟體中找到未知的、可利用的記憶體漏洞。
此前,網路安全巨頭CrowdStrike鬧出的一個由「C-00000291*.sys」配置檔案觸發的系統邏輯錯誤,瞬間就破壞掉全世界約10億臺計算機,直接導致微軟藍色畫面、全球停擺。
如果未來某一天,AI能幫我們解決所有技術領域的單點瞬時故障,不知會幫人類節省下多少財富?
用LLM在真實世界中「捉蟲」
隨著LLM程式碼理解和一般推理能力的提高,谷歌研究者一直在探索這些模型如何在識別和演示安全漏洞時,重新人類安全研究人員的方法。
在《Project Naptime:評估大型語言模型的攻防能力》中,Big Sleep團隊介紹了一個利用LLM輔助的漏洞研究框架,並透過在Meta的CyberSecEval2基準測試上提升了最新的效能,展示了這種方法的潛力。
從那時起,Naptime就變成「Big Sleep」,成為了Google Project Zero與Google DeepMind的合作專案。
就在剛剛,谷歌研究者激動地表示,Big Sleep Agent發現了首個真實世界漏洞:一個存在於SQLite中的可利用棧緩衝區下溢漏洞。
SQLite是一款被廣泛使用的開源資料庫引擎。
在十月初,Agent發現了了這個漏洞,於是谷歌研究者立刻將其報告給了開發者,他們在同一天進行了修復。
幸運的是,AI在這個問題出現在官方釋出版本之前,就發現了它,因此SQLite的使用者未受影響。
要知道,SQLite作為輕量級嵌入式資料庫,廣泛應用於智慧手機、瀏覽器、嵌入式系統、IoT裝置等多種環境,涵蓋了許多使用者和敏感資訊。
如果攻擊者利用該漏洞進行資料洩露、系統入侵或破壞,潛在損失金額可能少則幾百萬,多則數十億美元!
谷歌研究者表示,這是AI Agent首次在廣泛使用的真實世界軟體中發現未知的、可利用的記憶體安全問題的公開案例。
之所以會有這次嘗試,是因為今年早些時候,在DARPA的AIxCC活動中,亞特蘭大團隊在SQLite中發現了一個空指標取消引用的漏洞,這就給了谷歌研究者啟發——
是否可以使用SQLite進行測試,看看能否找到更嚴重的漏洞呢?
果然,AI Agent真的找出了一個漏洞。
這項工作,無疑具有巨大的潛力。
在軟體尚未釋出前就發現漏洞,就意味著攻擊者沒有機會利用:漏洞在他們有機會使用之前,就已被修復。
雖然模糊測試也能帶來顯著的幫助,但我們更需要的是一種方法,幫助防禦者找到那些很難透過模糊測試發現的漏洞。
現在,AI有望縮小這一差距!
谷歌研究者表示,這是一條有希望的道路,能為防禦者帶來不對稱的優勢。
因為這個漏洞相當有趣,而且SQLite的現有測試基礎設施(包括OSS-Fuzz和專案自身的測試)並沒有發現它,因此谷歌研究者進行了深入調查。
方法架構
Naptime和Big Sleep專案的關鍵驅動因素,就是已經發現並修補的漏洞變種,仍在現實中不斷被發現。
顯而易見,fuzzing(模糊測試)並不能成功捕獲此類變種漏洞,而對攻擊者而言,手動變種分析的方法仍然價效比很高。
谷歌研究者認為,相比更為寬泛的開放式漏洞研究問題,這種變種分析任務更適合當前的LLM。
透過提供一個具體的起點——比如此前修復的漏洞的詳細資訊——我們就可以降低漏洞研究中的不確定性, 並且還能從一個明確的、有理論支撐的假設出發:「這裡曾經存在一個漏洞,很可能在某處還存在類似的問題」。
目前,他們的專案仍處於研究階段,正在使用帶有已知漏洞的小型程式來評估研究進展。
最近,他們決定透過在SQLite上開展首次大規模的真實環境變種分析實驗,來測試他們的模型和工具鏈。
他們收集了SQLite repository近期的一系列提交,手動篩除了無關緊要的改動和純文件更新。
隨後,他們調整了prompt,為AI Agent同時提供了提交資訊和程式碼變更,並要求它審查當前程式碼庫(在HEAD位置)中可能仍未修復的相關問題。
Project Naptime
Naptime採用了一種專門的架構來增強大語言模型進行漏洞研究的能力,其核心是AI Agent與目的碼庫之間的互動。
系統架構
為了讓AI Agent可以模仿人類安全研究員的工作流程,研究團隊開發了一系列專用的工具:
程式碼瀏覽工具(Code Browser)使AI Agent能夠瀏覽目的碼庫,這與工程師使用Chromium Code Search的方式類似。它提供了檢視特定實體(如函式、變數等)原始碼的功能,並能識別函式或實體被引用的位置。
Python工具讓AI Agent能夠在隔離的沙盒(Sandbox)環境中執行Python指令碼,用於執行中間計算並生成精確而複雜的目標程式輸入。
偵錯程式工具(Debugger)為AI Agent提供了程式互動能力,可以觀察程式在不同輸入下的行為表現。它支援斷點設定並能在斷點處評估表示式,從而實現動態分析。
報告工具(Reporter)為AI Agent提供了一個結構化的進度通報機制。AI Agent可以傳送任務完成訊號,觸發控制器驗證是否達成成功條件(通常表現為程式崩潰)。當無法取得進一步進展時,它還允許AI Agent主動中止任務,避免陷入停滯狀態。
發現漏洞
這個漏洞非常有趣,比如在一個通常為索引型別的欄位iColumn中,使用了一個特殊的哨兵值-1:
7476: struct sqlite3_index_constraint {
7477: int iColumn; /* Column constrained. -1 for ROWID */
7478: unsigned char op; /* Constraint operator */
7479: unsigned char usable; /* True if this constraint is usable */
7480: int iTermOffset; /* Used internally - xBestIndex should ignore */
7481: } *aConstraint; /* Table of WHERE clause constraints */
這種模式產生了一個邊緣案例,所有使用該欄位的程式碼都需要正確處理這種情況,因為按照常規預期,有效的列索引值應該是非負的。
seriesBestIndex函式在處理這個edge case時存在缺陷,當處理包含rowid列約束的查詢時,導致寫入了帶有負索引的堆疊緩衝區。
在研究者提供給AI Agent的編譯版本中,debug assertion功能已啟用,這種異常情況會被第706行的斷言檢查所捕獲:
619 static int seriesBestIndex(
620 sqlite3_vtab *pVTab,
621 sqlite3_index_info *pIdxInfo
622 ){
...
630 int aIdx[7]; /* Constraints on start, stop, step, LIMIT, OFFSET,
631 ** and value. aIdx[5] covers value=, value>=, and
632 ** value>, aIdx[6] covers value<= and value< */
633 const struct sqlite3_index_constraint *pConstraint;
...
642 for(i=0; inConstraint; i++, pConstraint++){
643 int iCol; /* 0 for start, 1 for stop, 2 for step */
644 int iMask; /* bitmask for those column */
645 int op = pConstraint->op;
...
705 iCol = pConstraint->iColumn - SERIES_COLUMN_START;
706 assert( iCol>=0 && iCol<=2 );
707 iMask = 1 << iCol>
...
713 if( pConstraint->usable==0 ){
714 unusableMask |= iMask;
715 continue;
716 }else if( op==SQLITE_INDEX_CONSTRAINT_EQ ){
717 idxNum |= iMask;
718 aIdx[iCol] = i;
719 }
720 }
然而,在釋出版本中,這個斷言檢查並不存在。
在研究者的測試環境中(具體表現會因編譯器和最佳化級別而異),第718行的後續寫入操作會越界寫入aIdx緩衝區下方的記憶體區域,導致pConstraint指標的最低有效32位被破壞。
當這個被破壞的指標在迴圈的下一次迭代中被取消引用時,就會產生潛在的可利用漏洞條件。
不過,即使有了這樣的漏洞說明,對於人類研究員來說,要精確理解如何觸發這個漏洞仍然不易。
雖然針對ROWID列設定約束顯然是個不錯的切入點,但要完全理解,還需要深入研讀程式碼。
而 AI 智慧體似乎已經掌握了比人類研究員更多的SQLite相關知識,這使它能夠更高效地定位問題!
這種情況的一個典型例子,就是LLM在生成測試用例時會直接使用generate_series虛擬表。
(雖然研究者也觀察到模型會先研究可用的虛擬表的情況,但很明顯,它能夠在這裡直接運用已有的知識)。
執行過程重點分析
在這次基於Gemini 1.5 Pro的成功測試中,seed commit為 [1976c3f7],這是一個規模較大、複雜的程式碼變更。
而研究者的AI Agent發現的漏洞,與該seed commit中的變更僅存在鬆散關聯——這種情況在手動變種分析中較為常見,因為在理解程式碼庫中的某個漏洞時,研究人員往往會由此發現其他潛在問題。
以下是關鍵節點摘要,其中,所有ASSISTANT塊中的內容均直接來自AI Agent。
AI Agent對差異中的變化進行了合理的評估,並以此作為研究的切入點:
ASSISTANT
透過引入termFromWhereClause函式,CL改進了在虛擬表查詢中處理OR分支共享的WHERE條件的方式。該函式在考慮巢狀子句的情況下,從WhereClause中檢索特定的條件。
在此更改之前,由於條件索引對映不正確,共享的條件可能未被正確處理,可能導致低效的查詢計劃或錯誤的結果。
最可能出現的問題涉及在allocateIndexInfo和whereLoopAddVirtualOne函式中不正確地處理WHERE子句約束,其中 CL 修改瞭如何使用新的輔助函式來處理約束偏移量。
我將首先檢查allocateIndexInfo函式。
在經過數個步驟探索相關程式碼後,AI Agent形成了一個更明確的測試假設——它試圖透過執行patch中測試用例的修改版本來驗證這個假設,但未能成功。
原因在於patch中的測試用例依賴於一個在當前配置環境中未啟用的擴充套件模組:
TOOL(debugger_run )
程式在未崩潰或命中斷點的情況下退出。退出程式碼為1。
STDERR輸出為:
Runtime error near line 2: no such module: tcl
Parse error near line 3: no such table: x1
ASSISTANT
程式終止是因為TCL模組不可用。測試依賴於TCL虛擬表模組,需要在編譯SQLite時啟用該模組。提供的程式的輸入基於bestindexC.test檔案,它使用TCL模組來測試虛擬表的最佳索引選擇。
為了解決這個問題,需要更改程式輸入,使用不需要TCL模組的其他虛擬表。然而,該漏洞與虛擬表的查詢計劃有關,所以我們仍然需要一個虛擬表來觸發此問題。
我將使用code_browser_source工具來檢索SQLite中的內建虛擬表示例。然後,我將調整輸入,使用其中一個虛擬表,並重新在whereLoopAddVirtualOne中設定斷點。
AI Agent很好地應對了這個問題,準確識別出了修復測試用例所需的各個步驟,並在後續幾個步驟中成功收集到了所有構建測試用例所需的資訊,最終實現了觸達whereLoopAddVirtualOne的目標:
ASSISTANT
sqlite3_create_module函式用於註冊虛擬表模組。shell.c檔案包含了多個這樣的例項,包括generate_series、fsdir、completion和expert模組。其中json表的實現也很值得關注。
我首先關注的是generate_series,然後構造出一個使用該模組的查詢,從而觸發錯誤的約束處理。
需要注意的是,在最初發現和復現該漏洞時,AI Agent對於它試圖觸發的「不正確約束處理」的描述相當模糊。
在這種情況下,完整的執行軌跡讀起來頗似人類研究員的思維過程——從一個想法開始,在調查過程中產生其他幾個想法,最終生成了一個可以復現略有不同漏洞的測試用例。
在本例中,當whereLoopAddVirtualOne在generate_series表上被呼叫時,執行流程最終會進入seriesBestIndex,在那裡約束確實被錯誤處理了。
特別有趣的是,一旦程式崩潰,模型會在解釋問題時表現得更為出色——在生成根因分析(root-cause analysis)和完整崩潰報告時,我們可以看到AI Agent清晰地構造了一個測試用例來觸發特定的已識別邊緣案例:
ASSISTANT
相關的欄位是sqlite3_index_constraint結構體中的iColumn。該欄位表示約束的列編號。要導致斷言失敗,我們需要對索引大於3或小於-1的列設定約束。
讓我們構建一個涉及這些列約束的SQL查詢。generate_series表有名為value、start、step和stop的列。它們的索引是:
- value: 0
- start: 1
- step: 2
- stop:3
要觸發此漏洞,我們可以對ROWID施加一個約束,並對ROWID的約束使用iColumn = -1。
示例查詢如下:
SELECT * FROM generate_series(1,10,1) WHERE ROWID = 1;
該查詢將導致seriesBestIndex函式崩潰。
最終,得到了一個清晰準確的問題總結,幾乎可以直接用於漏洞報告!
TOOL(debugger_run)
程式崩潰:SIGABRT at :0
STDERR輸出為:
sqlite3: src/shell.c:6816: int seriesBestIndex(sqlite3_vtab *, sqlite3_index_info *): Assertion `iCol>=0 && iCol<=2' failed.
ASSISTANT(report_success)
程式在執行SELECT * FROM generate_series(1,10,1) WHERE ROWID = 1;時因seriesBestIndex函式中的斷言失敗而崩潰。
斷言assert(iCol>=0 && iCol<=2)失敗的原因是sqlite3_index_info結構體中的iColumn欄位可能包含超出列索引有效範圍的值,例如在約束涉及ROWID時為-1。
這導致iCol被計算為-2,從而導致斷言失敗。
關於模糊測試
一個顯而易見的問題是:為什麼傳統的模糊測試沒有更早發現這個漏洞?
答案就在模糊測試工具鏈的配置上。
OSS-Fuzz使用的工具並沒有啟用generate_series擴充套件,而替代的fuzzingshell.c工具包含的是舊版本的seriesBestIndex函式,未受此漏洞影響。
雖然SQLite AFL倉庫中包含一個針對研究者提供給Big Sleep智慧體的、相同CLI二進位制檔案的模糊測試配置,但似乎並未被廣泛使用。
這個漏洞是否真的容易發現?
為此,研究者嘗試透過模糊測試重新發現它。
他們遵循SQLite文件中的模糊測試說明,並使用CLI目標。在啟動AFL執行之前,他們還驗證了模糊測試語料庫中包含所需的generate_series和rowid關鍵字。
然而,經過150個CPU小時的模糊測試,問題仍未被發現。
隨後,他們嘗試透過將必要的關鍵字新增到AFL的SQL字典中,來簡化模糊測試的任務。
然而,似乎只有當語料庫包含與導致崩潰的輸入非常接近的示例時,漏洞才能被快速發現,因為程式碼覆蓋率對這個特定問題並不是可靠的指標。
誠然,AFL並不是針對像SQL這種基於文字的格式最適合的工具,大多數輸入在語法上無效,會被解析器拒絕。
然而,如果將這一結果與Michal Zalewski在2015年關於模糊測試SQLite的部落格文章進行比較,會發現十分有趣的事。
那時,AFL在發現SQLite漏洞方面相當有效;經過多年的模糊測試,該工具似乎已經達到自然的飽和點。
雖然研究者迄今為止的結果與AFL釋出時帶來的顯著效果相比顯得微不足道,但它有自己的優勢——有機率能夠有效地發現一組不同的漏洞。
結論
對於團隊來說,這個專案無疑成功了。
在廣泛使用且模糊化的開源專案中找到漏洞,非常一個令人興奮!
這也就意味著:當提供正確的工具時,當前的LLMs可以進行漏洞研究。
不過,研究者想重申,這些都是高度實驗性的結果。
Big Sleep 團隊表示:目前,在發現漏洞方面,針對特定目標的模糊器可能至少同樣有效。
研究者希望,未來這項工作將為防禦者帶來顯著優勢——
不僅有可能找到導致崩潰的測試用例,還能提供高質量的根本原因分析,使得問題的分類和修復變得更便宜且更有效。
谷歌研究者表示,會繼續分享自己的研究成果,儘可能縮小公共技術前沿和私有技術前沿之間的差距。
Big Sleep團隊也會將繼續努力,推進零日計劃的使命,讓0-day變得更加困難。
團隊介紹
Dan Zheng
團隊中唯一的華人Dan Zheng是谷歌DeepMind的研究工程師,從事程式碼和軟體工程的機器學習,以及程式語言的研究。
此前,他曾參與Swift for TensorFlow的工作,專注於Swift中的可微分程式設計。
他在普渡大學獲得了電腦科學專業的學士學位。畢業後,他做了多年的學生研究員,期間研究成果頗豐。