在過去的一週(2026/04/20 - 2026/04/26)中,BlockSec 監測並分析了八起攻擊事件,總估計損失約為 704 萬美元。下表總結了這些事件,後續章節則提供了每個案例的詳細分析。
| 日期 | 事件 | 類型 | 估計損失 |
|---|---|---|---|
| 2026/04/19* | 自定義再平衡合約 (Custom Rebalancer Contract) | 任意呼叫 (Arbitrary Call) | 約 6.4 萬美元 |
| 2026/04/20 | REVLoans (Juicebox) | 不當驗證 (Improper Validation) | 約 5.07 萬美元 |
| 2026/04/22 | Volo Vault / Navi | 金鑰洩漏 (Key Compromise) | 約 350 萬美元 |
| 2026/04/22 | Kipseli Router | 不當驗證 (Improper Validation) | 約 7.235 萬美元 |
| 2026/04/23 | GiddyDefi | 不完整的簽名驗證 (Incomplete Signature Validation) | 約 130 萬美元 |
| 2026/04/25 | Purrlend | 金鑰洩漏 (Key Compromise) | 約 150 萬美元 |
| 2026/04/26 | SingularityFinance | 預言機設定錯誤 (Oracle Misconfiguration) | 約 41.3 萬美元 |
| 2026/04/26 | Scallop | 會計缺陷 (Accounting Flaw) | 約 14.27 萬美元 |
*自定義再平衡合約事件未納入上週報告,為求完整性在此一併列出。
每週焦點:GiddyDefi
攻擊者並未破解簽名,沒有使用閃電貸,也沒有操控任何價格。他們重放了一個合法的簽名,同時將其未簽名的欄位替換為自己的合約。「利用你自己的簽名攻擊你」是 EIP-712 覆蓋範圍不完整如何將合法簽名轉變為通用授權(Permit)的最清晰演示。
2026 年 4 月 23 日,以太坊上的 GiddyVaultV3 遭到攻擊,損失約 130 萬美元。其簽名方案僅涵蓋了 SwapInfo.data,而將 aggregator、fromToken、toToken 和 amount 排除在 EIP-712雜湊之外,因此攻擊者可以篡改這些欄位並重放合法的簽名。攻擊者將 aggregator 指向了一個惡意合約,並將 fromToken 指向了策略的 LP 代幣,從而抽乾了約 130 萬美元的資金。
背景
GiddyVaultV3 (0x5f0a...4318) 是一個流動性挖礦金庫合約,用戶透過 deposit() 和 withdraw() 進行存取款。每項操作都必須攜帶一個由後端簽名的 VaultAuth 授權結構體,其中包含 EIP-712 簽名和一個描述代幣兌換路徑的 SwapInfo[ ] 陣列。當執行兌換時,合約會呼叫 GiddyLibraryV3.executeSwap(),該函式會對 swap.fromToken 執行 forceApprove 以授予 swap.aggregator 額度,然後透過 aggregator.call(swap.data) 執行兌換。隨後,策略合約會根據其配置的策略管理資金。
EIP-712 是一種用於對結構化鏈下數據進行簽名的標準:消耗該簽名的協議會在鏈上重構相同的結構體,根據約定的領域分隔符(Domain Separator)進行雜湊處理,並恢復簽署者的地址。因此,任何 EIP-712 流程的安全性取決於鏈上雜湊是否涵蓋了每一個會影響執行結果的欄位。在 Giddy 的設計中,後端簽署了一個包含用戶意圖及任何必要兌換路由指令的 VaultAuth,且 _validateAuthorization() 會在策略被允許移動資金之前重構該結構體以驗證簽名。
漏洞分析
核心漏洞在於 GiddyVaultV3 的 _validateAuthorization() 函式。在構建簽名資料時,每個 SwapInfo 結構中僅有 data 欄位被雜湊;而 aggregator、fromToken、toToken 和 amount 全都被排除在簽名範圍之外。這意味著任何掌握有效簽名的人,都可以自由替換 SwapInfo 的剩餘欄位,同時仍能通過簽名驗證。
每個被排除的欄位都是簽名留下的安全漏洞:aggregator 既作為 forceApprove 的獲批者,也作為 aggregator.call(swap.data) 的呼叫目標;fromToken 選擇了批准哪種策略資產;amount 設定了授權上限;toToken 僅用於 returnAmount > 0 的檢查。由於簽名中的 data 並未參照這些欄位,因此無法對其進行約束。

攻擊分析
以下分析基於該筆交易 0x5edb66...5482e5。
-
步驟 1:攻擊者從鏈上獲取了一個合法的後端授權
VaultAuth簽名,並保持data欄位不變。由於每次之前的deposit()或withdraw()呼叫都會在鏈上廣播完整的VaultAuth負載,任何歷史交易都是可重複使用簽名的免費來源;攻擊者只需尋找一個data欄位適合目標兌換呼叫的交易即可。 -
步驟 2:利用獲取的簽名,攻擊者保持
signature、nonce和data不變,同時篡改了剩餘欄位。攻擊者將fromToken設定為策略合約持有的 LP 代幣(真實資產),因此forceApprove授予了該協議確實持有的代幣額度。aggregator被替換為攻擊者的惡意合約,因此授權和後續的aggregator.call()都指向了攻擊者控制的代碼。由於這些欄位不在簽名驗證範圍內,_validateAuthorization()不加修改地接受了被篡改的結構體。為了繞過最後的require(returnAmount > 0, "SWAP_NO_TOKENS_RECEIVED")檢查,惡意聚合器實作了一個 mint 函式,向協議鑄造虛假代幣,在不執行任何實際兌換的情況下滿足了檢查條件。


- 步驟 3:由於惡意聚合器在步驟 2 中獲得了授權,攻擊者呼叫
transferFrom直接將金庫的 LP 代幣轉移到惡意聚合器中,從而完成了竊盜。這一步驟完全在協議受保護的執行路徑之外;當executeSwap()返回時,額度已經寫入且餘額檢查已經通過,協議再也沒有機會進行干預。

結論
此攻擊的根源在於不完整的 EIP-712 簽名覆蓋範圍。SwapInfo 中直接決定資金流向的核心欄位未受保護,導致攻擊者可以在提交有效簽名的同時,替換兌換路徑和聚合器地址。整合外部聚合器的開發者應注意:
-
確保 EIP-712 簽名涵蓋所有會影響執行結果的欄位,包括
aggregator、fromToken、toToken和amount。 -
實施聚合器白名單制度,以防止呼叫未經審計的外部合約。
-
將
toToken限制為預期的基礎代幣,防止虛假代幣繞過餘額檢查。
更廣泛地說,任何「先授權後呼叫」架構中的 EIP-712 必須對每一個影響最終鏈上狀態的欄位進行雜湊,而不僅僅是處理用戶意圖。每當後端簽名成為用戶參數與特權合約動作之間的唯一門控時,流入該動作的每個參數(呼叫目標、資產、金額、接收者)都必須包含在簽名結構體中。將 data 視為呼叫身份的代理是一個分類錯誤:呼叫的身份應為其所有參數的元組,且任何未被包含在簽名中的參數,其定義上即是由提交交易的人所控制的。
Web3 最佳安全審計服務
在發布前驗證設計、代碼和業務邏輯
本週更多事件
自定義再平衡合約 (Custom Rebalancer Contract)
2026 年 4 月 19 日,Avalanche 上的一個 sAVAX 再平衡合約遭到攻擊,從用戶的 Aave V3 信用委託中提取了約 6.4 萬美元(約 7,000 WAVAX)。一個公共函式在仍持有用戶委託的情況下執行了任意的 target.call(data),攻擊者藉此以被害者的名義呼叫了 Aave 的 borrow()。一個白帽機器人搶先阻止了該攻擊,並在任何提款發生前討回了資金。
背景
該再平衡合約 (0x7a7b...a8c9) 暴露了一個名為 b2a13230() 的函式,旨在再平衡用戶在 Aave 上的槓桿部位。該函式透過 Aave V3 信用委託代表用戶運作:用戶授予再平衡合約借款權限,再平衡合約則將這些借款與用戶提供的資金相結合以調整部位(例如借款 + 供應流程)。
漏洞分析
根本原因在於 b2a13230() 包含了一個 target.call(data) 步驟,其目標和 calldata 均由呼叫者控制。此呼叫在合約仍以用戶的 Aave V3 信用委託操作時執行,因此該步驟中呼叫的任何邏輯都會繼承用戶的借款能力。合約既沒有允許的目標白名單,也沒有對 calldata 形態的約束,因此呼叫可以觸發任何合約方法,包括將 onBehalfOf 設定為用戶的 Aave borrow() 函式。

攻擊分析
以下分析基於該筆交易:0xaaa1b2...35001b。
-
步驟 1:攻擊者閃電貸了一筆
sAVAX和USDC。接著透過再平衡合約將借入的USDC存入 Aave V3 以建立足夠的抵押品進行借款。同時,借入的sAVAX被直接轉移到再平衡合約,為借款後的供應步驟做準備。 -
步驟 2:攻擊者呼叫
b2a13230()函式。該函式首先執行了正常的借款操作,隨後進入了任意呼叫部分。此時,攻擊者構造了一個直接呼叫 Aave V3borrow()函式的指令,並將onBehalfOf設定為受害者地址。由於受害者已授予再平衡合約信用委託,借款成功執行。借入的WAVAX被轉移到了再平衡合約中。

- 步驟 3:攻擊者再次呼叫
b2a13230(),這次利用再平衡合約以自己的名義借入WAVAX。合約隨後將之前借入的(源自受害者部位的)WAVAX用於補充攻擊者的部位並進行償還,使攻擊者獲取利潤。
結論
缺陷在於在持有委託信用額度的特權上下文中,結合了任意外部呼叫。單獨的兩層防禦本來都很安全:受限的外部呼叫無法濫用委託,而沒有委託的任意呼叫也無法移動用戶資金。持有信用委託的合約絕不應暴露任意外部呼叫;如果必須進行這類呼叫,目標必須限制在白名單內,且 calldata 必須經過形態檢查。
REVLoans (Juicebox)
2026 年 4 月 20 日,Juicebox 上的借款擴充協議 REVLoans 在以太坊上遭到攻擊,損失約 5.07 萬美元。borrowFrom() 接受了由呼叫者提供的會計來源,卻未驗證該來源是否在協議中註冊;一個偽造的 36 小數位計數上下文觸發了同幣種捷徑,導致餘額在換算時出現了 1e18 的偏差。透過兩筆交易,一筆用於注入虛增的會計分錄,另一筆針對合法資金池進行借款,成功耗盡了 21.77 ETH。
背景
Juicebox 是一個以太坊上的混合募資與借貸協議。每個項目都有自己的 ERC20 分享代幣(此處稱為 REV)和分佈在一個或多個終端(Terminal,即實際保管項目資產並作為用戶進出入點的合約)的資金庫。一個項目可以在 JBDirectory 中註冊多個終端,每個 (terminal, project, token) 三元組都有一個 JBAccountingContext,聲明了該代幣在終端內部 bookkeeping 中的 (decimals, currency)。因此,REV 是對項目所有終端剩餘值集合的索賠,而非單一終端的索賠。
用戶可以將資產存入終端以兌換新鑄造的 REV,或在終端贖回 REV 以換取相應比例的剩餘資金(減去可配置的現金扣除稅)。REVLoans (0x2db6...1846) 作為一個疊加在上面的獨立合約,增加了借款功能:用戶燒毀 REV 作為抵押品,針對項目的一個終端進行借款,日後可透過還款贖回抵押品。借款金額的定價邏輯與贖回完全相同,因此借款在經濟學上等同於將相同的抵押品現金化。
REV 的分享價格為 (totalSurplus + totalBorrowed) / (REV.totalSupply + totalCollateral)。在分子中包含 totalBorrowed 可保持借款/還款的價格中性;這也意味著虛增的 totalBorrowed 會直接拉高分享價格,讓少量的抵押品能兌現出不成比例的資金。
漏洞分析
根本原因在於 source 參數未經驗證。borrowFrom() 接受呼叫者提供的 REVLoanSource source(一個包含 .terminal 和 .token 欄位的結構體),卻未檢查該對組合是否註冊於指定的 revnetId。這兩個欄位直接影響現金提取的計算,因此由 source.terminal 返回的會計上下文完全由呼叫者控制。當該上下文的 currency 欄位與目標終端匹配時,協議採取了同幣種捷徑,跳過了價格預言機,並權威性地採信了提供的位數和餘額數字。

未經驗證的 source 隨後被 _addTo() 寫入 _loanSourcesOf[revnetId] 和 totalBorrowedFrom[revnetId][source.terminal][source.token],該步驟同樣未執行註冊檢查。

一旦 (source, revnetId) 進入 bookkeeping,_borrowableAmountFrom() 就會將借款請求轉化為可付款金額。它透過 _totalBorrowedFrom() 計算 surplus = totalSurplus + totalBorrowed,然後將該盈餘與呼叫者的抵押品計數及分享供應量一併傳遞給 JBCashOuts.cashOutFrom()。

精度位數錯誤存在於更深一層的 _totalBorrowedFrom() 中。它遍歷 _loanSourcesOf 並使用 mulDiv(tokensLoaned, 10**decimals, pricePerUnit) 折疊每個條目。在同幣種路徑下,pricePerUnit = 10**decimals(目標的 18 位精度),因此公式簡化為 tokensLoaned 而未做變化。一個以 36 位小數儲存的餘額,以此落入 18 位小數的 ETH 總和時,放大了 1e18 倍。

放大效應發生在 cashOutFrom()。base = mulDiv(surplus, cashOutCount, totalSupply):由於 surplus 被虛增的 totalBorrowed 主導,即使極小的 cashOutCount(抵押品)也會導向不成比例的巨大付款。

攻擊分析
此攻擊使用了兩筆交易。第一筆污染了 REVLoans 的 bookkeeping:0xc46cb7...dead1f。第二筆則針對合法的終端耗盡了資金池:0x9adbd6...a8f938。
- 步驟 1:攻擊者呼叫
borrowFrom(),將 loan source 中的terminal和token指向一個虛假合約,並以少量REV作為抵押品。REVLoans 未檢查所提供的終端是否為該項目註冊,也不檢查代幣是否受認可。

- 步驟 2:REVLoans 詢問虛假終端以取得會計上下文,返回偽造的
(decimals=36, currency=ETH-code(61166))。因為來源和目標幣種匹配,REVLoans 採取了同幣種捷徑並跳過了價格預言機,隨後執行了跨越合法終端真實ETH剩餘值的現金提取計算,將這些數值表述為攻擊者的 36 位單位,使數字虛增了 1e18 倍。

- 步驟 3:REVLoans 將
(fake terminal, fake token)註冊到_loanSourcesOf並將虛增的數字寫入totalBorrowedFrom。虛假終端透過確認接收來「付款」;實際上沒有真實的ETH移動。第一筆交易結束時,totalBorrowed被向上操控,僅有小量的REV抵押品被燒毀。

- 步驟 4:攻擊者再次呼叫
borrowFrom(),此次將合法的ETH終端作為 loan source 並附帶極小數量的REV抵押品。現金提取數學運算在真實的 18 位ETH單位下執行。

- 步驟 5:計算
totalBorrowed時,REVLoans 遍歷_loanSourcesOf並觸及步驟 3 的條目。因為該條目的currency仍匹配ETH,同幣種捷徑再次觸發,以 36 位小數儲存的餘額被折疊進 18 位小數的ETH總和中,放大了 1e18 倍。totalBorrowed現在被虛假債務主導,分享價格分子被大規模膨脹。

- 步驟 6:現金提取運算返回了一個以虛增分子為尺寸的借款額,攻擊者已預先調整,使其低於合法終端真實的剩餘值。合法終端執行了付款,將資金池中幾乎所有的資金都抽乾給了攻擊者。

結論
根本原因在於兩個缺陷交織:(terminal, token) 對在未檢查項目註冊的情況下被接受,以及同幣種捷徑在折疊來源餘額至目標總和時未進行小數重正規化。任何一個缺陷單獨分開危險性都較小;兩者結合則允許呼叫者注入任意的 totalBorrowedFrom 並以面額兌現。緩解措施:根據項目的註冊終端驗證 (terminal, token),並在折疊前根據來源儲存的位數重新正規化餘額。
Volo Vault
2026 年 4 月 22 日,Sui 上的收益金庫 Volo 通過將用戶存款路由到 Navi 借貸協議來賺取收益,在運營私鑰洩漏後損失約 350 萬美元。金庫合約本身沒有合約級別的錯誤,攻擊者只是利用竊取的憑證執行了合法的運營路徑,抽乾了 Volo 在 Navi 的存款。
背景
Volo 是用戶面向的金庫 (0xcd86...27fefa);Navi 是底層借貸協議。該金庫持有一個 Navi AccountCap(授權從 Volo 的 Navi 帳戶提取資金的 Sui 能力物件),並將策略操作委託給運營者角色。要在 Navi 上存取款,運營者需呼叫 start_op_with_bag_v2() 將 AccountCap 從金庫提升到臨時 bag 中,然後利用 deposit_with_account_cap() / withdraw_with_account_cap_v2() 使用該 Cap 轉移資金。
漏洞分析
根本原因是營運/金鑰保管失敗,而非合約級別漏洞。Volo 策略路徑將提款權限委託給持有運營私鑰的任何人:start_op_with_bag_v2() 僅執行兩項檢查 (assert_operator_not_freezed 以及 assert_single_vault_operator_paired),兩者均僅驗證提供的能力具備運營者註冊身份。withdraw_with_account_cap_v2() 隨後接受任何能出示該 AccountCap 的呼叫者。因此,任何擁有運營私鑰的人,皆可執行與合法操作完全無法區分的相同路徑。

攻擊分析
以下分析基於該筆交易 AQw9wM...3RUS。
- 步驟 1:攻擊者使用洩漏的運營金鑰呼叫
@volosui/volo-vault::operation的start_op_with_bag_v2,將 NaviAccountCap提升到臨時 bag 中。

-
步驟 2:攻擊者使用
bag::remove從臨時 bag 中提取AccountCap。 -
步驟 3:攻擊者呼叫
@navi-protocol/lending::incentive_v3的withdraw_with_account_cap_v2並攜帶提取的AccountCap,將 Volo 在 Navi 上的存款提出。

- 步驟 4:攻擊者使用
bag::add將AccountCap放回,關閉操作並將資金轉移。
結論
缺陷在於結構性設計:單一運營金鑰、完全提款權限、缺乏二次驗證。三項更改可降低金鑰洩漏的影響:將運營者角色拆分為多簽或閾值簽名,意味著洩漏的單一金鑰無法自行授權提款。增加提款時間鎖定可為異常呼叫增加爭議窗口。將運營者的權力限制在僅能存取款與再平衡,而用戶面向的提款則路由至獨立路徑,可避免運營者角色直接接觸用戶資金。
Kipseli Router
2026 年 4 月 22 日,Base 上的 Kipseli Router 遭到攻擊,損失約 7.235 萬美元。該 Router 使用外部 USDC 定價計數器返回的報價作為原始輸出代幣的轉移金額,卻未驗證輸出代幣是否與報價代幣相等。攻擊者在計數器並不支援的路徑上以 0.04 WETH 兌換 cbBTC,導致路由器將計數器的 USDC 精度值(92,610,395)當作原始 cbBTC 單位收到(約 0.926 cbBTC)。
背景
Kipseli Router (0x579f...9a07) 是一個由外部報價系統驅動的兌換合約。該合約未開源;以下分析基於其反編譯字節碼。與直接計算鏈上 AMM 池價格不同,它會查詢計數器以取得輸出金額 (amountOut),然後基於該數值執行代幣轉移。正常情況下,用戶發送 tokenIn 到協議錢包,Router 從同一個錢包調用 tokenOut 並轉發給接收者。協議配置了單一 QUOTE_TOKEN,且邏輯均以 USDC 的 6 位小數點為單位;系統僅旨在支援 USDC 定價的報價。
漏洞分析
缺陷存在於相互疊加的兩層中。在 Router 側,0xcce096f3() 函式透過報價器 0x592() 取得報價 v0,並將其不加修改地傳入 0xd88() 作為 tokenOut.transferFrom(_wallet, receiver, v0)。Router 從未檢查 tokenOut 是否等於協議的 QUOTE_TOKEN,因此 USDC 精度的數值(6 位精度)被當作了 cbBTC(8 位精度)來轉移。在報價器側,底層 PropAMM 專為代幣對 USDC 設計,但在接受不支援的路徑 (WETH → cbBTC) 時卻未還原,而是默默忽略了 tokenIn,就像兌換有效一樣返回了一個 USDC 精度的值。

攻擊分析
以下分析基於該筆交易 0x96edee...3db3bb。
- 步驟 1:攻擊者以
tokenIn=WETH,tokenOut=cbBTC呼叫 Router。底層 AMM 不支援此路徑但未還原,報價器0x592()返回了USDC精度的數值 92,610,395(約 92.61USDC)。

- 步驟 2:Router 將該數值直接用於
cbBTC轉移金額。0.04WETH(約 $95)透過transferFrom流入;約 92,610,395 原始cbBTC單位(約 0.926cbBTC,約 $72.35K)從協議錢包流向了攻擊者。

結論
攻擊成功是因為報價器呼叫前後的假設均未檢查。報價器假設其輸出是在自己的 USDC 6 位精度框架內消耗的;Router 假設報價器返回的任何內容皆以所請求的 tokenOut 為單位。兩者任一修復皆可排除漏洞:
-
在 Router:確認
tokenOut == QUOTE_TOKEN,或在轉移前透過預言機將USDC精度的報價轉換回tokenOut單位。 -
在報價器:若兌換路徑中的代幣未在支援的對集中註冊,應還原交易,而非靜默返回
USDC精度的回退值。
Purrlend
2026 年 4 月 25 日,HyperLiquid 和 MegaETH 上的借貸協議 Purrlend 在私鑰洩漏後損失約 150 萬美元。攻擊者接管了橋接(Bridge)角色並鑄造了無背書的 pTokens(Purrlend 的類 Aave 收據代幣),然後將這些 pTokens 作為抵押品從資金池中借出了真實資產。
背景
Purrlend (0x81d5...a702) 是一個擁有類似 Aave 會計模型的借貸協議。當用戶存入資金時,會收到對應的 pTokens(類似 Aave 的 aTokens),代表其存入的部位,可用作抵押品進行借貸。
協議還包含特權角色,如 pool admin、risk admin 和 bridge。Bridge 角色旨在處理跨鏈會計:它可以鑄造 pTokens 以映照在對手鏈上發生的存款。其他管理員角色負責修改風險參數和配置可借貸資產。
漏洞分析
直接引發攻擊的是特權金鑰洩漏:攻擊者掌握了控制 Purrlend 管理員和 Bridge 角色的金鑰。合約級別的設計缺陷放大了洩漏後的損失:Bridge 角色的 pToken 鑄造路徑未與任何可驗證的跨鏈託管證明綁定。該函式允許擁有 Bridge 角色的呼叫者向任何地址、以任何金額發送 pTokens,而無需檢查源鏈上是否發生過對應存款。協議其他部分將 pTokens 視為有效抵押品,且借貸路徑不會在借款時重新檢查背書情況。因此,一個非法的 Bridge 角色鑄造直接轉化為了借款能力,mint 和資產提款之間缺乏第二道門限。
攻擊分析
以下分析基於 MegaETH 上的該筆交易 0xb96cff...dbbf24。
- 步驟 1:攻擊者持有特權金鑰,透過
GnosisSafeProxy使用MultiSendCallOnly批次處理,通過ACLManager將自己設定為pool admin、risk admin、bridge和emergency admin,接著啟用WETH作為可借資產並設定BorrowCap為 200。

-
步驟 2:攻擊者以 Bridge 身分向自己的地址鑄造了大量
pTokens。該鑄造路徑未經跨鏈託管驗證,因此這些新的pTokens沒有底層資產背書。 -
步驟 3:攻擊者將無背書的
pTokens用作抵押品。因為借貸路徑在不重複檢查背書的情況下將任何pToken餘額視為有效供應部位,抵押品檢查通過,WETH從資金池中被借出。
結論
這是一起私鑰洩漏引發的事故,並因合約設計缺陷而擴大了影響。洩漏的金鑰僅賦予攻擊者 Bridge 角色本應具備的權限,但該權限包含不受約束的 pToken 鑄造,直接轉化為可借貸抵押品。每一層皆可獨立強化:營運層面應將 Bridge 角色拆分為多簽或閾值機制,以防單點金鑰洩露;合約層面要求 Bridge 鑄造必須附帶可驗證的託管證明(例如來自受信任跨鏈驗證者的訊息承諾),並在未提供證明時拒絕交易。在鑄造時驗證證明是更持久的解決方案,因其完全消除了對單一金鑰保管的依賴。
SingularityFinance
2026 年 4 月 26 日,Base 上 SingularityFinance 的 dynBaseUSDCv3 金庫損失約 41.3 萬美元。該金庫配置了無效的 Uniswap V3 費用層級(42,該層級在 V3 中不存在),因此每個非 USDC 資產的預言機定價都解析為不存在的存取池。定價函式非但不還原交易,反而預設返回 0,導致金庫將其非 USDC 存儲資產估值為 0。攻擊者透過存入極少量的 USDC 鑄造了幾乎全部的份額份量,隨後兌現了底層資產。
背景
dynBaseUSDCv3 金庫 (0x67b9...4dcd) 持有多種帶息代幣,並透過 Uniswap V3 對非 USDC 存款進行定價。定價輔助工具 getPrice(base, fee, quote, amount) 會通過工廠將 (base, quote, fee) 元組解析為 Uniswap V3 池,然後讀取該池的 TWAP。金庫的 totalAssets() 加總了所有已定價存儲,份額鑄造與贖回比例皆源自於此總和。
漏洞分析
缺陷存在於 getPrice() 的提早返回分支中。當 IUniswapV3Factory.getPool 返回 address(0) 時,函式會跳過處理並返回初始化為 0 的 price 變量,而不是還原。金庫部署時 fee=42 並非 Uniswap V3 的支援層級 (500/3000/10000),因此每個非 USDC 代幣的查找均會命中此分支。因此 totalAssets() 僅加總了金庫的 USDC 餘額,而其實際持有的帶息代幣價值均為 0。作為分母的 totalAssets() 近乎為 0,導致鑄造和贖回比例嚴重錯誤。

攻擊分析
以下分析基於該筆交易 0x00b949...8d3732。
-
步驟 1:攻擊者借貸了約 10 萬
USDC。 -
步驟 2:攻擊者將
USDC存入金庫。由於totalAssets()僅計入USDC餘額,金庫估值僅為存款金額,攻擊者獲得了近 100% 的份額。 -
步驟 3:攻擊者贖回份額,按份額比例分配底層儲備。攻擊者獲得了金庫所持每種帶息代幣的巨大比例。
-
步驟 4:攻擊者歸還貸款,並將抽走帶息代幣作為利潤。
結論
缺失了兩項檢查:部署時未驗證 fee=42 是否為有效範圍;以及 getPrice() 在遺失池的情況下應還原而非返回 0。任一修復皆有效:在配置時驗證預言機參數,或在 getPool() == address(0) 時還原。作為深度防禦,鑄造份額的邏輯在接收存款前,應將 totalAssets() 與外部參考基準進行合理性檢查。
Scallop
2026 年 4 月 26 日,Scallop 在 Sui 上的質押獎勵計畫損失約 14.27 萬美元。更新用戶累積獎勵的函式未驗證傳入的獎勵追蹤物件是否與用戶帳戶匹配,攻擊者藉此從一個被棄用的、長期閒置的獎勵追蹤物件中提取虛假的積點餘額,並兌現合法獎勵池直到資金被抽乾。
背景
Scallop 是 Sui 上的借貸協議。在借貸產品之上,Scallop 運作著一個 spool 計畫:用戶存入單一資產到 Scallop 市場,收到 MarketCoin<T>(借貸收據,如 sSUI),然後將其質押到 Spool 以賺取積點,隨後可用這些積點兌換獎勵代幣。每個 Spool 是 Sui 的共享物件,追蹤一個全域性的每份份額 index;每個用戶持有個人 SpoolAccount,記錄質押餘額與累積的 points。
漏洞分析
缺陷在於 spool::user::update_points 函式未斷言 account.spool_id == object::id(spool)(或 stake_type 匹配)。同儕的 stake、unstake 和 redeem_rewards 在入口處皆執行了綁定檢查;僅 update_points 跳過檢查。沒有檢查時,spool_account::accrue_points 會計算 account.points += stake * (spool.index − account.index) / 1e9,並無視傳入物件的真實來源,將其 index 當作帳戶自己的獎勵流。

漏洞之所以可被利用,是因為 Sui 從未回收共享物件:閒置的 Scallop Spool 雖然持倉餘額已降至塵埃量,但其 reward share (每期增量 1e9 * reward / stakes) 仍會持續累積,導致其 index 隨時間無限增加。缺失綁定檢查下,update_points 可利用這已被放大的 index 向任何帳戶寫入巨大的積點增量。被注入的 points 隨後按 1:1 在目標 spool 的 RewardsPool 中兌現,因為綁定檢查會通過目標 spool 的檢核。
攻擊分析
以下分析基於該筆交易 6WNDjC...NfVL。
-
步驟 1:以 0.2
SUI為魚餌,攻擊者鑄造MarketCoin<SUI>,然後對目標 spool 呼叫new_spool_account+stake,創建一個綁定於target_spool的SpoolAccount。 -
步驟 2:攻擊者呼叫
update_points<MarketCoin<SUI>>(donor_spool, account, clock),將donor_spool設定為被閒置的Spool。該 donor 的index(約8.91e14)被寫入帳戶作為points。 -
步驟 3:攻擊者呼叫
redeem_rewards。綁定斷言接受帳戶,累積點數被按獎勵池 1:1 比率轉換直到資金耗盡,金額高達 150,098,061,595,978 原始SUI。 -
步驟 4:攻擊者解除質押並提取最初的 0.2-
SUI魚餌,並使用TransferObjects將資產移出。
結論
修復方法為在 update_points 的入口處加入與 stake、unstake 及 redeem_rewards 相同的 assert!(account.spool_id == object::id(spool)) 檢查。作為深度防禦,協議亦可對單次 accrue_points 調用所接受的 index 差異設定上限,確保即使綁定檢查未來再次失效,也沒有單次呼叫能寫入與其實際質押持續時間不符的點數。
關於 BlockSec
BlockSec 是一家全棧式區塊鏈安全與加密貨幣合規解決方案供應商。我們構建各類產品與服務,協助客戶在協議與平台的全生命週期內進行代碼審計(涵蓋智慧合約、區塊鏈底層及錢包)、即時攔截攻擊、分析事件、追蹤非法資金,並滿足 AML/CFT 合規義務。
BlockSec 已在頂級安全會議上發表多篇區塊鏈安全論文,報導過多個 DeFi 應用的零日漏洞,攔截過多次攻擊並累計挽回超過 2,000 萬美元的資金,並保障了數十億美元的加密資產安全。
-
官方 Twitter:https://twitter.com/BlockSecTeam
-
🔗 BlockSec 審計服務 : 提交審計申請



