Let’s Play — Capture the Ether : Lotteries (Part I)
This is the second installment of stories on my journey of Capture the Ether, a game where we hack Ethereum Solidity code for fun and learn about smart contract security. Read the introduction post for background, what to expect, and link to other sections.
What is Capture the Ether?
In Capture the Ether, we win points by completing challenges. Each challenge comes with a smart contract to deploy and objectives can be completed by exploiting the contract into a specific state. Challenges are grouped into categories that focus on specific areas of security vulnerabilities in smart contracts.
We will cover Guess the number, Guess the secrete number, and Guess the random number challenges in this article. These challenges demonstrate it is difficult to store private information on a public blockchain or to source randomness from a deterministic programming model in EVM.
I recommend you read, think about, attempt the problem on your own first. For each problem, I will discuss the high level approach followed by detailed solution.
Guess the number
We simply want
n == answer == 42 therefore we just need to call
guess(42) on the contract.
You should already be a pro calling smart contraction function using transactions. The only difference this time is that we need to send ether along the function call because
require(msg.value == 1 ether) .
To send ether in web3.js, simply add
value in the transaction option:
To send transation with ether in Remix, use
Value option on the top right.
You can choose units from the drop down menu from the right.
Guess the secret number
We would like to find the preimage that hashes into a specific hash using
keccak256 , the hash function used by Ethereum. Usually this would be very difficult because a good hash function like
keccak256 has good preimage resistance.
In this case, it is trivial because
n has type
uint8 and can only vary between 0 and 256 (2⁸). What remains is to iterate through all possible values by brute force.
We can run through all possible values using either Solidity directly or via offline tools such as web3.js.
We can easily replicate the
keccak256 check in a for loop as shown in the
GuessTheSecretNumberSolver contract in the above fiddle. You can deploy the contract in EthFiddle which runs an in-browser test EVM that come with accounts loaded with Ether.
Note EthFiddle provides a subset of the functionalities by Remix, where you can also choose built-in EVM from the environment. From hereafter we demonstrate code using EthFiddle as they can be beautifully embedded on Medium.
Because we wrote the function with
public view modifiers, we can call it without gas. Shortly after, it should spit out the answer in the box below.
Pay attention that you are using the correct type, in this case,
uint8 . Otherwise
keccak256 would produce different result as shown in the
keccak256Uint function. This is because
keccak256 hashes (tightly packed) arguments.
keccak256 hash from a string (optionally in hex.) In our case, it is important to pad hex representation of
uint8 to correct digits as shown in the last two lines.
Web3.js 1.x will have
web3.utils.soliditySha3 that takes care of the type coercion for us.
Guess the random number
Two ways to solve this problem:
- Compute answer using
heccak256based on block hash of the previous block of contract deploy (
block.number — 1) and timestamp of the contract deploy block.
answervalue directly from EVM storage. Even though
answeris a private field in the contract, because all transaction need to be validated by all nodes, all fields are in fact publicly readable from an Ethereum node, which can be queried using web3.js.
Calculate Answer from Hash
We first need to find the transaction corresponding to contract creation. Unfortunately I cannot find straightforward API to convert contract address to transaction hash. You can look up this on Etherscan or from the result of your deployment transaction call in Metamask.
With that we can find the block and its timestamp as well as parent block hash. We compute the
keccak256 hash as before and truncate the
byte32 string to
uint8 by taking the last two character and convert it back to integer.
Get Answer from EVM Storage
All persistent contract variables are stored in contract storage and they follow a specific layout as can be found in this documentation. I refer you to this excellent article on how to read contract storage using web3.js.
How to read Ethereum contract storage
Everybody is talking that data in contracts are public, but not everybody knows how to read it.
For the purpose of our exercise, there is only one field in the contract. Therefore we simply need to find the value at slot 0:
In this post, you have learnt to write Solidity code in EthFiddle to test various scenarios without spending gas, use web3.js to compute
keccak256 hash and find block information from a transaction hash, and pry open EVM storage to peak private storage variable of a contract.
Next time, we will look at more challenges in the Lotteries section where we will discover more interesting security vulnerabilities in smart contract and how to indirectly interact with other smart contracts using a smart contract. We will also look at how to use Truffle framework to write and test smart contract in a local simulated blockchain.
Capture the Ether is brought to you by @smarx, who blogs about smart contract development at Program the Blockchain.
I am not affiliated with the game itself and the awesome company behind it. Views expressed are solely my own and do not express the views or opinions of any entity with which I have been, am now, and will be affiliated.