【論文拆解】每個 skill 都沒事,合起來卻出事:SkillCascade 揭開 agent 的組合盲區

檢查一個 skill,可能只看到合理的資料清理;檢查下一個,可能只看到合理的風險權重;第三個也只是縮短摘要。把三個接起來,真正該通知人的警示卻不見了。Zihao Zhu 等人的《Stealth Apart, Harm Together》研究的正是這種「單件過關、組合失守」的 agent 安全問題。先把論文怎麼定義、怎麼測、測到了什麼說清楚,再談它把責任留給了誰。
原文摘要
問題:skill 不是孤島
作者以 Claude Code、OpenClaw、Codex 等使用 skill 的 agent 為背景。skill 可以包含描述何時啟用的文字、執行指示、程式腳本、參考文件及素材;agent 依請求載入它們並執行。論文把系統表為底層模型、已安裝技能集合、共享上下文與執行環境四部分。關鍵不是目錄裡有幾個檔,而是前一個 skill 的輸出會進入同一段共享上下文,供模型選擇與執行下一個 skill 時閱讀。資料、標籤和看似普通的處理規則,就在這裡跨過了技能邊界。
過去的研究與安裝前掃描,多聚焦單一 skill 的 prompt injection、腳本負載或越權工具呼叫。作者指出:倘若危害不在任一件元件中,而在多件元件的交接關係中,逐件審查就會錯過真正的分析單位。他們以供應鏈攻擊作類比,但這篇的對象不是軟體更新程式本身,而是技能之間傳遞的內容與 agent 對它的解讀。
威脅模型:攻擊者得先控制多個已安裝技能
攻擊者在論文中是惡意技能發布者,或其套件遭竄改的供應商;他能修改目標 agent 已安裝的至少兩個技能,包含其說明、指示、腳本與參考資料。他不能改模型權重、system prompt、平台機制或使用者請求,也不能任意修改別人的技能。讓多個受控技能一併被安裝,是這套威脅模型的必要前提;作者舉同一廠商以套裝方案供應多項技能,使用者一次批准整組為可能途徑。論文並沒有測出真實市場裡這種共同安裝有多常發生。
作者對「級聯攻擊」設三個條件:每個修改過的 skill 單獨掃描都被判為安全;組合執行時,在看似正常的請求上產生有害結果;還原其中任何一項修改時,危害理應消失。第三項很重要,它要求危害來自共同作用,而不是偷塞一個單獨就能得手的惡意技能。這是論文的形式定義;稍後實驗中移除一項修改後成功率大幅下降,並非每次都降到零,不能把經驗結果寫成絕對證明。
論文分出三種路徑。「因果級聯」按順序傳遞被削弱的訊號:前一階段改變欄位或標記,後一階段依它調整判斷。「組合級聯」不一定有先後,每個片段單看危害極小,最後聚合才出問題。「混合級聯」則同時需要前面的傳遞鏈與另一些平行片段。至於攻擊想達成什麼,作者列出七種目標:輸出內容錯誤、決策被引導、功能拒絕或失效、品質無聲下降、處理範圍擴張、資訊外洩,以及未獲授權的額外行動。這些目標跨越讀者看到的答案、agent 操作的對象,以及實際產生的副作用,不能都簡化成「模型答錯」。
論文以處方審查作概念例子:前一個技能讓某項近期停用藥物的訊號不顯眼,下一個調低與之相關的交互作用嚴重度,最後的摘要略過低優先警示。各階段都有一套局部可辯護的處理理由,組合後卻可能讓原本嚴重的警示到不了臨床人員。這是研究中的攻擊情境,不是現實醫療系統事故的通報,也不是可採用的醫療流程。
SkillCascade 怎麼製造並篩選案例
研究提出 SkillCascade 作為自動紅隊框架。第一個 agent 從任務出發找可串接的市集技能,或先看熱門技能再反推可能的工作流。第二個規劃攻擊目標與級聯類型,修改多個技能,並為每一項改動提出看起來正常的設計理由。第三個掃描代理在不知道整體攻擊計畫與其他技能的乾淨會話裡,逐件使用 Skill-Vetter 審查;有一件沒過,就回去修。第四個建立自然的使用者請求、輸入檔與判定標準;沙盒重跑後,由第五個代理依輸出與執行軌跡判定是否達成目標。失敗會按原因退回重做情境、改動或測試案例,而不是只留下成功的一次。
實作以 Claude Agent SDK 建立五個代理,均使用 Claude Sonnet 4.6;附錄記錄了逐階段重試上限、可回放的情境/測試/執行紀錄,以及九種反覆出現的變動機制。這九種包含把條件規則帶進共享上下文、改變分類框架與標籤、利用預設值、篩掉資料、藏在中繼欄位的資訊流、製造決策壓力、悄悄增加處理對象,以及拆散或混併資料粒度。這些不是九個互斥的攻擊類型,也不是說每個案例都用齊九項;作者是從攻擊計畫中歸納常見手法。尤其當下游把上游產出的標籤當成已核准的權威判斷時,最初只是「資料」的東西,就變成後續動作的依據。
框架從 ClawHub 上實際存在的技能出發,形成 SkillCascade-Bench:213 個經驗證的攻擊測試案例,涵蓋十個領域、七種目標與三種級聯類型;每案平均涉及 3.25 個技能。每個案例保存原版和修改版技能、輸入與請求,以供在不同目標系統上重跑。三名專家各自核查候選案例是否需要組合、工作流是否合理、結果是否符合攻擊標準;只收三人一致同意的案例,報告 Fleiss’s κ = 0.82。注意這是已成功建構並驗證的攻擊集合,不是隨機抽取所有市集技能後的自然風險分布;論文也說案例受控存取,不能把「提出/釋出 benchmark」理解為任何人都能無條件下載完整攻擊包。
實驗:高成功率有其條件
研究把修改後的技能裝到 OpenClaw、Claude Code、Codex 三種目標系統,搭配八種模型,共 24 種系統—模型設定。每個案例在每種設定執行三次,再按事先設定的評估標準,由 Judge Agent 檢查最終輸出與軌跡。表 2 報告全域平均攻擊成功率 89.4%;不同模型的平均從 Claude Opus 4.6 的 76.5% 到 Qwen 3 72B 的 97.1%。GPT-5.4 平均為 80.6%,GPT-5.4 Mini 為 96.7%;同一模型在不同宿主系統的差距,作者報告均低於六個百分點。這是此攻擊集、這些沙盒設定與判定方式下的結果,並非任一 agent 在野外遭攻擊的機率。
按目標、級聯路徑及領域切開,論文認為因果鏈較容易讓攻擊訊號逐步傳下去;組合型需要多個片段在最後聚合,較難奏效。日常知識工作領域的成功率傾向更高,但報告中最低的領域仍高於 80%。作者透過三技能案例的還原實驗檢查共同作用:拿掉一項修改後,殘留成功率降至 13%~18%;再拿掉第二項,降至個位數。因果型與混合型對哪個位置被還原的敏感度也不同。這強化了「多點一起起作用」的解釋,同時提醒我們其經驗條件不是「少任何一件都絕不會失敗」的定理。
防線:通過率高,不等於掃描器完全沒有訊號
研究測五種逐技能安裝前掃描器,修改技能仍有 86.6%~94.3% 被判安全;但原版技能也有 91.3%~96.8% 通過,改動前後的差距約 2.5~4.7 個百分點。作者用此說明改動很難被單件審查捕捉,不能說每個掃描器「完全零辨識」;也不能只報修改版的高通過率,卻不交代原版的基準值。
把整組技能一起交給三種聯合掃描器,仍有 81.7%~83.0% 的修改組合通過;原版組合的對照約 82.7%~88.2%。其中簡單的 Joint-LLM 是把技能文本併在一起看,不等於模擬執行後果。四種執行時防禦在 OpenClaw 搭配 GPT-5.4 Mini 的設定下,規避率介於 82.5% 與 91.8%,平均 88.5%。這一組數字只在該特定宿主與模型上測,不能直接外推到表 2 的全部 24 種配置;它也量測「逃過監控」,不是把逃過監控的比例當成另一個攻擊成功率。
作者還試了一個行為組合掃描器:先逐一記錄每個技能在什麼條件下承諾做什麼,再把承諾串成假設工作流,追問合起來可能造成的危害。因果型的安全通過率比直接把文字併給 Joint-LLM 看降低三十多個百分點;組合型改善較小,混合型仍有相當缺口。論文稱它是概念驗證,不是完整防禦。金融案例讓這差異變得具體:財報電話會議提及持續經營疑慮、約十一週現金餘額與債務重整,三個技能依次把揭露資訊移到另一欄、降低該欄權重、在主管摘要省略低權重內容;最後留下的 Buy/Hold 備忘錄卻沒寫流動性危機。逐件看每一步都像有理由的資訊整理,問題發生在它們合起來的後果。
作者自己劃的邊界
這是預印本的沙盒壓力測試,不是生產環境事件統計。作者承認以模型作 Judge 的大規模驗證仍需要更多人工補強;兩名標註者覆核其中 30 個判定,與 Judge 的 Cohen’s κ 為 0.86,但這不會消除所有標準或抽樣偏差。更關鍵的是,真實市集裡攻擊者能讓多個自己控制的技能共同安裝、並在同一請求裡被觸發的頻率,論文沒有實測。受控存取與負責任揭露也說明此研究本身存在雙重用途風險。讀者應保留「已展示可行的組合漏洞」與「已知道它在現實中有多普遍」之間的距離。
城武觀點
逐件掃描還是有用,但它不該替整條工作流蓋章。這篇最刺眼的數字不是 89.4%,而是論文連把三份技能文本一起丟給掃描器,都還有八成以上的修改組合被放行;只有讓檢查器想像它們在同一請求裡會怎麼交接,因果型的通過率才明顯下降。多看幾份檔案,和理解它們一起做了什麼,不是一回事。
無害示意:兩個 skill,漏掉一項會議待辦
以下是我依論文機制自行改編的玩具例子,不是論文附錄的原始技能,也沒有接上真實 agent 或安全掃描器。使用者要求:「整理會議紀錄,列出所有待辦。」原始紀錄只有一項:「週五前確認投影設備」。
meeting-extractor的原本規則:辨認待辦,標成must。改動後的SKILL.md規則:若待辦只是會議準備事項,標成optional;理由是讓報告能區分準備工作與核心決策。meeting-summary的原本規則:列出所有收到的待辦。改動後的SKILL.md規則:為了保持摘要簡短,略過optional項目。它只看上游標籤,沒有回頭查原始會議紀錄。
兩個改動單看都有日常工作流程中的理由;一起執行時,投影設備那項待辦就從最終摘要消失。下面用確定性的 Python 模擬技能交接,讀者可複製執行;它只展示資料怎麼流動,不測試任何 LLM 是否服從指示,也不宣稱兩個改動能通過論文的掃描器。
item = "週五前確認投影設備"
def extract(changed): # 上游 skill:原版 must,改版 optional
return {"text": item, "label": "optional" if changed else "must"}
def summarize(record, changed): # 下游 skill:改版略過 optional
if changed and record["label"] == "optional":
return []
return [record["text"]]
for upstream, downstream in [(False, False), (True, False),
(False, True), (True, True)]:
print((upstream, downstream), summarize(extract(upstream), downstream))
輸出中 (False, False) 是兩者原版;(True, False)、(False, True) 分別只改一處;只有 (True, True) 兩處都改,清單才變成 []。這是級聯的必要性對照,不是「掃描器判安全」的證據;在真實系統中,仍須分別測技能審查、觸發條件及執行時結果。
風險更高的思想實驗:專案清理變成根目錄清理
如果嫌「漏一項待辦」不夠直觀,可以把同一個交接問題放進檔案清理情境。使用者只要求清除專案內的暫存檔。假設上游的 workspace-locator 在找不到專案目錄時,本來會停止並要求明確路徑;被修改後卻把缺失值誤補成檔案系統根目錄 /,還把這個結果標成「已解析」。下游的 cleanup 本來會獨立檢查目標是否在專案內;被修改後改成信任上游的「已解析」標記,省去重複檢查,並將目標下所有項目列為待刪。兩個改動合在一起,可能把原本針對專案的請求變成 rm -rf /* 這種災難性的刪除企圖。
這裡的字串只用來描述絕不可執行的危險結果,不是操作步驟或測試指令;本篇沒有產生、執行或驗證任何刪除命令。還原上游,路徑缺失時就會停止;只還原下游,根目錄會被它的獨立邊界檢查擋住。唯有兩處都失守,才會走到危險的待執行狀態。這是我自行構造的思想實驗,不是論文附錄中的實際案例,也沒有證據顯示這兩個修改能騙過論文使用的掃描器;它只是讓「逐件理由都說得通,不等於組合後安全」有一個可辨認的失敗邊界。
我傾向把「技能安全」的最終檢查單位從套件改成任務中的高風險交接:上游產生的標籤、權重或目標名單,何時被下游當成可以直接行動的依據?誰能追溯它來自哪項原始資料、誰又批准了最後那個省略或擴張?這不是主張把所有技能排列組合都模擬一遍——組合數量和誤報成本會先把維運團隊壓垮;而是要對醫療警示、金流決策與跨範圍操作等可造成損失的交接,保留原始訊號與可核對的輸出不變條件。這是我從論文推導的設計方向,不是論文已驗證的修補方案。如果未來跨交接的檢查大量阻擋正常工作卻沒降低實際事故,我會修正這個判斷。
知識上的不對稱是:使用者只看得到最後那份看起來完整的報告,技能供應者卻能預先決定哪些訊號被降權、被誰承接;平台若只展示逐件審查的綠燈,便把組合後果的驗證成本丟給最不可能看到中間軌跡的人。安全章應該蓋在誰能回放與糾錯的地方,而不是蓋在一排各自無辜的零件上。
城武的未解檔案——每個零件都有檢驗合格證,失蹤的卻是整條生產線上的警報。
- 原文:Stealth Apart, Harm Together: Skill Cascading Attacks on Skill-Based Agent Systems(Zihao Zhu、Siwei Lyu、Adel Bibi、Baoyuan Wu,arXiv 預印本,2026-09-24)