前言
- 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 | 

 
          