Musyg/eip712-signature-replay-audit
GitHub: Musyg/eip712-signature-replay-audit
一个演示 EIP-712 签名延展性重放漏洞的智能合约安全审查项目,提供可复现的 Foundry PoC 和修复分支对照。
Stars: 0 | Forks: 0
# EIP-712 签名重放(延展性)演示安全审查

这是一个智能合约安全审查的自包含演示:一个故意留有漏洞的无 gas 领取合约,其重放防护以签名字节作为键;由此导致的 ECDSA 延展性双重支付漏洞(已通过通过的 [Foundry](https://book.getfoundry.sh) 概念验证证明);以及一个 `fixed` 分支,在该分支中,相同的场景已被中和。
## 为什么创建此仓库
任何人都可以在个人简介中写下“我审计智能合约”。本仓库展示了实际工作:一个目标、一个具体的发现、一个可执行的证明,以及一个经过验证的修复。如果不可复现,就等于没做。
## 仓库布局
本次审查分布在两个分支中:
| 分支 | 内容 | 绿色的 `forge test` 代表什么 |
|--------|----------|---------------------------------|
| `master` | 存在漏洞的合约及其利用 PoC | 同一个签名支付两次 |
| `fixed` | 修复后的合约及相同场景 | 延展性孪生签名和完全重放均被拒绝 |
- `src/SignatureClaim.sol`,受审查的合约
- `test/SignatureClaim.poc.t.sol`,概念验证
- `EIP712_Signature_Replay_Review.pdf`,完整的书面报告
## 发现
| ID | 严重性 | 摘要 |
|----|----------|---------|
| H-01 | 高 | 签名延展性重放。重放防护存储了 `keccak256(r, s, v)`,但每个 ECDSA 签名对于相同的消息和签名者都有第二个有效编码 `(r, N - s, v ^ 1)`。中继者提交延展性孪生签名,领取操作将执行第二次,针对单次签名授权向接收者支付两次。 |
`master` 上的 PoC 数据:授权者为 `bob` 签署了一份 1,000 ether 的领取请求。中继者提交了该签名,随后又提交了其延展性孪生签名。`bob` 收到了 2,000 ether;多出的 1,000 是在单次授权的情况下从金库中被抽走的。在 `fixed` 分支上,孪生签名被拒绝,`bob` 恰好收到 1,000。
## 复现步骤
需要 [Foundry](https://book.getfoundry.sh/getting-started/installation)。
```
git clone https://github.com/Musyg/eip712-signature-replay-audit.git
cd eip712-signature-replay-audit
forge install
# master: 双花成功
forge test -vv
# fixed: 相同攻击被中和
git checkout fixed
forge test -vv
```
## 修复方案
包含两项标准修改。首先,重放防护以消息摘要作为键,而不是签名字节,因此相同授权的任何第二次编码都会与已使用的摘要发生碰撞。其次,合约通过拒绝 `s` 范围的上半部分和非规范 `v` 来执行 EIP-2,从而在恢复之前拒绝延展性孪生签名。这与 OpenZeppelin 的 `ECDSA` 库行为一致。
## 严重性评级标准
高:直接盗取金库资金。任何能够观察到可中继签名的参与方(每个中继者都可以)都能使任何领取的支付额翻倍。不需要特殊角色,受害者除了签署一次合法授权外,无需进行任何交互。
标签:Foundry, Solidity, 区块链安全, 智能合约, 漏洞分析, 路径探测