零时科技丨CTF技能宝典之智能合约#整数溢出漏洞
字数 1708 2025-08-22 12:22:54
智能合约整数溢出漏洞分析与利用
1. 整数溢出漏洞概述
整数溢出是智能合约中常见的安全漏洞之一,主要发生在算术运算结果超出变量类型所能表示的范围时。在Solidity中,整数类型(uint/int)都有固定的位数(如uint256为256位),当运算结果超出这个范围时会发生溢出。
溢出类型
- 上溢(Overflow): 当数值超过类型最大值时,会"回绕"到最小值
- 下溢(Underflow): 当数值小于类型最小值时,会"回绕"到最大值
2. 漏洞合约分析
以2018年WCTF比赛中的BelluminarBank合约为例,该合约存在整数溢出和变量覆盖漏洞。
合约关键结构
pragma solidity ^0.4.23;
contract BelluminarBank {
struct Investment {
uint256 amount;
uint256 deposit_term;
address owner;
}
Investment[] balances;
uint256 head;
address private owner;
bytes16 private secret;
// ... 其他函数 ...
}
关键漏洞点
- 整数溢出漏洞:
require(deposit_term >= balances[balances.length - 1].deposit_term + 1 years);
当balances[balances.length - 1].deposit_term + 1 years计算结果超过uint256最大值时,会发生上溢归零。
- 变量覆盖漏洞:
Investment storage investment = balances[account];
investment.amount = msg.value;
investment.deposit_term = deposit_term;
investment.owner = msg.sender;
未初始化的storage指针会从storage[0]开始覆盖全局变量。
3. 漏洞利用步骤
攻击目标
获取合约所有权并清空合约余额。
详细攻击流程
-
初始状态分析
- 合约部署时存入31337 wei
- 初始owner为合约创建者
- secret为部署时设置的私密值
-
第一次invest调用
- 参数:
- account: 1
- deposit_term: 2^256 - 31536000 (1年秒数)
- value: 1 wei
- 效果:
- 通过整数溢出绕过存款期限检查
- 变量覆盖:
- balances.length被覆盖为2
- head被覆盖为大数值
- owner被覆盖为攻击者地址
- 参数:
-
第二次invest调用
- 参数:
- account: 2
- deposit_term: 0
- value: 2 wei
- 效果:
- 将head重置为0
- 进一步调整balances和amount值
- 参数:
-
平衡合约余额
- 由于变量覆盖导致账目不一致
- 通过自毁合约向目标合约转账2 wei
-
调用confiscate
- 参数:
- account: 2
- secret: 从storage读取的值
- 效果:
- 满足所有条件检查
- 转移合约全部余额
- 参数:
4. 关键知识点
1. Solidity存储布局
- 状态变量按声明顺序存储在storage中
- 复杂类型(struct/array)的局部storage变量会覆盖全局变量
2. 时间单位处理
1 years= 31536000秒- 时间计算同样受整数溢出影响
3. 变量覆盖机制
Investment storage investment = balances[account];
未初始化的storage指针会从storage[0]开始覆盖:
- investment.amount → storage[0] (balances.length)
- investment.deposit_term → storage[1] (head)
- investment.owner → storage[2] (owner)
4. 整数溢出计算
// 当x + 1 years >= 2^256时,结果为0
uint256 x = 2^256 - 31536000;
require(deposit_term >= x + 1 years); // 恒成立,因为x + 1 years = 0
5. 防御措施
-
使用SafeMath库
using SafeMath for uint256; a = b.add(c); // 会自动检查溢出 -
正确初始化storage指针
Investment storage investment = balances[balances.length]; balances.push(Investment(msg.value, deposit_term, msg.sender)); -
合理设计时间检查逻辑
- 避免直接进行大数值时间运算
- 使用区块时间戳进行比较
-
最小权限原则
- 关键函数添加权限检查
- 避免owner可以被任意修改
6. 工具与资源
-
测试工具
- Remix IDE: http://remix.ethereum.org/
- MetaMask: https://metamask.io/
- MyEtherWallet: https://www.myetherwallet.com/
-
学习资源
- Solidity文档: https://docs.soliditylang.org/
- CTF题库: https://github.com/beched/ctf
- 安全实践: https://consensys.github.io/smart-contract-best-practices/
7. 总结
整数溢出漏洞常与其他漏洞(如变量覆盖)结合出现,在CTF比赛中是常见题型。理解Solidity的存储布局和算术运算特性是发现和利用这类漏洞的关键。开发者应使用SafeMath等防护措施,审计时应特别注意涉及大数值计算的代码路径。