1. Fallback
挑战描述
合约包含一个构造函数和四个函数。目标是成为合约所有者并提取所有资金。
构造函数在部署时初始化所有者的贡献值:
1
2
3
|
function Fallback() {
contributions[msg.sender] = 1000 * (1 ether);
}
|
contribute()函数记录调用者的以太坊贡献,若贡献超过当前所有者则转移所有权:
1
2
3
4
5
6
7
|
function contribute() public payable {
require(msg.value < 0.001 ether);
contributions[msg.sender] += msg.value;
if(contributions[msg.sender] > contributions[owner]) {
owner = msg.sender;
}
}
|
withdraw()函数仅允许所有者提取资金:
1
2
3
|
function withdraw() onlyOwner {
owner.transfer(this.balance);
}
|
回退函数在满足条件时直接转移所有权:
1
2
3
4
|
function() payable {
require(msg.value > 0 && contributions[msg.sender] > 0);
owner = msg.sender;
}
|
回退函数机制
以太坊函数调用通过4字节函数选择器识别,未匹配时触发回退函数。函数选择器由keccak256哈希的前4字节生成,例如transfer(address,uint256)的选择器为0xa9059cbb。
解决方案
- 调用contribute()存入少量ETH
- 直接向合约地址转账触发回退函数
- 调用withdraw()提取资金
2. Fallout
挑战描述
构造函数名称Fal1out与合约名Fallout拼写差异,导致其成为普通公共函数:
1
2
3
4
|
function Fal1out() payable {
owner = msg.sender;
allocations[owner] = msg.value;
}
|
解决方案
直接调用contract.Fal1out()即可获得所有权。此类错误在真实合约(如ZiberCrowdsale)中确实存在。
3. Token
挑战描述
transfer函数存在整数下溢漏洞:
1
2
3
4
5
6
|
function transfer(address _to, uint _value) public returns (bool) {
require(balances[msg.sender] - _value >= 0); // 无符号整数永真
balances[msg.sender] -= _value; // 可触发下溢
balances[_to] += _value;
return true;
}
|
解决方案
调用contract.transfer(0x0, 21)使余额变为2^256-1
4. Delegation
挑战描述
Delegation合约通过delegatecall在回退函数中调用Delegate合约:
1
2
3
4
5
|
function() {
if(delegate.delegatecall(msg.data)) {
this;
}
}
|
Delegate合约包含所有权修改函数:
1
2
3
|
function pwn() {
owner = msg.sender;
}
|
解决方案
向合约发送包含pwn()函数选择器0xdd365b8b的交易数据,通过delegatecall在Delegation上下文中执行所有权转移。
5. Force
挑战描述
目标向无payable函数的合约强制转账。解决方案:
- 使用selfdestruct(address)
- 设置挖矿奖励地址
- 合约创建前向地址转账
解决方案
创建自毁合约:
1
2
3
4
5
6
|
contract Selfdestruct{
function Selfdestruct() payable{}
function attack(){
selfdestruct(0x..);
}
}
|
6. Re-entrancy
挑战描述
withdraw函数存在重入漏洞:
1
2
3
4
5
6
7
8
|
function withdraw(uint _amount) public {
if(balances[msg.sender] >= _amount) {
if(msg.sender.call.value(_amount)()) { // 外部调用优先执行
_amount;
}
balances[msg.sender] -= _amount; // 余额更新滞后
}
}
|
解决方案
创建代理合约:
- 实现调用withdraw的回退函数
- 调用donate()存款
- 调用withdraw()触发重入攻击
结论
本CTF通过实践方式深入讲解了智能合约安全漏洞,展示了Manticore符号执行和Slither静态分析工具的实际应用。所有挑战均可通过Remix浏览器在ropsten测试网部署验证。