{"content":{"title":"发现智能合约中的 bug 的 7 个方法","body":"寻找智能合约 bug 可能是一项高回报的工作，而且它也保护了生态系统免受黑客攻击。我最近有幸[采访了一位开发人员](https://twitter.com/leonspacewalker)，他发现了一个价值 70 亿美元的错误，并因报告该错误而[获得了 220 万美元](https://medium.com/immunefi/polygon-lack-of-balance-check-bugfix-postmortem-2-2m-bounty-64ec66c24c7d)的报酬。\r\n\r\n在这篇文章中，我将详细介绍该开发人员发现的 bug 的过程，以及它如何有可能损害 70 亿美元的价值，然后再提供一些可帮助你查找错误的策略和工具。\r\n\r\n让我们开始吧。\r\n\r\n## Polygon 智能合约 bug 案例\r\n### 背景\r\n2020 年 5 月 31 日，[Matic 区块链上线](https://crypto.news/matic-network-mainnet-live/)（Matic 后来更名为 [Polygon](https://polygon.technology/)）。 Polygon 是一种与 EVM 兼容的区块链，以其低 gas 费用和短块时间而闻名。该链最近开始探索 [zk-rollup 技术](https://www.alchemy.com/overviews/polygon-zk-rollups)。\r\n\r\n如果你查看 Polygon 的[“创世”区块](https://polygonscan.com/block/0)，即区块链的第一个区块，你将看到 10 笔交易。其中一笔交易创建了一个名为 MRC20 的合约。\r\n\r\n\r\n![bughunting-1.png](https://img.learnblockchain.cn/attachments/2022/11/yEUkygWo6384a9c4ed67d.png)\r\nPolygon 创世区块\r\n\r\n这个智能合约是什么？\r\n\r\n当我们发送原生区块链通证时，我们必须花费 gas。因此，Polygon 团队部署了一个合约，允许你签名一项交易以向某人发送 ETH，而其他人则可以支付这笔交易的 gas 费。这种功能被称为“元交易”，随着 [EIP-712](https://eips.ethereum.org/EIPS/eip-712) 的推出而普及。\r\n\r\n你可以看到，该合约获得了近 100 亿个 MATIC 通证，以帮助促进这些无需 gas 的交易。然而，这个设计精巧的合约却包含一个 bug ，而这个 bug 可能会被利用来盗走全部余额！\r\n\r\n2021 年 12 月 3 日，故事的主人公、伪匿名开发者 [Leon Spacewalker](https://twitter.com/leonspacewalker) 向 [Immunefi bug 赏金计划](https://immunefi.com/bounty/immunefi/)提交了一份报告，详细介绍了这个函数的问题。第二位英雄，我们称之为 Whitehat2，也在一天后报告了该 bug 。\r\n\r\n在该链于 [2021 年 12 月 5 日](https://github.com/maticnetwork/bor/releases/tag/v0.2.12)最终分叉、回滚和修复之前，大约有 800,000 个 MATIC 被盗。\r\n\r\n这次事件给我们抛出了很多问题： bug 是什么？它是如何长期未被发现的？它是怎么被发现的？\r\n\r\n###  bug 利用\r\n以下是发送无 gas 交易的函数\r\n```\r\n   function transferWithSig(\r\n       bytes calldata sig,\r\n       uint256 amount,\r\n       bytes32 data,\r\n       uint256 expiration,\r\n       address to\r\n   ) external returns (address from) {\r\n       require(amount > 0);\r\n       require(\r\n           expiration == 0 || block.number <= expiration,\r\n           \"Signature is expired\"\r\n       );\r\n\r\n       bytes32 dataHash = getTokenTransferOrderHash(\r\n           msg.sender,\r\n           amount,\r\n           data,\r\n           expiration\r\n       );\r\n       require(disabledHashes[dataHash] == false, \"Sig deactivated\");\r\n       disabledHashes[dataHash] = true;\r\n\r\n       from = ecrecovery(dataHash, sig);\r\n\r\n       _transferFrom(from, address(uint160(to)), amount);\r\n   }\r\n```\r\n乍一看，它似乎无害：它需要用户的签名、有多少通证、他们想将通证发送给谁、进一步的数据，以及交易的到期日期。\r\n\r\n它也有一些限制，获取数据哈希以发送元交易，确保数据哈希未被使用，并执行这个 `ecrecovery` 函数。\r\n\r\n这个函数本质上是 [Solidity ecrecover 函数](https://docs.soliditylang.org/en/latest/cheatsheet.html?highlight=ecrecover#global-variables)的 Wrapper。\r\n\r\n\r\n![bughunting-2.png](https://img.learnblockchain.cn/attachments/2022/11/xFtlxL3I6384aa4a74157.png)\r\nSolidity ecrecover 函数的 wrapper\r\n\r\nHere’s the actual code:\r\n我们这个函数验证签名交易的来源。你会注意到，即使在 Solidity 文档中，它也说它将“错误的返回值为零”。 ecrecovery 函数也一样，如果有问题，它会返回 0。正如许多开发人员所知，这可能是有风险的。如果它在出错时返回零，那意味着我们应该检查以确保返回的地址不为零，对吗？\r\n\r\n这是实际的代码：\r\n\r\n\r\n![bughunting-3.png](https://img.learnblockchain.cn/attachments/2022/11/1JlZ21eG6384aa5c019b9.png)\r\n\r\n这是理想中的代码：\r\n\r\n\r\n![bughunting-4.png](https://img.learnblockchain.cn/attachments/2022/11/haaz87AB6384aa649ad52.png)\r\n\r\n我们其实没有对地址执行检查以确保它不会导致错误，好吧。 `transferWithSig` 函数中的最后一行代码执行实际的转账，我们肯定要在那里执行某种检查，对吧？\r\n```\r\nfunction _transfer(address sender, address recipient, uint256 amount)\r\n   internal\r\n{\r\n   require(recipient != address(this), \"can't send to MRC20\");\r\n   address(uint160(recipient)).transfer(amount); // It just sends the money!\r\n   emit Transfer(sender, recipient, amount);\r\n}\r\n``` \r\n\r\n`_transferFrom` 函数刚刚调用了我们的 `_transfer` 函数，如上所示。你会注意到它不会检查，以确保 `from` 地址有足够的余额。\r\n\r\n这意味着有人可以发送无效签名，这将导致从 ecrecovery 返回零地址，但 MRC20 合约仍会向 `to` 地址发送一定数量的通证。这就是 9,999,993,000 MATIC 被盗走的方式，因为 MRC20 合约直接从自身发送通证！\r\n\r\n如果在合约中检查，以确保发件人地址有足够的余额用于此签名交易，就可以避免此问题。\r\n\r\n## 为什么智能合约 bug 长期未被发现\r\n令我感到奇怪的是，在该 bug 潜伏了将近一年半之后，它在几天之内就被另一位白帽子 Leon 和一名黑客发现了。\r\n\r\n好像有猫腻的样子，但 Immunefi 团队告诉我，这种情况经常发生。某些 bug 被利用可能会因一篇文章、文章或[挑战](https://github.com/code-423n4/2021-09-swivel-findings/issues/61)而变得流行，然后人们开始寻找该 bug ，导致多个人同时找到它。\r\n\r\n但更有可能的是，事实证明 Polygon 在这段时间左右在 Polygonscan 上验证了合约——那也是人们真正开始关注它的时候。\r\n\r\n也许还有更多的故事，但也许不是。\r\n\r\n无论如何，让我们把这个 bug 作为一个教学案例，看看 Leon 和其他 bug hunter 用来发现 bug 的一些技能，帮助保护 Web3 生态系统。\r\n\r\n## 7 发现合约 bug 的技巧\r\n现在，我们将学习 Leon 和其他 bug hunter 用来发现这些 bug 并申请 bug 赏金的技能。此提示列表假定你已经了解智能合约的基础知识，所以是的，[学习 Solidity ](https://www.youtube.com/watch?v=gyMwXuJrbJQ&t=23615s)是先决条件。\r\n\r\n请[有道德](https://www.eccouncil.org/ethical-hacking/)地去使用这些技巧，请记住[负责任地披露](https://www.hackerone.com/vulnerability-management/what-responsible-disclosure-policy-and-why-you-need-one)你发现的任何 bug 。\r\n\r\n许多查找 bug 的工作来自于查看代码和运行诸如 [slither](https://github.com/crytic/slither) 之类的工具。对于这笔 220 万美元的收获，Leon 表示他能够通过逐行查看智能合约代码来找到 bug ，所以请记住，发现 bug 通常需要大量的人工手动操作！\r\n\r\n除了下面的实用技巧外，Leon 最大的收获是让智能合约 bug hunter “找到你的优势”，什么意思？通常，这意味着找到让你有别于其他人的东西。作为一个社区，我们需要覆盖智能合约空间的每一个角落，所以找到你特别擅长和擅长的部分。\r\n\r\n这里有七个策略和技巧可以帮助你找到优势，让你变成成功的智能合约的 bug hunter。\r\n\r\n### 1. 找到一个项目然后搜索 bug\r\n找到错误的第一种方法是详细地了解协议工作原理。这是每个智能合约 bug hunter 需要学习的首要技能之一：端到端理解协议的能力。\r\n\r\n浏览文档，尝试自己重新实现协议，并在区块浏览器上通过该协议查看交易。\r\n\r\nLeon 说这个策略对其他 bug hunter 有效，但对他无效。他专注于接下来的三个，但对于每个 bug hunter 来说，能够做到这一点很重要。\r\n\r\n### 2. 找到 bug 然后搜索项目\r\n寻找 bug 的一种更简单的方法是：找到一个鲜为人知的 bug，然后尝试查看哪些协议的实现中包含这个 bug。\r\n\r\n这种策略需要大量研究，因为有很多人[致力于向公众公开 bug](https://swcregistry.io/)。\r\n\r\n你首先需要了解所有基本的智能合约 bug ，然后是它们的高级版本。你需要了解最佳实践并查看是否有未遵循的协议。\r\n\r\n一旦发现智能合约 bug，你认为很多项目可能无法防范，就开始搜索该 bug。直到真正熟悉这个新的 bug 以及如何找到它。一定写博客或某种帖子来帮助遇到此 bug 的其他智能合约开发人员。\r\n\r\n### 3.要快\r\n希望 bug hunter 查看其智能合约的项目需要注册像 [Immunefi](https://immunefi.com/) 这样的漏洞赏金计划。你会想成为第一批发现新赏金的开发者之一。如果你比其他猎人先开始查看合约，你将有更多时间找到漏洞。\r\n\r\n有几种方法可以加快速度——Leon 能够在其他人之前找到智能合约漏洞的方法之一是通过 [Immunifi Discord 频道](https://discord.gg/nVE8WqH3MH)的通知。每当有新项目进入或项目更新时，他都会收到通知。像这样的工具可以帮助你抢在其他人之前深入研究代码。\r\n\r\n### 4. 要有创意\r\nLeon 获得优势的另一种方式是查看大量的社区论坛，发现他们正在考虑提交 bug。然后他甚至在赏金获得批准之前就开始查看智能合约。这让他比其他开发人员有更多时间查看合约，因为别人会等待项目方 bug 赏金提交成功。\r\n\r\n### 5. 了解你的工具\r\nBug hunter 要使用 VSCode Solidity 的 extension、Hardhat、Foundry、Brownie、Dune、Etherscan 以及许多其他工具。\r\n\r\n一种的 bug 查找策略可能是加载 VSCode，使用 Solidity extension 将代码添加到 VSCode，然后逐行查找常见错误或有漏洞的代码实现。\r\n\r\n找到潜在漏洞后，设置测试环境以对合约运行测试。你通常可以重用协议开发人员最初使用的大量测试。\r\n\r\n### 6. 不要放弃审计过的项目\r\n这里就不多说了。审计公司会犯错误。 Leon 发现漏洞的许多项目都已经过顶级公司的审计。\r\n\r\n使用我们在此博客中讨论的技巧可以帮助你找到这些问题！\r\n\r\n### 7. 行业特定知识\r\n你最大优势之一可能就是专注于特定的细分市场。如果你非常了解一个领域，那么你将拥有了解所有函数如何相互调用的知识。相反，如果你是一个厉害的智能合约漏洞专家，但对 DeFi 一无所知，那么很难找到 DeFi 合约中的漏洞。例如，许多开发人员了解代码，但不了解财务术语。\r\n\r\n你可能非常擅长理解去中心化交易所、借贷协议，或者只是 NFT！\r\n\r\n如果你能成为安全专家和 Web3 中某个垂直领域的专家，你将处于有利地位，在其他寻找漏洞的人面前占据优势。\r\n\r\n## 总结\r\n我希望这篇文章在你的智能合约 bug 搜索之旅中帮助到你。如果你想在编写智能合约时了解更多有关安全的信息，请务必查看[十大 DeFi 安全最佳实践](https://blog.chain.link/defi-security-best-practices/)。\r\n\r\n而且，一如既往，我希望看到你在那里建设并保持生态系统更安全。\r\n\r\n相关链接:\r\n\r\n[MRC20 contract.](https://polygonscan.com/address/0x0000000000000000000000000000000000001010#code)\r\n\r\n[Immunefi writeup. ](https://medium.com/immunefi/polygon-lack-of-balance-check-bugfix-postmortem-2-2m-bounty-64ec66c24c7d)\r\n\r\n[Change to Polygon contracts.](https://github.com/maticnetwork/contracts/commit/55e8118ad406c9cb0e9b457ca4f275c5977809e4#diff-cc4ed03464edad9d87d48cff647eb6940dfe9a4c419f63e3994bdc91b01bfecb)\r\n\r\n[Previous Polygon contracts.](https://github.com/maticnetwork/contracts/blob/56ec7eb257ce10a9f70621f56f6e3f37eb8e0c57/contracts/child/MRC20.sol)\r\n\r\n[Ecrecovery challenge.](https://github.com/code-423n4/2021-09-swivel-findings/issues/61)\r\n\r\n这篇文章中表达的观点仅代表作者，并不反映 Chainlink。\r\n\r\n欢迎关注 Chainlink 预言机并且私信加入开发者社区，有大量关于智能合约的学习资料以及关于区块链的话题！"},"author":{"user":"https://learnblockchain.cn/people/398","address":null},"history":null,"timestamp":1669638783,"version":1}