相信Uniswap v4 不久就會和大家見面了!

這次Uniswap 團隊可謂目標宏大,計畫引入眾多全新功能[1],包括每個交易對支援無限數量的流動性池和動態費用、單例設計、閃電記帳、Hook,以及支援ERC1155 代幣標準。利用EIP-1153 引入的瞬態存儲,Uniswap v4 預計將在以太坊坎昆升級之後發布。

在諸多創新中, Hook 機制因其強大潛力引起了廣泛關注。 Hook 機制支援在流動性池生命週期中的特定點執行特定程式碼,大大增強了池子的可擴展性和靈活性。

然而,Hook 機制也可能是一把雙面刃。雖然它功能強大且靈活,安全使用Hook 同樣是一個不小的挑戰。 Hook 的複雜性不可避免地帶來了新的潛在攻擊向量。因此,我們希望撰寫一個系列文章,來系統介紹與Hook 機制相關的安全問題與潛在風險,以此推動社區的安全發展,相信這些見解將有助於建立安全的Uniswap v4 Hook。

作為本系列文章的開頭之作,本文介紹了與Uniswap v4 中Hook 機制相關的概念,並概述了Hook 機制存在的安全風險。

Uniswap V4的機制

在深入探討之前,我們需要對Uniswap v4 的機制有基本的了解。根據官方公告[1]和白皮書[2], Hook、單例架構和閃電記帳是實現自訂流動性池和跨多個池子實現高效路由的三個重要功能。

1.1 Hook

Hook 指的是流動性資金池生命週期的不同階段運作的合約,Uniswap 團隊希望透過引入Hook使任何人都能做出權衡決策。透過這種方式,可以實現原生支援動態費用、添加鏈上限價單、或透過時間加權平均做市商(TWAMM)分散大訂單。

目前有八個Hook 回調,分為四組(每組包含一對回呼):

  • beforeInitialize/afterInitialize

  • beforeModifyPosition/afterModifyPosition

  • beforeSwap/afterSwap

  • beforeDonate/afterDonate

以下是白皮書[2]中介紹的交換Hook 的流程。

為何說Hook是Uniswap V4的一把「雙面刃」?

圖1:交換Hook 流程

Uniswap 團隊用一些範例(例如TWAMM Hook[3])介紹了操作方法,社群參與者也做出了一些貢獻。官方文件[4]也連結到了Awesome Uniswap v4 Hooks[5] 倉庫,該倉庫收集了更多的Hook 範例。

1.2 單例、閃電記帳和鎖機制

單例(singleton)架構和閃電記帳(flash accounting)旨在透過降低成本和確保效率來提高效能。它引入了一種新的 singleton 合約,即所有流動性池都保存在同一個智能合約中。這個單例設計依賴一個 PoolManager 來儲存和管理所有池子的狀態。

在Uniswap 協議的早期版本中,兌換或添加流動性等操作涉及直接代幣轉移,v4 版本則有所不同,在於其引入了閃電記帳和鎖定機制(lock mechanism)。

鎖機制的運作方式如下:

1. 某個locker合約在 PoolManager 上請求lock。

2. PoolManager 將該locker合約的位址加入lockData 佇列,並呼叫其 lockAcquired 回呼。

3. 此locker合約在回呼中執行其邏輯。在執行過程中,locker合約與池子的互動可能導致非零的貨幣增量。然而,在執行結束時,所有增量必須結算為零。另外,如果lockData 佇列不為空,只有最後一個locker合約可以執行操作。

4. PoolManager 檢查lockData 佇列和貨幣增量的狀態。驗證後,PoolManager 將刪除該locker合約。

總結來說,鎖機制防止了並發訪問,並保證了所有的交易都能被清算。而locker合約依序申請lock,然後透過lockAcquired 回呼執行交易。在每次池操作前後會觸發對應的Hook回呼。最後,PoolManager 會檢查狀態。

這種方法意味著操作調整的是內部淨餘額(即delta ),而不是執行即時轉帳。任何修改都會記錄在池子的內部餘額中,實際的轉帳則在操作(即lock )結束時進行。這個過程保證了沒有未清算的代幣,從而維持了資金的完整性。

由於鎖定機制的存在,外部所有帳戶 (EOA) 不能直接與PoolManager 互動。相反,任何互動都必須透過一個合約進行。該合約作為一個中間的locker,在進行任何池操作之前都需要請求lock。

主要存在兩種合約互動場景:

  • 某個locker合約來自官方的程式碼庫,或是由使用者部署。在這種情況下,我們可以將互動視為透過路由器進行。

  • 某個locker合約和Hook 整合到同一個合約中,或由第三方實體控制。對於這種情況,我們可以將互動視為透過Hook進行。在這種情況下,Hook既扮演了locker合約的角色,又負責處理回調。

威脅模型

在討論相關的安全問題之前,我們需要確定威脅模型。我們主要考慮以下兩種情況:

  • 威脅模型I :Hook 本身是良性的,但有漏洞。

  • 威脅模型II :Hook 本身就是惡意的。

在接下來的部分,我們將根據這兩種威脅模型討論潛在的安全問題。

2.1 威脅模型 I 中的安全性問題

威脅模型I 關注的是與Hook 本身相關的漏洞。這個威脅模型假設開發者及其Hook 是無惡意的。然而,智能合約現有的已知漏洞也可能出現在Hook 中。例如,如果Hook 是作為可升級合約實現的,那麼它可能會遇到類似於OpenZeppelin 的UUPSUpgradeable 漏洞的相關問題。

鑑於上述因素,我們選擇聚焦在v4版本特有的潛在漏洞。在Uniswap v4 中,Hook 是能夠在核心池操作(包括初始化、修改位置、交換和收集)之前或之後執行自訂邏輯的智慧合約。雖然Hook 預計將實現標準的接口,但它也允許包含自訂邏輯。因此,我們的討論範圍將限制在涉及標準Hook 介面的邏輯。然後,我們將嘗試找出可能的漏洞來源,例如,Hook 可能如何濫用這些標準Hook 函數。

具體來說,我們將關注以下兩種Hook:

  • 第一種hook, 保管用戶資金。在這種情況下,攻擊者可能會攻擊這個hook 來轉移資金,造成資產損失。

  • 第二種hook, 儲存使用者或其他協定所依賴的關鍵狀態資料。在這種情況下,攻擊者可能會試圖改變關鍵狀態。當其他使用者或協定使用錯誤狀態時,可能會帶來潛在風險。

請注意,這兩種範圍之外的hook 不在我們的討論範圍內。

由於本文撰寫時還沒有Hook 的真實用例,我們將從Awesome Uniswap v4 Hooks 倉庫中獲取一些資訊。

在對Awesome Uniswap v4 Hooks倉庫(提交哈希為3a0a444922f26605ec27a41929f3ced924af6075)進行深入研究後,我們發現了幾個嚴重的漏洞。這些漏洞主要源自於hook、PoolManager 以及外部第三方之間的風險交互,主要可分為兩類:存取控制問題輸入驗證問題。具體發現請見下表:

為何說Hook是Uniswap V4的一把「雙面刃」?

總的來說,我們發現了22個相關項目(排除與Uniswap v4無關的項目)。在這些項目中,我們認為有8個(36%)項目是存在漏洞的。在這8個有漏洞的專案中,6個有存取控制問題,2個容易受到不受信任的外部呼叫。

2.1.1 存取控制問題

在這部分討論中,我們主要關注的是v4 中的回呼函數可能導致的問題,包括8 個hook 回調和lock 回呼。當然,還有其他情況需要驗證,但這些情況因設計而異,暫不在我們的討論範圍之內。

這些函數應該只能被PoolManager 調用,不能被其他位址(包括EOA和合約)呼叫。例如,在獎勵由資金池金鑰分發的情況下,如果相應的函數可以由任意帳戶調用,那麼獎勵可能會被錯誤地領取。

因此,對於hook來說,建立強大的存取控制機制是至關重要的,尤其是它們可以被除了池子本身之外的其他方調用。透過嚴格管理存取權限,流動性池可以顯著降低與hook未授權互動或惡意互動的風險。

2.1.2 輸入驗證問題

在Uniswap v4中,由於存在鎖定機制,用戶在執行任何資金池操作之前必須透過合約獲得一個lock。這確保了目前參與交互的合約是最新的locker合約。

儘管如此,仍然存在一個可能的攻擊場景,即由於在一些易受攻擊的Hook 實作中輸入驗證不當而導致的不受信任的外部呼叫:

  • 首先,hook並未驗證用戶打算互動的資金池。這可能是一個含有虛假代幣並執行有害邏輯的惡意資金池。

  • 其次,一些關鍵的hook函數允許任意的外部呼叫。

不受信任的外部呼叫極其危險,因為它可能導致各種類型的攻擊,包括我們熟知的重入攻擊。

為了攻擊這些易受攻擊的hook,攻擊者可以為自己的假代幣註冊一個惡意資金池,然後呼叫hook在資金池執行操作。在與資金池互動時,惡意代幣邏輯劫持控制流以便進行不良行為。

2.1.3 針對威脅模型 I 的防範措施

為了規避與hook相關的此類安全問題,透過適當執行對敏感的外部/公共函數的必要存取控制,並對輸入參數進行驗證,從而對互動進行驗證是至關重要的。此外,重入保護可能有助於確保hook 不會在標準邏輯流程中重複執行。透過實施適當的安全防護措施,資金池可以降低與此類威脅相關的風險。

2.2 威脅模型 II 中的安全問題

在這個威脅模型中,我們假設開發者及其hook 是惡意的。鑑於涉及範圍很廣,我們僅關注與v4 版本相關的安全問題。因此,關鍵在於提供的hook 是否能夠處理使用者轉帳或授權的加密資產。

由於存取hook 的方法決定了可能賦予hook 的權限,我們據此將hook 分為兩類:

  • 託管型Hook(Managed Hooks) :hook 不是入口點。使用者必須透過路由器(可能由Uniswap 提供)與hook 進行互動。

  • 獨立型Hook(Standalone Hooks) :hook 是入口點,允許使用者直接與之互動。

為何說Hook是Uniswap V4的一把「雙面刃」?

圖2:惡意Hook 的例子

2.2.1 託管型 Hook

在這種情況下,用戶的加密資產(包括原生代幣和其他代幣)被轉帳或授權給 router 。由於 PoolManager 執行了餘額檢查,惡意hook 不容易直接竊取這些資產。然而,仍然存在潛在的攻擊面。例如,v4 版本的費用管理機制可能會被攻擊者透過hook 進行操縱。

2.2.2 獨立型 Hook

當Hook 被用作入口點時,情況就更加複雜。在這種情況下,由於使用者可以直接與hook 進行交互,hook 獲得了更多的權力。理論上,hook 可以透過這種互動執行想要的任何操作。

在v4 版本中,程式碼邏輯的驗證是非常關鍵的。主要問題在於是否可以操縱程式碼邏輯。如果hook 是可升級的,這意味著一個看似安全的hook 可能會在升級之後成為惡意的,從而構成重大風險。這些風險包括:

  • 可升級的代理(可以被直接攻擊);

  • 帶有自毀邏輯。在聯合呼叫 selfdestruct 和 create2 的情況下可能會被攻擊。

2.2.3 針對威脅模型 II 的防範措施

至關重要的一點在於評估hook 是否是惡意的。具體來說,對於託管型hook,我們應該專注於費用管理的行為;而對於獨立型hook,主要的關注點在於它們是否可升級。

結論

在本文中,我們首先簡要概述了與Uniswap v4 的Hook 安全問題相關的核心機制。隨後,我們提出了兩種威脅模型並簡要概述了相關的安全風險。

在後續文章中,我們將對每種威脅模型下的安全問題進行深入分析。