第 1 篇:The DAO——重入的原型
2016 年 6 月,约 360 万 ETH 从 The DAO 流出,直接导致以太坊硬分叉与 ETH/ETC 分裂。漏洞只是一个语句顺序问题:转账写在了余额清零之前。这一篇拆开它的完整调用栈,说明为什么「转账」在 EVM 里等于「交出控制权」,并把重入的六个变体逐一列出——其中至少三个在今天仍然被写错。
2016 年 6 月,约 360 万 ETH 从 The DAO 流出,直接导致以太坊硬分叉与 ETH/ETC 分裂。漏洞只是一个语句顺序问题:转账写在了余额清零之前。这一篇拆开它的完整调用栈,说明为什么「转账」在 EVM 里等于「交出控制权」,并把重入的六个变体逐一列出——其中至少三个在今天仍然被写错。
2020 年 4 月,Lendf.Me 被重入掏空约 2500 万美元。它的代码 fork 自当时被认为安全的 Compound,一行没改错——问题是团队自己上架了一个 ERC-777 代币,而这个标准在转账时会回调转出方。这一篇讲清楚「代码正确」和「前提成立」的区别,并列出十种会打破 ERC-20 心智模型的代币。
一个只读的 view 函数,不改任何状态,却让一批协议在 2022–2023 年间连续损失数千万美元。原因是:被读的那个协议在自己的函数执行到一半时,把控制权交了出去,于是外部读到了一个「中间状态」。最反直觉的是受害者——出事的从来不是被读的那个协议,而是读它的人。
2017 年 7 月,约 15 万 ETH 从 Parity 多签钱包被取走。漏洞是一个本该只被调用一次的初始化函数,没有任何访问控制——任何人都能把自己设成钱包的 owner。这一篇拆解 delegatecall 的存储语义为什么让这个错误变得致命,并给出「代理模式下哪些函数必须被锁死」的完整清单。
2017 年 11 月,有人在无意中把 Parity 的新版钱包库据为己有,然后销毁了它。所有依赖这个库的钱包同时变砖,约 51.3 万 ETH 至今取不出来。这一篇讲三件事:实现合约本身也是一个活合约、可用性失效比资金失窃更不可逆,以及 Cancun 之后这个攻击为什么打不成了——而它的教训为什么仍然成立。
2021 年 8 月,约 6.11 亿美元从 Poly Network 跨链桥流出,当时是史上最大。攻击者没有偷任何私钥,也没有破解任何密码学——他让桥的管理合约「自己」把签名者名单改成了他的公钥。这一篇讲混淆代理人攻击、四字节函数选择器可以被暴力碰撞,以及为什么「持有权限的合约不能同时是通用调用转发器」。
2022 年 8 月,Nomad 桥被掏空约 1.9 亿美元。技术原因只有一行:一次例行升级把可信根初始化成了 0,而未经证明的消息查表也返回 0,于是「任何消息」都通过了验证。更值得研究的是它的第二重灾难——攻击交易公开在链上后,数百个地址复制粘贴、只改收款地址,把它变成了一场公开哄抢。
2010 年 8 月 15 日,区块 74638 里出现了一笔凭空产生 1844 亿枚比特币的交易——总量的八千多倍。漏洞不在业务逻辑里,而在验证代码本身:检查「输出之和不超过输入」时,那个加法自己溢出了。这一篇讲为什么「校验代码里的算术」是最危险的一类算术,以及为什么必须同时校验单项和总和。
2018 年 4 月,BEC 代币被凭空铸出 2×2²⁵⁵ 枚,交易所紧急下架,价格归零。最讽刺的是:这个合约通篇都在用 SafeMath,唯独出问题的那一行乘法是裸的。这一篇讲「防护措施用得不一致等于没用」,以及 Solidity 0.8 之后仍然会静默溢出的三个地方。
2022 年 10 月,攻击者伪造了一份 Merkle 证明,让 BNB Chain 的跨链桥凭空增发约 200 万 BNB。密码学本身没有问题,出问题的是那份证明验证代码的实现——它没有完整约束证明的结构。这一篇讲 Merkle 证明必须绑定的三样东西,以及「暂停整条链」这个止损手段暴露了什么。