---
title: "ERC-1271 签名重放漏洞"
description: "2023年10月27日，Alchemy发现了一个ERC1271合约签名重放漏洞，该漏洞影响了大量智能合约账户（SCA），并导致与多个应用程序交互时存在风险。"
---

# ERC-1271 签名重放漏洞

<ImageBlock
  src="https://media.alchemy.com/1711750158-smart-contract.png"
  alt="智能合约账户签名重放漏洞"
  width={2400}
  height={1260}
  caption="智能合约账户签名重放漏洞"
  priority
/>

2023年10月27日，Alchemy 发现了一个 ERC1271 合约签名重放漏洞，该漏洞影响了大量智能合约账户\(SCA\)，并导致与若干应用程序交互时存在风险。受影响的 SCA 包括我们的 LightAccount 和 OKX 的 SmartAccount，我们识别出存在风险的应用交互包括 Permit2 和 [Cowswap](https://www.alchemy.com/dapps/cowswap)。我们及时将此问题告知了各受影响的 SCA 和应用方，并发现独立安全研究员 [curiousapple](https://twitter.com/0xcuriousapple) 在一个月前就已发现了同样的漏洞。我们与 curiousapple、[Frangio](https://twitter.com/frangio_)\(ERC1271 作者\)、ERC4337 团队以及其他 SCA 技术专家合作制定了修复方案。截至目前，没有资金处于风险之中，对应用的影响也相当有限。所有涉及的 SCA 要么已确认该风险，要么已发布修复。

## 技术细节

### ERC-1271 合约签名

在 Ethereum 及所有基于 Ethereum Virtual Machine \(EVM\) 的链上，存在两种不同类型的账户——外部拥有账户\(EOA\)和智能合约。EOA 能够使用其关联的 ECDSA 密钥对中的私钥对消息进行签名以完成认证。然而，由于智能合约在创建时会获得一个预先确定的地址，它并没有可直接用来对消息签名的私钥。

为了解决这个问题，[ERC-1271](https://eips.ethereum.org/EIPS/eip-1271) 合约签名标准于 2018 年被提出。按照该标准，智能合约可以实现关于何为有效签名的限制/检查逻辑，应用可以调用 `contract.isValidSignature` 来验证某项操作是否经过该智能合约授权。

在智能合约账户\(SCA\)的场景下，ERC-1271 非常实用，它使得 SCA 用户可以像 EOA 用户一样使用基于签名的应用。这些应用包括 [OpenSea](https://www.alchemy.com/dapps/opensea)，以及大多数依赖代币授权\(token approval\)→调用这一使用方式的 DeFi 应用。

### ERC1271 签名重放漏洞

大多数 SCA 都是使用上文所示的参考实现来实现 ERC-1271 的。从工程角度看，这是一种轻量级实现，它使得客户端集成更加简便，因为我们可以像对待 EOA 一样复用 `signTypedData`、`signMessage`、`eth\_signTypedData\_v` 和 `personal\_sign` 等方法。

然而，如果同一地址拥有多个 SCA，且应用没有将交互的来源地址纳入签名范围，那么同一个签名在该应用中对这两个账户都是有效的。

由于该漏洞只有在 SCA 与应用组合的情况下才会出现，其严重程度取决于该交互能与哪些应用配合使用。我们首先研究的应用是 [Permit2](https://github.com/dragonfly-xyz/useful-solidity-patterns/tree/main/patterns/permit2)，它是由 Uniswap 构建的公共基础设施，用于提升整个行业中 [ERC20](https://www.alchemy.com/overviews/erc20-solidity) 代币授权流程的安全性和用户体验，因此目前被广泛使用。

下方代码块展示了 Permit2 签名所覆盖的结构体。值得注意的是，`address owner`（即代币被转出的地址）并未被签名覆盖，而是作为参数在调用 Permit2 时传入。

攻击者利用该签名重放漏洞的过程大致如下：

1. Bob 向拥有 `n` 个 SCA 的 Alice 请求支付 `X` 个代币，并要求通过 Permit2 完成。
1. 在 Alice 签署第一个 permit 后，Bob 可以在 Alice 的所有 SCA 上重放该 permit，从而共获得 `n X` 个代币。

<ImageBlock
  src="https://media.alchemy.com/1711750457-diagram-of-bob-s-attack-on-alice-that-owns-2-scas.png"
  alt="Bob 攻击拥有 2 个 SCA 的 Alice 的示意图"
  width={852}
  height={559}
  caption="Bob 攻击拥有 2 个 SCA 的 Alice 的示意图"
/>

在此过程中，我们制作了一个概念验证\(proof-of-concept\)来确认该漏洞。相关代码可在此处找到：[**replay-sig-poc**](https://github.com/omgwiNNING/replay-sig-poc)​

## 影响

在调查过程中，我们发现：

1. 多个 SCA 存在风险。

1. 除了我们的 LightAccount 外，受影响的 SCA 还包括 Zerodev 的 [Kernel](https://github.com/zerodevapp/kernel/blob/main/src/Kernel.sol)、[Biconomy](https://github.com/bcnmy/scw-contracts)、[Soul Wallet](https://github.com/SoulWallet/soul-wallet-contract)、eth-infinitism 针对 Gnosis Safe 的 [EIP4337Fallback](https://github.com/eth-infinitism/account-abstraction/blob/8215b88768d993fb6459c2723d173791a537a2e7/contracts/samples/gnosis/EIP4337Fallback.sol)、[AmbireAccount](https://github.com/AmbireTech/wallet/blob/main/contracts/AmbireAccount.sol)、OKX 的 [SmartAccount](https://github.com/okx/AccountAbstraction/tree/main/contracts/wallet)、Argent 的 [BaseWallet](https://github.com/argentlabs/argent-contracts/blob/develop/contracts/wallet/BaseWallet.sol)，以及 [Fuse Wallet](https://github.com/fuseio/fuse-wallet-contracts)。
1. 多个应用存在风险：

1. Permit2——基于签名的转账可被重放。但 Permit2 的大多数用途是转发给 Universal Router，要利用这一点还需要 Universal Router 本身存在一个独立的严重漏洞。
1. Cowswap——使用 ERC-1271 路径的交易可被重放。由于签名覆盖了 `address recipient`，因此这里的风险最多是价格过时和/或一定的 MEV 损失。
1. [Gnosis Safe](https://www.alchemy.com/dapps/gnosis-safe) 不受此攻击向量影响。

此时，我们通过一个 telegram 群组将此事披露给相关 SCA 和应用方，并发现 curiousapple 在一个月前也发现了同样的问题，并正在与 Frangio 及其他 SCA 技术专家合作修复。截至目前，受影响的 SCA 与应用组合的完整列表如下所示：

<ImageBlock
  src="https://media.alchemy.com/1711750508-screenshot-2024-02-06-at-1-59-14-pm-1.png"
  alt="受 ERC-1271 签名重放漏洞影响的智能合约账户和应用列表"
  width={637}
  height={587}
  caption="影响范围：账户与应用"
/>

注：对于 Argent，由于其是一个移动端应用，且签名者是按设备生成的，因此同一个 EOA 不可能拥有 2 个 SCA，所以签名重放攻击对 Argent 无效。然而，那些 fork 了 Argent 合约却没有 fork 其整体架构的项目可能存在风险，应采用 Argent 的钱包架构，或发布修复。

## 修复方案

共提出了两种 SCA 修复方案。SCA 开发者应注意，需实现以下两种方案之一以防止上述重放攻击：

两种方案均可防止 ERC-1271 签名重放攻击。后者更为轻量，但这意味着钱包客户端必须向用户展示一个不透明的哈希值供其签署。而前一种修复方案能更容易地确保签名对用户而言不是不透明的，这也是我们为 LightAccount 选择前一种修复方案的原因。大多数其他 SCA 也选择了相同的修复方案。

## 致谢

非常感谢 OKX 为此问题向 Howy 支付了漏洞赏金！

祝贺 [curiousapple](https://twitter.com/0xcuriousapple) 从 Ambire、Instadapp、Biconomy 和 Cowswap 获得了漏洞赏金！

此外，特别感谢：

1. [Dror Tirosh](https://twitter.com/drortirosh) 提出了大多数 SCA 采用的 EIP-712 结构体修复方案的思路
1. [Frangio](https://twitter.com/frangio_) 分享了关于 ERC-1271 的更多背景信息，并大力推动通过 EIP 委员会更新 ERC-1271 的参考实现
1. [Ivo \(Ambire\)](https://twitter.com/ivshti) 深入研究了两种提议方案在技术实现上的差异
1. [Vectorized](https://twitter.com/optimizoor) 为嵌套 EIP-712 方案的客户端实现悬赏并出资 0.5 ETH
1. [Juno \(ChainLight\)](https://twitter.com/junorouse) 接受了上述挑战，发布了[嵌套 EIP712 方案的客户端实现](https://github.com/junomonster/nested-eip-712)并获得了 Vectorized 的赏金
1. [David Eiber](https://twitter.com/eiber_david) 协助头脑风暴相关漏洞、整理受影响的 SCA 和协议清单，并制作概念验证
1. [Yoav Weiss](https://twitter.com/yoavw) 在整个过程中提供了帮助，包括帮我们联系安全研究员以及其他受影响的 SCA 和应用方
