Back to Blog

玫瑰中的荆棘:探索 Uniswap v4 新型 Hook 机制的安全风险

Code Auditing
November 6, 2023
10 min read

Uniswap v4 即将推出!团队雄心勃勃的计划引入一系列新功能[1],包括(理论上)无限数量的池以及每个交易对的动态费率、单例、闪电记账、钩子以及对 ERC1155 的支持。Uniswap v4 利用 EIP-1153 引入的瞬态存储,预计将在以太坊的 Cancun 升级后启动。

在这些创新中,钩子机制因其强大的潜力而备受关注。它允许在池操作的特定时间点执行特定代码,大大增强了池的可扩展性和灵活性。

然而,钩子机制也可能是一把双刃剑。虽然它功能强大且灵活,但安全利用它的挑战不容忽视。这种复杂性不可避免地会带来新的潜在攻击向量。为了从安全角度为社区做出贡献,我们的目标是系统地呈现一系列文章,探讨与此机制相关的安全问题和顾虑。我们相信,这些见解将有助于构建安全的 Uniswap v4 钩子。

本文是本系列的开篇,为读者提供全面概述和基础理解。敬请关注更多精彩讨论!

Uniswap v4 的机制

在深入细节之前,我们需要对 Uniswap v4 的机制有一个基本的了解。根据官方公告[1]和白皮书[2],钩子、单例架构和闪电记账是实现池定制和跨多个池的高效路由的三项关键功能。

钩子

v4 中的钩子旨在允许任何人通过钩子做出权衡决策,钩子是在池操作生命周期内不同时间点运行的合约。通过这样做,可以定制原生支持动态费用的池,添加链上限价单,或者充当时间加权平均做市商(TWAMM)来随着时间分散大额订单。

目前,有八个钩子回调,分为四组(每组包含一对回调):

  • beforeInitialize/afterInitialize
  • beforeModifyPosition/afterModifyPosition
  • beforeSwap/afterSwap
  • beforeDonate/afterDonate

下图展示了白皮书[2]提供的 swap 钩子流程。

图 1: Swap Hook Flow
图 1: Swap Hook Flow

Uniswap 团队提供了一些示例(例如 TWAMM Hook[3])来演示其用法,社区的参与者也做出了贡献。官方文档[4]链接到存储库 Awesome Uniswap v4 Hooks[5],其中收集了更多钩子示例。

单例、闪电记账和锁定机制

单例架构和闪电记账旨在通过降低成本和确保效率来提高性能。具体来说,它引入了一个新的 singleton 合约,所有池都存在于单个智能合约中。这种单例设计依赖于 PoolManager 来存储和管理所有的所有状态。

与 Uniswap Protocol 的早期版本不同,后者中的交换或流动性添加等操作涉及直接的代币转账,Uniswap v4 引入了闪电记账锁定机制

具体来说,锁定机制按以下方式运行:

  1. 锁定器合约请求对 PoolManager 的锁定。
  2. PoolManager 将锁定器的地址添加到 lockData 队列中,并调用其 lockAcquired 回调。
  3. 锁定器在回调中执行其逻辑。在锁定器的执行过程中,其与池的交互可能会产生非零的货币差额。但是,在执行结束时,所有差额必须结算为零。此外,如果 lockData 队列不为空,则只有最后一个锁定器可以执行操作。
  4. 之后,PoolManager 检查 lockData 队列和货币差额的状态。验证后,PoolManager 将移除锁定器。

总之,锁定机制可防止并发访问并确保结算。锁定器排队等待锁定,然后通过 lockAcquired 回调执行。在池操作之前和之后,会调用指定的钩子回调。最后,PoolManager 检查状态。

这种方法意味着操作会调整内部净余额(即差额),而不是执行即时转账。所有修改都记录在池的内部余额中,实际转账发生在操作结束时(即锁定)。此过程确保没有未结代币,从而保持偿付能力。

由于锁定机制,EOA(外部拥有账户)无法直接与 PoolManager 交互。相反,任何交互都必须通过一个合约。该合约作为中间锁定器,在任何池操作之前请求锁定。 主要有两种合约交互场景:

  • 锁定器合约来自官方存储库或由用户部署。在这些情况下,我们可以将交互视为通过路由器进行。

  • 锁定器和钩子集成在同一合约中或由第三方实体控制。在这种情况下,我们可以将交互视为通过钩子进行。因此,钩子同时扮演锁定器和回调处理程序的双重角色。

威胁模型

在讨论相应的安全问题之前,需要确定威胁模型。 基本上,在使用钩子时会出现一些考虑因素:

  • 威胁模型 I:钩子是良性的但易受攻击。
  • 威胁模型 II:钩子是恶意的。

在接下来的部分中,我们将根据威胁模型讨论潜在的安全问题/顾虑。

威胁模型 I 中的安全顾虑

威胁模型 I 侧重于与钩子本身相关的漏洞。显然,此威胁模型假设开发人员及其钩子是良性的。 然而,智能合约中已知的漏洞也可能出现在钩子中。例如,如果钩子实现为可升级合约,它可能会遭受类似于 OZ 库的 UUPSUpgradeable 漏洞[6]的一些漏洞。

考虑到这些因素,我们选择专注于v4 特有的潜在漏洞。具体来说,在 Uniswap v4 中,钩子是可定制的智能合约,能够在核心池操作(包括initializemodifyPositionswapdonate)之前或之后执行逻辑。钩子应实现标准的钩子接口,但也允许它们包含自定义逻辑。 因此,我们的范围仅限于与标准钩子接口相关的逻辑。然后,我们可以总结钩子可能如何利用这些标准钩子函数来确定潜在的漏洞来源。

广义而言,钩子可以分为两类:

  • 充当用户资金保管者的钩子。在这些情况下,攻击者可能会以钩子为目标来转移资金,从而导致资产损失。
  • 存储用户或其他协议依赖的关键状态数据的钩子。对于这些钩子,攻击者可能会故意更改关键状态。错误的状态,当被其他用户或协议使用时,会引入潜在风险。

请注意,不属于这两类范围的钩子不在我们讨论的范围内。

尽管范围有限,但在此处枚举所有可能性仍然不可行。由于截至本写作之时没有实际的应用,我们决定通过 Awesome Uniswap v4 Hooks 存储库来获取一些见解。

在仔细检查存储库(提交哈希为 3a0a444922f26605ec27a41929f3ced924af6075)后,我们识别出了一些关键漏洞。这些漏洞主要源于钩子、PoolManager 和外部第三方之间的风险交互。漏洞分为两大类:有缺陷的访问控制不正确的输入验证。结果总结在下表中:

# of flawed projects # of flawed access control # of improper input validation
8 6 2

总体而言,我们确定了 22 个相关的项目(不包括一些似乎与 Uniswap v4 无关的项目)。其中,8 个(占 36%)被认为存在漏洞。具体来说,在这些有漏洞的项目中有 6 个检测到有缺陷的访问控制,而 2 个容易受到不受信任的外部调用。

缺陷访问控制

在本讨论中,我们重点关注与 v4 相关的访问控制问题,这些问题源于 v4 的回调函数,包括 8 个钩子回调和锁定回调。当然,其他情况可能也需要进行验证。然而,这些情况取决于设计,超出了我们 earlier 讨论指定的范围。

这些函数只能由 PoolManager 调用,而不是由其他地址(包括 EOA 和合约)调用。例如,考虑由池密钥分发奖励的情况。如果相应的函数可以被任意账户调用,则可能会错误地认领奖励。

因此,至关重要的是为钩子建立强大的访问控制机制,特别是由于它们可以被池本身以外的方调用。通过仔细管理访问权限,池可以大大降低与钩子的未经授权或恶意交互的风险。

不正确的输入验证

在 Uniswap v4 中,由于锁定机制,用户必须先通过一个合约获得锁定,然后才能执行任何池操作。这确保了当前交互的合约是最新的锁定器。

尽管如此,由于某些有漏洞的钩子实现中不正确的输入验证,仍然存在一个潜在的攻击场景,即不受信任的外部调用

  • 首先,钩子不验证用户打算与之交互的池。这可能是一个带有假代币并执行有害逻辑的恶意池。
  • 其次,一些关键的钩子函数允许任意外部调用。

不受信任的外部调用极其危险,因为它可能导致各种类型的攻击,包括众所周知的重入问题。

为了利用这种有漏洞的钩子,攻击者可以注册他们假代币的恶意池,然后调用钩子在该池上执行操作。在与池交互时,恶意代币逻辑会劫持控制流以方便不当行为。

威胁模型 I 的缓解措施

为了规避钩子中的此类问题,通过适当执行敏感外部/公共函数的必要访问控制,以及验证输入参数,来验证交互至关重要。此外,重入保护可能有助于确保钩子无法在标准逻辑流程内重复执行。通过实施适当的保护措施,池可以减轻与此类威胁相关的风险。

威胁模型 II 中的安全顾虑

在此威胁模型中,开发人员及其钩子被假定为恶意的。鉴于可能性范围广泛,我们选择主要关注与 v4 相关的问题。因此,关键考虑因素是提供的钩子是否能够处理用户已转账或已批准的加密资产。

根据访问钩子的方法,该方法决定了授予钩子的潜在权限,我们可以将钩子分为两个不同的类别:

  • 托管钩子:钩子不是入口点。用户必须通过路由器(可能由 Uniswap 提供)与钩子交互。
  • 独立钩子:钩子是入口点,允许用户直接与它们交互。
图 2: 恶意钩子示例
图 2: 恶意钩子示例

托管钩子

在托管钩子的情况下,用户的加密资产(包括原生代币和其他资产)会转账或批准给路由器。由于 PoolManager 强制执行的余额检查,恶意钩子直接收割这些资产并不容易。

然而,潜在的攻击面仍然存在。例如,v4 中的费用管理机制可能被攻击者通过钩子操纵。

独立钩子

当钩子以独立钩子的形式用作入口点时,情况会变得更加复杂。在这种情况下,钩子获得更多权力,因为用户可以直接与它们交互。理论上,钩子可以通过这种交互执行任何它们想执行的操作。

在 v4 情景下,代码逻辑的验证是一个关键点。主要担忧在于代码逻辑是否会被操纵。钩子可以实现为可升级的,这意味着一个看起来“安全”的钩子将来可能会被升级为恶意的,从而构成重大风险。这包括:

  • 可直接利用的可升级代理;
  • 带有自毁逻辑,由于 selfdestructcreate2 的组合使用而可能被利用。

威胁模型 II 的缓解措施

评估钩子是否是恶意的至关重要。具体来说,对于托管钩子,我们应侧重于费用管理行为;而对于独立钩子,主要担忧是它们是否可升级。

结论

在本文中,我们首先简要总结了 Uniswap v4 的核心机制,这些机制与 v4 钩子的安全问题相关。在此之后,我们定义了两个威胁模型,并对各自的安全问题进行了高层次的讨论。在接下来的本系列文章中,我们将详细分析每个威胁模型下的安全问题。敬请关注!

参考

[1] 我们对 Uniswap V4 的愿景

[2] Uniswap v4 白皮书草稿

[3] Uniswap v4 TWAMM Hook

[4] Hook 示例

[5] Awesome Uniswap v4 Hooks

[6] UUPSUpgradeable 漏洞事后分析

阅读本系列的另一篇文章

Best Security Auditor for Web3

Validate design, code, and business logic before launch. Aligned with the highest industry security standards.

BlockSec Audit