Paradigm 以低至 100 万美元和高达 1 亿美元的资金支持颠覆性的加密公司、协议和货币。
每隔一段时间,就会出现一项改变一切的新技术。互联网定义了过去几十年的创新。我们相信加密将定义未来几十年。
Paradigm 是一家专注于支持未来伟大的加密货币公司、协议和货币的投资公司。我们的方法是灵活的、长期的、多阶段的和全球性的。我们经常在形成的最早阶段参与进来,并随着时间的推移用额外的资金支持我们的投资组合。
从技术(机制设计、智能合约安全、工程)到运营(招聘、监管策略),我们采取深入的实践方法来帮助项目充分发挥潜力。
寿司差点出大事,幸好有白帽老哥 samczsun 的及时发现。不然,说不定过一段时间又要有丢几个亿美金的大新闻了。感叹一下,Paradigm 真的牛,资金我投,项目我帮忙搞,人我帮忙招,安全我也顺带帮你弄了,不要这样,给其它夸夸群的VC们留条活路,他们连吹牛都还结巴呢。
微博@DeFi_Panda
https://weibo.com/6886470115/KtZvFr46L
sushi miso上演了一场惊心动魄却是有惊无险3.5亿美金的营救活动,根据著名白帽samczcun,主要代码的bug是batch 的代理call,里面的msg sender和value是持久的,也就是说可以用一个msg来免费参与所有竞拍
https://weibo.com/7514679219/KtZjnEgw2
@0x泰迪
原文链接:www.paradigm.xyz/2021/08/two-rights-might-make-a-wrong/
两种权利可能会出错
8.17.2021 | 三星
内容
构建软件时的一个常见误解是,如果系统中的每个组件都经过单独验证是安全的,那么系统本身也是安全的。这种信念在 DeFi 中得到了更好的体现,在 DeFi 中,可组合性是开发人员的第二天性。不幸的是,虽然组合两个组件在大多数情况下可能是安全的,但只需要一个漏洞即可对数百甚至数千名无辜用户造成严重的经济损失。今天,我想告诉你我如何发现并帮助修补了一个使超过 109,000 ETH(按今天的汇率计算约为 3.5 亿美元)面临风险的漏洞。
相遇
上午 9:42
当我注意到@ivangbi_和@bantg之间就 SushiSwap 的 MISO 平台上的新加薪进行了讨论时,我正随意浏览 Telegram 上的 LobsterDAO 组。我通常会尽量避免在公共场合发生戏剧性事件,但我忍不住在谷歌上进行了快速搜索以了解它的全部内容。我得到的结果对我来说并不是特别有趣,但我继续前进,因为我觉得如果我继续寻找,就会发现这里有一些有趣的东西。
MISO 平台运营两种类型的拍卖:荷兰式拍卖和批量拍卖。在这种情况下,加薪是通过荷兰式拍卖进行的。当然,我做的第一件事就是在Etherscan上打开合约。
上午 9:44
我根据参与协议快速浏览了 DutchAuction 合同并检查了每个有趣的功能。提交函数(commitEth
、commitTokens
、 和commitTokensFrom
)似乎都正确实现了。拍卖管理功能(setDocument
、setList
等)也有适当的访问控制。但是,在底部附近,我注意到该initMarket
功能没有访问控制,这非常令人担忧。此外,initAuction
它调用的函数也不包含访问控制检查。
不过,我真的没想到这是一个漏洞,因为我没想到 Sushi 团队会犯下如此明显的失误。果然,该initAccessControls
函数验证了合约尚未初始化。
然而,这让我有了另一个发现。在滚动浏览所有文件时,我注意到了SafeTransfer
和BoringBatchable
库。我对这两者都很熟悉,我立即对BoringBatchable
图书馆引入的潜力感到震惊。
对于那些不熟悉的人,这BoringBatchable
是一个 mixin 库,它旨在轻松地将批处理调用引入任何导入它的合约。它通过为输入中提供的每个调用数据对当前合约执行委托调用来实现这一点。
function batch(bytes[] calldata calls, bool revertOnFail) external payable returns (bool[] memory successes, bytes[] memory results) {
successes = new bool[](calls.length);
results = new bytes[](calls.length);
for (uint256 i = 0; i < calls.length; i++) {
(bool success, bytes memory result) = address(this).delegatecall(calls[i]);
require(success || !revertOnFail, _getRevertMsg(result));
successes[i] = success;
results[i] = result;
}
}
看看这个函数,它似乎被正确地实现了。然而,在我脑海的角落里,有什么东西在对我唠叨。那时我意识到我过去曾见过非常相似的东西。
发现
上午 9:47
一年多前的今天,我在与 Opyn 团队的 Zoom 通话中,试图弄清楚如何在遭受毁灭性黑客攻击后恢复和保护用户资金。hack 本身很简单但很巧妙:它使用一次 ETH 支付来行使多个选项,因为 Opyn 合约msg.value
在循环中使用了该变量。虽然处理代币支付涉及transferFrom
每次循环迭代的单独调用,但处理 ETH 支付只是检查是否msg.value
足够。这允许攻击者多次重复使用相同的 ETH。
我意识到我正在以不同的形式查看完全相同的漏洞。在委托调用中,msg.sender
并且msg.value
被持久化。这意味着我应该能够批量调用多个调用commitEth
并msg.value
在每个承诺中重复使用我的,允许我在拍卖中免费出价。
上午 9:52
我的直觉告诉我这是真实的交易,但我无法确定没有验证。我迅速打开 Remix 并编写了一个概念验证。令我沮丧的是,我的主网分叉环境完全被破坏了。我一定是在伦敦硬分叉期间不小心弄坏了它。尽管有多少资金处于风险之中,但我没有足够的时间。我很快在命令行上拼凑了一个穷人的主网叉,并测试了我的漏洞。有效。
上午 10:13
在报告之前,我给我的同事Georgios Konstantopoulos 打了电话,让他们再看一遍。在等待回应的同时,我又回到合同中寻找提高严重性的方法。能够免费参加拍卖是一回事,但能够窃取所有其他出价则是另一回事。
我注意到在我最初的扫描过程中有一些退款逻辑,但没想到。现在,这是一种让 ETH 脱离合约的方法。我很快检查了我需要满足哪些条件才能获得合同为我提供退款。
令我惊讶(和恐惧)的是,我发现发送的任何超过拍卖硬上限的 ETH 都会获得退款。即使达到硬上限,这也适用,这意味着合约不会完全拒绝交易,而是简单地退还您的所有 ETH。
突然间,我的小弱点变得更大了。我不是在处理一个让你出价超过其他参与者的错误。我在看一个价值 3.5 亿美元的错误。
披露
上午 10:38
在与 Georgios 确认错误后,我让他和Dan Robinson尝试联系 Sushi 的 Joseph Delong。几分钟后,Joseph 做出了回应,我发现自己与 Georgios、Joseph、Mudit、Keno 和 Omakase 打了一个 Zoom 电话。我很快就漏洞向参与者进行了汇报,然后他们离开以协调响应。整个通话只持续了几分钟。
上午 11:10
约瑟夫带着 Google Meet 室回到乔治斯和我身边。我加入的时候,Georgios 正在对 Joseph、Mudit、Keno 和 Omakase 以及来自 Immunefi 的 Duncan 和 Mitchell 进行汇报。我们很快讨论了下一步。
我们有三个选择:
- 离开合同,希望没人注意到
- 使用漏洞抢救资金,可能使用 Flashbots 隐藏交易
- 通过购买剩余的分配并立即完成拍卖来挽救资金,需要管理员权限
经过一些快速辩论,我们决定选项 3 是最干净的方法。我们分成不同的房间,以便分别处理通信和操作。
准备
上午 11:26
在行动室里,穆迪特、基诺、乔治斯和我正在忙着写一份简单的救援合同。我们决定最干净的做法是进行快速贷款,购买到硬上限,完成拍卖,然后使用拍卖本身的收益偿还快速贷款。这不需要前期资金,这非常好。
下午 12:54
我们遇到了一个问题。原本应该是简单的救援行动,却变成了无法拆除的滴答作响的定时炸弹,因为又是一个活跃的拍卖会。这是一次批量拍卖,这意味着我们不能只购买硬上限,因为根本没有硬上限。幸运的是,没有硬性上限也意味着无法从合约中提取 ETH,因为没有提供退款。
我们很快讨论了对第一个合同执行白帽救援的利弊,最终决定,即使批量拍卖承诺了 800 万美元,这 800 万美元没有风险,而原始荷兰语中的 3.5 亿美元拍卖仍然处于危险之中。即使有人被我们强制停止荷兰式拍卖而得到消息,并在批量拍卖中发现了错误,我们仍然会节省大部分资金。发出了继续的呼吁。
下午 1:36
当我们完成救援合同的工作时,我们讨论了批量拍卖的后续步骤。Mudit 指出,即使在拍卖进行时也可以设置积分列表,并且在每次 ETH 承诺期间都会调用它。我们立即意识到这可能是我们正在寻找的暂停功能。
我们集思广益,想出了不同的方法来使用这个钩子。立即恢复是一个明显的解决方案,但我们想要更好的东西。我考虑添加一个检查,每个源只能为每个块做出一个承诺,但我们注意到该函数被标记为视图,这意味着 Solidity 编译器将使用静态调用操作码。我们的钩子不允许进行任何状态修改。
经过一番思考,我意识到我们可以使用积分列表来验证拍卖合约是否有足够的 ETH 来匹配所做的承诺。换句话说,如果有人试图利用这个漏洞,那么承诺会比 ETH 多。我们可以很容易地检测到这一点并恢复交易。Mudit 和 Keno 开始编写测试以进行验证。
救援
下午 2:01
通信突围团队与运营突围团队合并以同步进度。他们已经与执行加薪的团队取得了联系,但该团队希望手动完成拍卖。我们讨论了风险并同意自动化机器人注意到交易或能够对其采取任何行动的可能性很小。
下午 2:44
执行加注的团队完成了拍卖,消除了直接威胁。我们互相祝贺,然后分道扬镳。批量拍卖将在当天晚些时候结束,但不会大张旗鼓。信任圈之外没有人知道刚刚避免了多大的危机。
反思
下午 4:03
过去的几个小时感觉很模糊,几乎没有时间过去。我花了半个多小时从遭遇到发现,20 分钟内披露,另外 30 分钟内作战室,三个小时内修复。总而言之,只用了五个小时就保护了 3.5 亿美元不落入坏人之手。
即使没有金钱损失,我相信所有参与其中的人都更愿意一开始就没有经历过这个过程。为此,我有两个主要的要点给你。
首先,msg.value
在复杂系统中使用很困难。它是一个全局变量,您无法更改并在委托调用中保持不变。如果您msg.value
用来检查付款是否已收到,则绝对不能将该逻辑置于循环中。随着代码库复杂性的增加,很容易忘记发生的位置并意外地在错误的位置循环某些内容。虽然包装和解开 ETH 很烦人,并引入了额外的步骤,但 WETH 和其他 ERC20 代币之间的统一接口可能是值得的,如果这意味着避免这样的事情。
其次,安全组件可以组合在一起使某些东西变得不安全。我之前曾在可组合性和 DeFi 协议的背景下宣扬过这一点,但这次事件表明,即使是安全的合约级组件也可能以产生不安全的合约级行为的方式混合。这里没有像“检查-效果-交互”这样的包罗万象的建议,所以您只需要了解新组件引入了哪些额外的交互。
我要感谢 Sushi 的贡献者 Joseph、Mudit、Keno 和 Omakase 对这个问题的快速响应,以及我的同事 Georgios、Dan 和 Jim 在整个过程中提供的帮助,包括审阅了这篇文章。
免责声明:本文仅用于一般信息目的。它不构成购买或出售任何投资的投资建议或推荐或招揽,不应用于评估做出任何投资决定的优点。不应依赖它提供会计、法律或税务建议或投资建议。这篇文章反映了作者的当前观点,并不代表 Paradigm 或其附属公司,也不一定反映 Paradigm、其附属公司或与 Paradigm 相关的个人的意见。此处反映的意见如有更改,恕不更新。
AD:【微信:shentucao123 推特:shentuao 微博@区块链神吐槽Q:2570730399 tucaod.com】