2016年6月17日,合约以太坊上那个叫 The 安全DAO 的项目被掏空了360万枚ETH。按当时价格算,漏洞大约5000万美元。合约攻击者没有破解任何密码,安全没有攻破任何服务器。漏洞他只是合约调用了一个函数,反复调用,安全像拧开水龙头一样把钱放走了。漏洞
这就是合约智能合约安全最讽刺的地方——代码完全按你写的执行,问题恰恰出在你写的安全东西上。
The DAO 的漏洞在 Solidity 里叫 reentrancy。简单说:合约在给用户转账之前,合约没有先把用户的安全余额清零。攻击者写了一个恶意合约,漏洞在收到ETH的那一刻——也就是转账还没结束的时候——重新调用提款函数。因为余额还没清零,合约认为他还能提。如此循环几十次,池子就干了。
修复方式其实不复杂:先改状态,再转账。或者加一个互斥锁。但 The DAO 写代码的时候,没人觉得这是个问题。这行代码看起来太正常了。
我见过不少审计报告,重入漏洞至今仍然排在出现频率前三。2022年,Solana 上的 Crema Finance 因为类似逻辑被薅走880万美元。链换了,语言换了,思路没换。
Solidity 的 uint256 有范围。你从0减1,不会得到-1,会得到 2^256-1。听起来像个数学冷知识,但在合约里,这意味着一个余额为0的账户可以瞬间拥有天量代币。
2018年,BEC(美蜜币)就栽在这上面。攻击者利用批量转账函数里的乘法溢出,凭空生成了天文数字的代币,然后砸盘。币价归零,交易所紧急下架。
SafeMath 库后来成了标配。Solidity 0.8 之后,溢出检查直接内置了。但老合约还在链上跑,升级不了的合约就是定时炸弹。
有些合约的初始化函数没有加权限控制。部署之后,任何人都能调用它,重新设置 owner。然后owner就能提走合约里所有资金。
2021年,Poly Network 被攻击,6亿美元资产被转移。漏洞出在跨链合约的权限验证逻辑上——攻击者伪造了验证者的签名,让合约以为他是合法的跨链消息发送方。Poly Network 最后追回了大部分资产,但那是运气好,攻击者主动还了。
更多时候,钱走了就是走了。
闪电贷本身不是漏洞,但它把攻击门槛降到了零。你不需要自有资金,借一大笔钱,在一个交易里完成攻击,还回去,净赚差价。
2022年,Beanstalk Farms 被闪电贷攻击,1.82亿美元没了。攻击者借了大量资金,瞬间获得治理投票权,通过了一个恶意提案,把国库的钱转给自己。整个流程在一个以太坊区块里完成,大约13秒。
这种攻击的可怕之处在于:你的合约代码可能没有任何传统意义上的漏洞。但经济模型设计有缺陷,治理机制可以被临时操纵。代码安全了,逻辑没安全。
开发者想的是:这个函数应该怎么用。攻击者想的是:这个函数能被怎么用。
我翻过一些开源合约,注释写得很清楚,变量命名规范,代码风格漂亮。然后你发现 approve 函数没有检查返回值,transferFrom 的授权额度没有上限,fallback 函数里写了复杂逻辑。每一个都是入口。
审计能发现一部分问题,但审计不是证明。审计报告写“未发现严重漏洞”,不等于没有漏洞。它只是说,在审计的那几天里,那几个人,用他们的知识储备,没找到。
链上代码是公开的。攻击者可以慢慢看,看几个月,看一年。你没法打补丁,没法热修复,没法回滚。部署那一刻,代码就死了,只有攻击者还在研究它的尸体。