05.babysandbox
2023-09-18 11:10:26
# 19.Paradigm CTF 2021
babysandbox
分析
1.全局观
- Setup:初始化题目
- BabySandbox:只有一个run方法
2.任务
使sandbox合约的代码长度为0,即销毁sandbox合约
1 | function isSolved() public view returns (bool) { |
3.详细分析
做本题需要了解的操作码:
1 | call(): msg.sender |
让我们来用外部合约来调用一次run()
:
if eq(caller(), address())
不会进入,因为调用者不是此合约if lt(gas(), 0xf000)
,当然有足够的gascalldatacopy(0x00, 0x00, calldatasize())
:从位置0x00的calldata复制calldatasize()
字节到位置0x00的内存中,也就是将所有的calldata放到内存0x00的位置if iszero(staticcall(0x4000, address(), 0, calldatasize(), 0, 0))
:如果成功调用,staticcall()
返回true即1,不会被revert,反之如果修改了状态则返回0被revert- 0x4000:此次staticcall最多消耗0x4000gas
address()
,0
,calldatasize()
: 执行此合约的run()
,因为从0位置开始读取calldatasize()
的内容就是run()
0
,0
: 不需要返回值。
- 再次重入执行此合约的
run()
if eq(caller(), address())
:判断成功,进入switch delegatecall(gas(), code, 0x00, 0x00, 0x00, 0x00)
:调用我们输入的地址code
,要求:不得修改此合约状态(否则staticcall失败),并且成功调用(否则switch选择0失败)。由于后面全是0x00,那么就是进入到地址code
的fallabck()
return(0x00, returndatasize())
:此次重入run()
执行完毕
switch call(0x4000, address(), 0, 0, calldatasize(), 0, 0)
:再次重入此合约的run(),但可以修改状态了if eq(caller(), address())
:为trueswitch delegatecall(gas(), code, 0x00, 0x00, 0x00, 0x00)
:调用我们输入的地址code
,要求:成功调用- 那么此时我们就可以执行地址
code
中的代码逻辑,如果fallback中含有selfdestruct()
,则完成题目。
看完这个流程,其实就是将Ethernaut的电梯那关用内联汇编出题,原理是一样的。
解题
1 | // SPDX-License-Identifier: UNLICENSED |
解法1
1 | // SPDX-License-Identifier: UNLICENSED |
解法2
1 | // SPDX-License-Identifier: UNLICENSED |