前言
RCTF2020
区块链roiscoin
题目- 以太坊
Ropsten
测试链
Source
1 | pragma solidity ^0.4.23; |
Analyse
- 题目直接给了源码
- 题目有非预期:
beOwner
在合约账户余额为0
的情况下可以直接成为owner
,这个没有控制好条件,同时settle
那里应该也有非预期,应该只让猜3
次,结果也没有控制好条件,本文不介绍非预期的做法 - 抛除非预期,这里介绍下题目正常的逻辑,考点有三个:
- 预测随机数: 这里的随机数是未来的随机数,可以说是预测未来的随机数,看似不可能,关键在于
guess
的范围是2
,也就是只有0
和1
,所以可以爆破 - 未初始化的结构体
storage
覆盖问题:settle
中的failedlog
未初始化会造成storage
变量覆盖,会覆盖codex
数组长度 - 数组任意写: 当数组长度被修改后,可以覆盖
owner
,当然这对数组长度有一定的要求,根据情况选择合适的数据,这里是用msg.sender
覆盖数组长度的高20
字节
- 预测随机数: 这里的随机数是未来的随机数,可以说是预测未来的随机数,看似不可能,关键在于
exp
- 部署
hack
合约,这里需要注意:- 数组在
storage5
位置,keccak256(bytes32(5)) = 0x036b6384b5eca791c62761152d0c79bb0604c104a5fb6f4eb0703f3154bb3db0
- 当我们修改
codex[y],(y=2^256-x+6)
时就能修改slot 6
,从而修改owner
, 其中x = keccak256(bytes32(5))
- 计算出
y = 114245411204874937970903528273105092893277201882823832116766311725579567940182
, 即y = 0xfc949c7b4a13586e39d89eead2f38644f9fb3efb5a0490b14f8fc0ceab44c256
- 所以数组的长度
codex.length
要> y
, 由于msg.sender
覆盖数组长度的高20
字节,所以其实是变相要求address(msg.sender) > y
, 我们可以生成以0xfd
或0xfe
或0xff
开头的地址即可简单满足这一点
- 数组在
- 解题步骤
- 调用
hack1
- 调用
hack2
一次,这一次需要满足result = 1
,否则继续调用hack2
,直至这一次成功 - 调用
hack3
两次,这两次需要满足result = 0
,否则继续调用hack3
,直至两次为止 - 调用
hack4
修改owner
,这里有个坑点,题目给的合约不是真正的合约,因为调用hack4
总是不能成功修改owner
, 逆向合约,可以看出revise
函数有问题,额外要求msg.sender
最低位字节是0x61
,所以对msg.sender
总共有两点要求: 大于y
并且最低字节是0x61
- 调用
hack5
- 调用
1 | pragma solidity ^0.4.23; |
- 对于该题目生成满足
msg.sender
的合约地址可通过下面脚本生成,直接调用generate_eoa2
即可
1 | from ethereum import utils |