使用safe-tx-hashes验证安全的多签名钱包签名 - Cyfrin

  • cyfrin
  • 发布于 2025-01-22 18:50
  • 阅读 39

本文介绍了如何使用Cyfrin的safe-tx-hashes工具来验证安全的多签名钱包签名,以修复Radiant Capital黑客事件的教训。文章强调了在进行多签名交易时,确保每个交易的签名数据的真实性的重要性,并提供了详细的操作步骤和代码示例。

如何验证 Safe 多重签名钱包的签名:Radiant Capital 黑客修复 - 学会这个否则损失 5000 万美元

了解如何使用 Cyfrin 的 safe-tx-hashes 工具验证 Safe 多重签名钱包的签名 - 这是对 Radiant Capital 黑客事件的修复。

非常感谢 pcaversaccio 推动这个并编写原始工具。


Web3 从观看 Radiant Capital 损失 5000 万美元中学到的教训

Radiant Capital 黑客事件(基本上)是这样的:

  1. 他们有一个 Safe 多重签名钱包
  2. 一些签名者的计算机被攻陷
  3. 他们的计算机屏幕显示“良好的”数据,但他们向钱包发送了恶意数据
  4. 他们签署了钱包交易,因为他们不知道如何验证这些交易

这个代价高昂的错误强调了理解和验证多重签名(multi-sig)交易的关键重要性。如果你想参与安全委员会、DAO、事件响应或 DevOps 团队,能够验证签名是一项必须具备的技能。即使你只是想确保自己的个人账户不被攻击。

多重签名钱包以其安全性而闻名,但为了有效利用这种安全性,你需要知道如何验证你正在签署的内容确实是你打算签署的。

让我们学习如何验证你在 Safe 多重签名 过程中的每个部分,以便你不会成为此攻击的受害者。

如果你更愿意观看,我们在 YouTube 上还有一个逐步指导的视频。


学习这个以免你损失 5000 万美元 | 多重签名(Safe)硬件钱包验证 - YouTube

Patrick Collins

162K 订阅者

学习这个以免你损失 5000 万美元 | 多重签名(Safe)硬件钱包验证

Patrick Collins

搜索

信息

购物

轻触以取消静音

如果播放没有很快开始,请尝试重启设备。

你已退出登录

你观看的视频可能会添加到电视的观看历史中并影响电视推荐。为了避免这种情况,请在计算机上取消订阅并登录 YouTube。

取消确认

分享

包含播放列表

检索共享信息时发生错误。请稍后再试。

稍后观看

分享

复制链接

在此观看

0:00

/ •直播

在 YouTube 上观看


开始:下载 safe-tx-hashes 工具

首先,你需要下载这个工具。前往 safe-tx-hashes 仓库下载 safe_hashes 可执行文件。

这个工具使我们能够快速输入安全交易数据,查看我们应该获得的内容,并验证显示在我们钱包中的内容确实是正确的。

在此过程中,首先理解我们做出的安全假设和可能的威胁向量很重要,以便能够避免它们。

使用这个工具,我们已经假设以下工具是安全的:


验证交易:设置

让我们从一个场景开始。

你的团队需要批准 Uniswap 路由器访问你的 USDC 进行交换 - 这是一个相对常见的 DeFi 交易。你的 Safe UI 可能看起来像这样:

交易构建界面提示用户以指定的详细信息和批准选项授权访问代币。

当你滚动到页面底部时,你会看到一个大“签名”按钮。当你点击它时,你的钱包会显示一条消息让你进行验证,这可能看起来像这样:

硬件钱包在其屏幕上显示带有十六进制值的密码学消息。

一个 Trezor 设备显示确认消息,包含十六进制代码和“按住确认”按钮。

这是我们需要验证并确保其操作正确的 Safe 交易消息哈希。那么,它到底在做什么?

这条消息是我们多重签名用于验证交易的 EIP-712 签名。必须有足够的用户以特定格式签署一条消息以使其被接受。如果你想了解更多,我们在博客和 Cyfrin Updraft 中讨论了 EIP-712 和 191

安全哈希的计算如下(在 Solidity 中):

keccak256(abi.encodePacked(bytes1(0x19), bytes1(0x01), bytes32($domain_hash), bytes32($message_hash)))

消息哈希是这样计算的(使用 bash/foundry):

local message=$(cast abi-encode "SafeTxStruct(bytes32,address,uint256,bytes32,uint8,uint256,uint256,uint256,address,address,uint256)" \
       "$safe_tx_typehash" \
       "$to" \
       "$value" \
       "$data_hashed" \
       "$operation" \
       "$safe_tx_gas" \
       "$base_gas" \
       "$gas_price" \
       "$gas_token" \
       "$refund_receiver" \
       "$nonce")

   # 计算消息哈希。
   local message_hash=$(cast keccak "$message")

本质上,这些哈希结合所有关键交易组件以创建唯一签名。这确保当多重签名钱包验证签名时,它能够确定该签名确实对应于该特定交易在该特定钱包实例上。

现在,重要的是我们必须完全确定这个签名包含我们期望的数据。如果有人想让我们签署不同的交易,他们只需更改哈希为 data_hashed 的数据。但是,这将返回一个不同的签名!所以如果我们保持警惕,我们可以发现这一点,而不签署。

因此,我们的过程将是离线重建我们所期望的签名,然后与我们从硬件钱包获取的签名进行比较。


验证交易:未初始化的 Safe API

当你到达 Safe UI 的这个页面时,Safe API 会“保存”这里的数据,以便我们可以调用 API 来填充我们的工具。我们可以简单地运行:

safe_hashes --address <MULTI_SIG_WALLET_ADDRESS> --nonce XX --network xxxx --untrusted

这将从 Safe UI 提取所有数据,并直接显示给我们!包括交易参数和预期签名。例如,你可以运行这个脚本自己查看输出!

safe_hashes --network sepolia --address 0x86D46EcD553d25da0E3b96A9a1B442ac72fa9e9F --nonce 7 --untrusted

你将得到如下输出:

交易数据
多重签名地址:0x86D46EcD553d25da0E3b96A9a1B442ac72fa9e9F
收件人:0x7b79995e5f793A07Bc00c21412e50Ecae098E7f9
值:0
数据:0x095ea7b3000000000000000000000000fe2f653f6579de62aaf8b186e618887d03fa31260000000000000000000000000000000000000000000000000000000000000001
编码消息:0xbb8310d486368db6bd6f849402fdd73ad53d316b5a4b2644ad6efe0f941286d80000000000000000000000007b79995e5f793a07bc00c21412e50ecae098e7f900000000000000000000000000000000000000000000000000000000000000001c62604b0ed9a9ec0e55efe8fb203b3029e147d994854cf0dd8a9fcf5b240d600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
方法:approve
参数:[\
  {\
    "name": "guy",\
    "type": "address",\
    "value": "0xfE2f653f6579dE62aAF8B186E618887d03FA3126"\
  },\
  {\
    "name": "wad",\
    "type": "uint256",\
    "value": "1"\
  }\
]

传统 Ledger 格式
二进制字符串字面量:\xe8\xc9\xfbR\xf3$\\xc5\xe451\x1b\xa4\xd5pAJ4=\x80\xeal7g\xa1jy\xbd*\xaa\x0c7

哈希
域哈希:0xE411DFD2D178C853945BE30E1CEFBE090E56900073377BA8B8D0B47BAEC31EDB
消息哈希:0x27B5FF2CB38C914873DAF41B5A4984C76DB35F1812877F72A9D5DEA6960ED0B1
安全交易哈希:0xe8c9fb52f3245cc5e435311ba4d570414a343d80ea6c3767a16a79bd2aaa0c37

这笔交易是基于 Safe UI 给我们的数据计算的。我们也可以看到这里发送的交易!通过这样,我们可以验证交易是正确的,并确认 安全交易哈希 是否与我们在钱包中看到的匹配。

如果黑客在他们给我们发送恶意交易时接管了 Safe UI/API,我们的脚本将重新计算预期哈希,并且我们会通过检查 method 部分看到交易数据是恶意的,或者我们会看到不同的哈希,我们会拒绝该交易!

然而,如果 Safe API 被攻破,我们就需要手动进行验证过程。


验证交易:没有 Safe API 的未初始化状态

要在没有 Safe API 的情况下验证哈希,我们只需手动将数据添加到我们的工具中,并使用 --offline 标志。像这样(你也可以运行它!):

safe_hashes --offline --data 0x095ea7b3000000000000000000000000fe2f653f6579de62aaf8b186e618887d03fa31260000000000000000000000000000000000000000000000000000000000000001 --address 0x86D46EcD553d25da0E3b96A9a1B442ac72fa9e9F --network sepolia --nonce 6 --to 0x7b79995e5f793A07Bc00c21412e50Ecae098E7f9

数据是使用 cast 工具计算的:

cast calldata "approve(address,uint256) 0xfe2f653f6579de62aaf8b186e618887d03fa3126 1

你可以通过 这个视频 学习更多关于获取 calldata 和验证交易的内容。这将给你如下输出:

交易数据
多重签名地址:0x86D46EcD553d25da0E3b96A9a1B442ac72fa9e9F
收件人:0x7b79995e5f793A07Bc00c21412e50Ecae098E7f9
值:0
数据:0x095ea7b3000000000000000000000000fe2f653f6579de62aaf8b186e618887d03fa31260000000000000000000000000000000000000000000000000000000000000001
编码消息:0xbb8310d486368db6bd6f849402fdd73ad53d316b5a4b2644ad6efe0f941286d80000000000000000000000007b79995e5f793a07bc00c21412e50ecae098e7f900000000000000000000000000000000000000000000000000000000000000001c62604b0ed9a9ec0e55efe8fb203b3029e147d994854cf0dd8a9fcf5b240d600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
跳过解码数据,因为已传递原始数据

传统 Ledger 格式
二进制字符串字面量:!;\xe07'\\x94D\x9a(\xb4\xed\xea\xd7k\x0dc\xc7\xe1+R%\x7f\x9dV\x86\xd9\x8b\x9a\x1a_\xf4

哈希
域哈希:0xE411DFD2D178C853945BE30E1CEFBE090E56900073377BA8B8D0B47BAEC31EDB
消息哈希:0x4BBDE73F23B1792683730E7AE534A56A0EFAA8B7B467FF605202763CE2124DBC
安全交易哈希:0x213be037275c94449a28b4edead76b0d63c7e12b52257f9d5686d98b9a1a5ff4

这样,你甚至不需要信任 Safe API!


结束

一旦交易至少签署过一次,你就可以通过 Safe API 获取其签名,而不需要传递 --untrusted 命令。

然后,当每个人都签署并验证后,你可以再次运行该命令,使用 --print-mst-calldata 标志查看 calldata 应该是什么样子,以便你可以进一步验证实际交易。

你可以在 GitHub 仓库 查看更多示例、文档和 safe_hashes 工具。


最后的想法

确保多重签名交易的安全性不仅仅是使用正确的工具,还包括理解过程及潜在的风险。通过遵循这些指南,你可以有效地保护你的加密货币资产。

请为你和你的团队制定一个验证签名的流程,以免你落入和 Radiant 团队相同的黑客陷阱!

  • 原文链接: cyfrin.io/blog/verify-sa...
  • 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
点赞 0
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
cyfrin
cyfrin
Securing the blockchain and its users. Industry-leading smart contract audits, tools, and education.