Ethernaut 靶场学习笔记 05:Token
这是我的 Ethernaut 靶场个人学习笔记,用来记录每一关的题目目标、漏洞原理、利用过程和复盘要点,方便后续按关卡重新练习和查漏补缺。
- 关卡:Level 5 - Token
本关目标
让玩家持有的 Token 数量超过初始 20。
考察知识点
- 无符号整数下溢
- Solidity 0.6 算术行为
- 余额检查顺序
题目源码
1 | // SPDX-License-Identifier: MIT |
源码与漏洞解析
- 漏洞在
transfer的检查:require(balances[msg.sender] - _value >= 0)。 balances[msg.sender]是uint,在 Solidity 0.6 中无下溢检查。当余额 20 转出 21 时,20 - 21不会报错,而是回绕成一个极大的无符号整数。- 无符号整数永远大于等于 0,所以 require 通过。随后
balances[msg.sender] -= _value再次下溢,玩家余额变成极大值。 - Solidity 0.8 默认会检查算术上下溢;旧版本必须用 SafeMath 或显式
require(balance >= amount)。
过程截图

图注:转出 21 个 token 后余额发生下溢回绕的记录。
解题过程
- 初始余额为 20。
transfer里先检查balances[msg.sender] - value >= 0,但无符号整数下溢后会变成极大值。- 转出 21 个代币即可让余额回绕。
Console WP
1 | await contract.balanceOf(player) |
最终 WP
- 查看初始余额:
balanceOf(player)应为 20。 - 调用
transfer(instance, 21),转出比余额多 1 的数量。 - 再次查看余额,看到余额回绕为极大值。
- 提交实例。
复盘与拓展
- 易错点:无符号整数永远不小于 0,配合旧编译器的回绕行为会让检查失效。
- 防御建议:使用 Solidity 0.8+ 或 SafeMath;扣减前显式检查
balance >= amount。
参考资料
- 相关资料:https://hackmd.io/@0xbc000/ryKHlS34p
- Ethernaut 官方仓库:https://github.com/OpenZeppelin/ethernaut
- Solidity 文档:https://docs.soliditylang.org/