/** *Submitted for verification at Etherscan.io on 2018-11-27 */
pragma solidity ^0.4.24;
/** * @title SafeMath * @dev Math operations with safety checks that revert on error */ library SafeMath {
/** * @dev Multiplies two numbers, reverts on overflow. */ function mul(uint256 a, uint256 b) internal pure returns (uint256) { // Gas optimization: this is cheaper than requiring 'a' not being zero, but the // benefit is lost if 'b' is also tested. // See: https://github.com/OpenZeppelin/openzeppelin-solidity/pull/522 if (a == 0) { return 0; }
uint256 c = a * b; require(c / a == b);
return c; }
/** * @dev Integer division of two numbers truncating the quotient, reverts on division by zero. */ function div(uint256 a, uint256 b) internal pure returns (uint256) { require(b > 0); // Solidity only automatically asserts when dividing by 0 uint256 c = a / b; // assert(a == b * c + a % b); // There is no case in which this doesn't hold
return c; }
/** * @dev Subtracts two numbers, reverts on overflow (i.e. if subtrahend is greater than minuend). */ function sub(uint256 a, uint256 b) internal pure returns (uint256) { require(b <= a); uint256 c = a - b;
return c; }
/** * @dev Adds two numbers, reverts on overflow. */ function add(uint256 a, uint256 b) internal pure returns (uint256) { uint256 c = a + b; require(c >= a);
return c; }
/** * @dev Divides two numbers and returns the remainder (unsigned integer modulo), * reverts when dividing by zero. */ function mod(uint256 a, uint256 b) internal pure returns (uint256) { require(b != 0); return a % b; } }
seed - ((seed / 1000) * 1000)) < 288:The obtained seed needs to meet certain conditions: less than 288, which means a probability of success of 288/1000.
Because the information that determines the seed is in the block, the caller cannot control it (miners can choose), so we can only try it randomly until we get 8888 money(only get 10 if we guess successfully:( so it will take many times to get 8888 money). Fortunately, this method only consumes GAS instead of ETH.
And then let’s look at the turingTest(), it means we can use an EOA account to call airdrop() or a contract that only has a constructor() but no function(). So in this level, we can use EOA account to do or create a contract to do. You can create a specific account in the site.
It is so normal, right? but why our tx fail? Because the puzzle provider us a wrong contract! The WinnerList contract is a fake contract! I search the WinnerList tx in the blockchain browser, found the address of WinnerList and decompile it, the output is as following:
contract Contract { function main() { memory[0x40:0x60] = 0x80; if (msg.data.length < 0x04) { revert(memory[0x00:0x00]); } var var0 = msg.data[0x00:0x20] / 0x0100000000000000000000000000000000000000000000000000000000 & 0xffffffff; if (var0 != 0x03b6eb88) { revert(memory[0x00:0x00]); } var var1 = msg.value; if (var1) { revert(memory[0x00:0x00]); } var1 = 0x0091; var var2 = msg.data[0x04:0x24] & 0xffffffffffffffffffffffffffffffffffffffff; var var3 = msg.data[0x24:0x44]; func_0093(var2, var3); stop(); } function func_0093(var arg0, var arg1) { var var0 = 0x00; storage[var0] = (arg0 & 0xffffffffffffffffffffffffffffffffffffffff) | (storage[var0] & ~0xffffffffffffffffffffffffffffffffffffffff); storage[var0 + 0x01] = arg1; var var1 = ~0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff & 0x0100000000000000000000000000000000000000000000000000000000000000 * 0xb1; var var2 = tx.origin * 0x01000000000000000000000000; var var3 = 0x12; if (var3 >= 0x14) { assert(); } var temp0 = byte(var2, var3) * 0x0100000000000000000000000000000000000000000000000000000000000000 & ~0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff != var1; var1 = temp0; if (!var1) { label_023F: if (!var1) { return; } else { revert(memory[0x00:0x00]); } } else { var1 = ~0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff & 0x0100000000000000000000000000000000000000000000000000000000000000 * 0x43; var2 = tx.origin * 0x01000000000000000000000000; var3 = 0x13; if (var3 >= 0x14) { assert(); } var1 = byte(var2, var3) * 0x0100000000000000000000000000000000000000000000000000000000000000 & ~0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff != var1; goto label_023F; } } }
Obviously, the real contract has a lot of code that proves the contract given us is fake. But how to read it? I can’t understand the decompile contract :( Don’t worry, let’s analysis it step by step.
(1) We can easily infer the func_0093() function is note()
(2) address _addr ===> var arg0, uint _value ===> var arg1
(3)
(4) byte(x,y): nth byte of x, where the most significant byte is the 0th byte. In this case, the penultimate byte of tx.origin is 0xb1.
(5) if the penultimate byte is not 0xb1, 0x43 in the last byte can be ok.
solve
All in all, we can solve this level like this:
get a specific account that pass the function note(), you have 2 choice: