Moledao 和 OGBC 联合举办的 Web3 系列课已经进入到第四周的课程了。第二周的课程笔记也为大家整理好了!第二周为 DeFi 之周,内容涵盖了 DEX、新一代无链 DEX、稳定币、钱包、托管和 DeFi 安全。
第二周:DeFi 之周 | DeFi 安全
www.bilibili.com/video/BV1J24y1q7dv/?spm_id_from=333.999.0.0&vd_source=8497d6b38e91681ca1de4fd1c2530a4c
学习收获
Max 导师给大家详细复盘了 2022 年有名的安全事件,还给大家讲解了 DeFi 常见安全漏洞以及预防,最后还给了大家安全建议。
以上数据来自 SlowMist。2022 年被公开的区块链安全事件有 305 起,损失总金额达到 43.78 亿美元。
以上是 2022 年比较有名的区块链安全事件,同时损失也比较大。
黑客常用通过钓鱼等 APT 方式控制组织内一台电脑,然后横向拓展,最终控制整个组织系统的手段。复盘一下,Axie 员工的安全意识比较薄弱,再加上组织内部的安全体系建设工作没有做好,面对国家级黑客组织时没有足够的应对手段导致了这次攻击损失惨重。
此次安全事件主要是项目代码层面的问题。这里用以太坊来举例,以太坊智能合约使用的 solidity 版本是在不断迭代的,一些函数在迭代过程中就会被舍弃。而这次攻击的原因就是合约开发者利用了低版本的函数导致。所以开发人员在开发过程中尽量要使用最新的版本。
跨链互操作协议 Nomad 桥遭受黑客攻击,本次攻击是由于 Nomad 桥 Replica 合约在初始化时可信根被设置为 0x0,且在进行可信根修改时并未将旧根失效,导致了攻击者可以构造任意消息对桥进行资金窃取,攻击者能够从攻击中榨取超过 1.9 亿美元的价值。黑客正是利用该漏洞,找一笔有效交易反复发送构造好的交易数据抽取跨链桥被锁定的资金,从而导致 Nomad 上锁定的资金被几乎全数盗走。
这次事故的本质上是协议初始化设置存在一定问题。如果黑客找到了一笔曾经有效的交易然后重新广播,那这笔交易资金就会被转出。
这次问题暴露出一个问题,现在很多智能合约都是开源的,所以对于黑客来讲,要发现并利用漏洞就很容易。所以一旦代码中有漏洞存在,基本就宣布了项目的失败。
基于以太坊的算法稳定币项目 Beanstalk Farms 此次闪电贷攻击导致的的协议损失约为 1.82 亿美元,具体资产包括 79238241 枚 BEAN3CRV-f、1637956 枚 BEANLUSD-f、36084584 枚 BEAN 和 0.54 枚 UNI-V2_WETH_BEAN。攻击者获 利超 8000 万美元,包括约 24830 枚以太坊以及 3600 万枚 BEAN。本次攻击的主要原因在于提案的投票与执行两阶段间无时间间隔,导致攻击者在完成投票后未经社区审核可以直接执行恶意提案。
这次攻击主要是利用了项目机制。任何人抵押了代币就能提交提案。攻击者在前一天购买了一定数量的代币然后获取了提案资格。在提案之后的 24 小时,提案将会被投票。投票一旦通过,合约将立即执行,没有任何时间锁。于是第二天,黑客通过闪电贷获取了大量代币去投票,将提交的恶意合约通过了。通过执行合约完成套利。
这个事故暴露的问题在于,现在很多社区采用去中心化的治理方式,大家都能提案。那这些提案由谁审核,或者需不需要创建一种机制来预警或者降低损失?这都是社区治理要思考的问题。
9 月 21 日早上,Evgeny Gaevoy 在推特上公布被盗事件进展,称 Wintermute 确实曾于 6 月份使用 Profanity 和一个内 部工具来创建钱包地址。这样做的原因是优化手续费,而不是为了创建靓号。并称在上周得知 Profanity 存在漏 洞后,Wintermute 加速弃用旧密钥,但由于内部 ( 人为 ) 错误,调用了错误的函数,因此 Wintermute 没有删除 受感染地址的签名和执行操作。
以太坊的机制是钱包地址中的 0 越多,那么手续费就越低。Wintermute 使用 Profanity 来创建靓号钱包地址。最后也是 Profanity 的漏洞导致了 Wintermute 的损失。所以用户在使用外部开源工具时,要对这个工具有一个安全评估。如果风险过大,建议还是不要使用。
Horizon 跨链桥损失逾 1 亿美元,包括超 1.3 万枚以太坊及 5000 枚 BNB。Harmony 创始人:Horizon 被攻击是由私钥泄露导致。据彭博社报道,根据区块链研究公司 Elliptic 的最新分析,被称为 Lazarus Group 的疑似朝鲜黑客组织被认为是在 Harmony 跨链桥 Horizon 盗取 1 亿美元的幕后黑手。Elliptic 的分析强调了此次黑客事件中指向 Lazarus Group 的关键因素,包括自动存入 Tornado.Cash 以模拟 Ronin Bridge 事件的程序化洗钱,以及盗窃的时间等。攻击手段很可能和 Ronin Bridge 攻击者一致,即伪装成 HR 在 LinkedIn 上发送钓鱼文件等 APT 手段。
2022 年 12 月 2 日。Deployer update 了合约,然后给 Ankr Exploiter 转了一些 BNB。Ankr Exploiter 通过 更新后合约的铸币方法进行铸币,凭空铸造出 10 万亿枚 aBNBc,黑客 A 将 aBNBc 通过 PancakeSwap 兑换出 500 万枚 USDC,掏空了交易池,导 致 aBNBc 几乎归零。黑客 A 后续将币跨到以太坊,并转入 Tornado Cash。
黑客 A 铸币之后的约半小时,aBNBc 暴跌,产生了套利机会,套利者 B 利用借贷协议 Helio 的预言机 6 小时平均加 时权重的设置,利用 aBNBc 在市场上和在 Helio 系统中的价差将其换成 hBNB,并将 hBNB 质押换出稳定币 HAY,并将其换成 BNB 和 USDC ,总共套出超过 1700 万美元等值的稳定币和 BNB,基本掏空 hay 的交易池。
Ankr 在事故发生后发布了一篇文章,宣称是已离职的员工作恶。从这里可以看出 Ankr 内部安全体系不是很健全。
这个事故暴露了一个问题,现在 DeFi 中有很多项目是堆积木的。这就导致了其中一个项目受影响,那堆叠在一起的所有项目都有可能受影响。
黑客使用了两个账户,一共 1000 万 USDT 起始资金。
第一步,黑客首先向 Mango 交易平台 A、B 地址分别转入 500 万美元。
第二步,黑客通过 A 地址在 Mango 上利用 MNGO 永续合约做空平台 Token MNGO,开仓价格 0.0382 美元,空单头寸 4.83 亿个;与此同时,黑客在 B 地址上做多 MNGO,开仓价格 0.0382 美元,多单头寸 4.83 亿个。( 多空双开的原因在于,Mango 平台深度较差,如果不和自己作对手盘,仓位就很难开到这么高 )。
第三步,黑客转身拉盘多个平台 (FTX、Ascendex) 上 MNGO 的现货价格,致使价格出现 5-10 倍的增长,该价格通过 Pyth 预言机传递到其中 Mango 交易平台,进一步推动价格上涨,最终 Mango 平台上 MNGO 价格从 0.0382 美元拉升至最高 0.91 美元。
第四步,黑客的多头头寸收益为 4.83 亿个 *(0.91 美元 - 0.0382 美元 )= 4.2 亿美元,黑客再利用账户净资产从 Mango 进行借贷;好在平台流动性不足,黑客最终只借出近 1.15 亿美元资产。
攻击发生后,黑客发布了一项新提案,表示希望官方利用国库资金 (7000 万美元 ) 偿还协议坏账。据了解,目前国库资金约为 1.44 亿美元,其中包括价值 8850 万美元的 MNGO Token 以及近 6000 万美元的 USDC。黑客表示,如果官方同意上述方案,将返还部分被盗资金,同时希望不会被进行刑事调查或冻结资金。「如果这个提案通过,我将把这个账户中的 MSOL、SOL 和 MNGO 发送到 Mango 团队公布的地址。Mango 国库将用于覆盖协议中剩余的坏账,所有坏账的用户将得到完整补偿......一旦 Token 如上述所述被送回,将 不会进行任何刑事调查或冻结资金。
据 CoinDesk 报道,此前公布身份的 Mango 攻击者 Avraham Eisenberg 12 月 26 日在波多黎各被捕,Avraham Eisenberg 面临商品欺诈和商 品操纵的指控,这些指控可能会受到罚款、监禁的惩罚。
这起事件严格上来讲不能算是安全事故,因为攻击者利用的是项目方本身的业务模式。所以项目方在设计业务模式的时候要进行充分的威胁建模。
常见漏洞类型一般有闪电贷、价格操纵、函数权限问题、任意外部调用、fallback 函数问题、业务逻辑漏洞、私钥泄漏、重入。
常⻅攻击往往伴随着闪电贷,攻击者喜欢通过闪电贷借出大量资金,对价格进行操纵,攻击业务逻辑等等。
开发者需要考虑,合约功能是否会因为巨额的资金导致功能异常,或通过巨额资金在一笔交易中与合约中多个函数交互获取奖励这些场景。
经常可以看到合约中一些 token 数量的值被用于计算奖励,或是使用 dex 交易对中的 token 数量参与各种计 算,由于开发者在设计这些功能时没有考虑到攻击者可以利用闪电贷操控这些变量,导致合约资金被盗。
价格操纵问题与闪电贷关系密切,这个问题主要是由于价格计算时其中一些参数可以被用户控制,常出现的问题的类型有两种。
一种是计算价格时使用第三方的数据,但使用方式不正确或检查缺失导致价格被恶意操控。另外一种是由于使用了一些地址的 token 数量作为计算变量,而这些地址的 token 余额可以被临时增加或减少。
重入本质上是两行程序的上下顺序有问题。
mapping (address => uint) private userBalances;
function withdrawBalance() public{
uint amountToWithdraw = userBalances[msg.sender];
(bool success,) = msg.sender.call.value(amountToWithdraw)(""); // 如果 msg.sender 是一个合约,那么合约的 fallback 代码会被执行,并可以再次调用 withdrawBalance 完成重入攻击
require(success);
userBalances[msg.sender] = 0;
}
由于用户的余额直到函数的最后才设置为 0,第二次 ( 和以后的 ) 调用仍然会成功,并且会一遍又一遍地提取余额。
针对于不同的合约,重入存在的方式很多,可以结合合约的不同函数或多个不同合约的函数,完成重入攻击,所以我们在解决重入问题时需要注意以下几点:
04 鸣谢
05 关于 Moledao
【免责声明】市场有风险,投资需谨慎。本文不构成投资建议,用户应考虑本文中的任何意见、观点或结论是否符合其特定状况。据此投资,责任自负。