06.bouncer
2023-09-26 20:18:46
# 19.Paradigm CTF 2021
bouncer
分析
1.全局观
Bounder.sol
- ERC20Like:接口
- Bouncer
- owner机制,需要验证,无法修改owner
- 抄AAVE的存取款机制,存款可以让别人帮,可以批量存款
- owner可以执行任何逻辑:delegatecall
Setup:初始化题目
2.任务
将bouncer的ETH余额设置为0
1 | function isSolved() public view returns (bool) { |
3.详细分析
根据Setup的初始化情况,我们来看一下资产与状态情况:
ETH | WETH | 状态 | |
---|---|---|---|
Setup | 48 | 注册:10WETH,10ETH | |
bouncer | 52 | owner=Setup | |
party |
我们要将bouncer的52ETH归零,那么就需要让bouncer发钱出来,可能的方法只有:payout()
、claimFees()
、hatch()
:
claimFees()
:需要owner才做,并且从题目(0.8.0)可以看出,owner无法修改,同时0.8.0也意味着不可能有重入、移除的可能性了。此方法废弃。hatch()
:需要owner才能操作,但我们不可能是owner,或者是否可能让合约回调自己内?看完题目知道也没有相关的方法。此方法废弃。payout()
这个是转账的逻辑,那么本题只可能在转账的逻辑等问题上出考点了。
要调用
payout()
只能通过redeem()
。虽然redeem()
可以随意调用,但是余额不足回revert。因此我们必须先存款。存款方法有两个,一个是普通单个存款
convert()
,一个是为了省gas的批量存款convertMany()
。convert()
分析完之后,没啥问题,就是模仿AAVE的机制,再来看一下批量存款convertMany()
,额这么少代码,应该没问题,不对,等等!这特么好经典的问题:循环 + 存款逻辑,那么msg.value就可以被复用,一份msg.value存多次。1
2
3
4
5function convertMany(address who, uint256[] memory ids) payable public {
for (uint256 i = 0; i < ids.length; i++) {
convert(who, ids[i]);
}
}
解题
1 | // SPDX-License-Identifier: UNLICENSED |
漏洞修改
1 | function convertMany(address who, uint256[] memory ids) payable public { |
模拟实验:本题的问题和A合约中的canBuy()
原理一样
1 | pragma solidity 0.8.0; |