区块链安全—溢出的BEC漏洞
字数 1656 2025-08-22 12:22:30
区块链安全:BEC代币整数溢出漏洞分析与复现
一、BEC代币简介
BEC(Beauty Chain)是世界上第一个专注于美容生态系统的区块链平台,基于Beauty Chain的创新开放平台,汇集了美容行业上下游应用。BEC代币是该生态系统中使用的通证,用于激励用户、工作人员、应用开发者及上下游企业。
关键数据:
- 2018年2月上线OKEx交易所
- 发行总量:70亿枚
- 市值峰值:280亿美元
- 漏洞爆发时间:2018年4月22日
- 合约地址:0xc5d105e63711398af9bbff092d4b6769c82f793d
二、合约代码架构分析
BEC合约基于ERC20标准实现,采用多层继承结构:
1. 基础合约
SafeMath库:
library SafeMath {
function mul(uint256 a, uint256 b) internal constant returns (uint256) {
uint256 c = a * b;
assert(a == 0 || c / a == b);
return c;
}
// 其他安全运算函数...
}
ERC20Basic合约:
- 定义代币基础接口
- 包含
totalSupply、balanceOf和transfer等基本功能
BasicToken合约:
- 实现ERC20Basic接口
- 使用SafeMath进行安全运算
- 管理地址余额映射
mapping(address => uint256) balances
StandardToken合约:
- 扩展ERC20标准功能
- 实现
transferFrom和approve等授权功能 - 管理授权额度映射
mapping (address => mapping (address => uint256)) internal allowed
2. 功能扩展合约
Ownable合约:
contract Ownable {
address public owner;
function transferOwnership(address newOwner) onlyOwner public {
require(newOwner != address(0));
owner = newOwner;
}
// 其他所有权管理函数...
}
Pausable合约:
contract Pausable is Ownable {
bool public paused = false;
modifier whenNotPaused() { require(!paused); _; }
function pause() onlyOwner whenNotPaused public {
paused = true;
}
// 其他暂停控制函数...
}
PausableToken合约:
- 继承StandardToken和Pausable
- 重写转账相关函数,添加暂停检查
- 关键漏洞函数:
batchTransfer
function batchTransfer(address[] _receivers, uint256 _value) public whenNotPaused returns (bool) {
uint cnt = _receivers.length;
uint256 amount = uint256(cnt) * _value; // 漏洞点:未使用SafeMath
require(cnt > 0 && cnt <= 20);
require(_value > 0 && balances[msg.sender] >= amount);
balances[msg.sender] = balances[msg.sender].sub(amount);
for (uint i = 0; i < cnt; i++) {
balances[_receivers[i]] = balances[_receivers[i]].add(_value);
Transfer(msg.sender, _receivers[i], _value);
}
return true;
}
BecToken合约:
- 设置代币基本信息(名称、符号、小数位)
- 初始化总供应量:70亿 × 10¹⁸
三、漏洞原理分析
1. 漏洞位置
在batchTransfer函数中,amount的计算未使用SafeMath的安全乘法:
uint256 amount = uint256(cnt) * _value; // 不安全运算
2. 整数溢出条件
当满足以下条件时会发生整数溢出:
cnt * _value > 2²⁵⁶ - 1
计算结果会回绕到极小的值甚至0
3. 具体攻击参数
攻击者可构造以下参数实现攻击:
_value= 2²⁵⁵ = 57896044618658097711785492504343953926634992332820282019728792003956564819968cnt= 2
计算:
amount = 2 × 2²⁵⁵ = 2²⁵⁶ ≡ 0 (mod 2²⁵⁶)
4. 绕过安全检查
虽然合约有余额检查:
require(_value > 0 && balances[msg.sender] >= amount);
但当amount溢出为0时,攻击者即使余额不足也能通过检查
四、漏洞复现步骤
1. 部署合约
- 使用Solidity 0.4.16编译器
- 部署BecToken合约
- 验证初始余额分配
2. 准备攻击
- 选择两个接收地址(如A和B)
- 设置攻击参数:
_receivers: [A, B]_value: 2²⁵⁵
3. 执行攻击
调用batchTransfer函数:
contract.batchTransfer(
["0x147...160c", "0x4b0...2db"],
"57896044618658097711785492504343953926634992332820282019728792003956564819968"
);
4. 验证结果
- 攻击者余额不变(因为amount=0)
- 接收方A和B各获得2²⁵⁵ BEC代币
- 合约总供应量被恶意增发
五、漏洞修复方案
1. 立即修复方案
- 暂停合约交易(已实施)
- 部署新合约并迁移资产
2. 代码层面修复
// 使用SafeMath进行乘法运算
uint256 amount = uint256(cnt).mul(_value);
3. 长期预防措施
- 所有算术运算强制使用SafeMath
- 严格参数校验:
require(_value > 0 && _value <= MAX_ALLOWED_VALUE); - 引入静态分析工具检测潜在漏洞
- 进行全面的安全审计
六、经验教训
-
安全开发实践:
- 所有算术运算必须使用安全库
- 对用户输入参数进行严格验证
- 特别关注批量操作函数的安全性
-
合约设计原则:
- 遵循"最小权限原则"
- 重要功能添加速率限制
- 考虑使用升级模式应对未知漏洞
-
测试要求:
- 必须包含边界值测试
- 进行整数溢出专项测试
- 模拟极端情况下的合约行为
-
应急响应:
- 建立漏洞披露和响应机制
- 准备合约暂停和资产迁移方案
- 保持与交易所的紧急沟通渠道