Ethereum Attacks
Ethereum Attacks
HackPedia: 16 Solidity
Hacks/Vulnerabilities, their Fixes
Vaibhav
Saini and Real World Examples
@vaibhavsaini_67863
July 21st 2018 TWEET THIS
1. Re-Entrancy
The Vulnerability
EtherStore.sol
This contract has two public functions. depositFunds() and
withdrawFunds(). The depositFunds() function simply increments
the senders balances. The withdrawFunds() function allows the
sender to specify the amount of wei to withdraw. It will only
succeed if the requested amount to withdraw is less than 1 ether
and a withdrawal hasn't occurred in the last week. Or does it?...
The vulnerability comes on line [17] where we send the user their
requested amount of ether. Consider a malicious attacker creating
the following contract,
Attack.sol
Let us see how this malicious contract can exploit our EtherStore
contract. The attacker would create the above contract (let's say at
the address 0x0...123) with the EtherStore's contract address as
the constructor parameter. This will initialize and point the public
variable etherStore to the contract we wish to attack.
The final result, is that the attacker has withdrawn all (bar 1) ether
from the EtherStore contract, instantaneously with a single
transaction.
Preventative Techniques
The second technique is to ensure that all logic that changes state
variables happen before ether is sent out of the contract (or any
external call). In the EtherStore example, lines [18] and [19] of
EtherStore.sol should be put before line [17]. It is good practice to
place any code that performs external calls to unknown addresses
as the last operation in a localised function or piece of code
execution. This is known as the checks-effects-interactions
pattern.
The Vulnerability
TimeLock.sol
This contract is designed to act like a time vault, where users can
deposit ether into the contract and it will be locked there for at
least a week. The user may extend the time longer than 1 week if
they choose, but once deposited, the user can be sure their ether is
locked in safely for at least a week. Or can they?…
In the event a user is forced to hand over their private key (think
hostage situation) a contract such as this may be handy to ensure
ether is unobtainable in short periods of time. If a user had locked
in 100 ether in this contract and handed their keys over to an
attacker, an attacker could use an overflow to receive the ether,
regardless of the lockTime.
The attacker could determine the current lockTime for the address
they now hold the key for (its a public variable). Let's call this
userLockTime. They could then call the increaseLockTime function
and pass as an argument the number 2^256 - userLockTime. This
number would be added to the current userLockTime and cause an
overflow, resetting lockTime[msg.sender] to 0. The attacker could
then simply call the withdraw function to obtain their reward.
3. Unexpected Ether
The Vulnerability
Pre-sent Ether
EtherGame.sol
Preventative Techniques
I’m yet to find and example of this that has been exploited in the
wild. However, a few examples of exploitable contracts were given
in the Underhanded Solidity Contest.
4. Delegatecall
FibonacciBalance.sol
Can you spot any error(s) in this contract? If you put this into remix,
fill it with ether and call withdraw(), it will likely revert.
You may have noticed that the state variable start is used in both
the library and the main calling contract. In the library contract,
start is used to specify the beginning of the Fibonacci sequence
and is set to 0, whereas it is set to 3 in the FibonacciBalance
contract. You may also have noticed that the fallback function in
the FibonacciBalance contract allows all calls to be passed to the
library contract, which allows for the setStart() function of the
library contract to be called also. Recalling that we preserve the
state of the contract, it may seem that this function would allow
you to change the state of the start variable in the local
FibonnacciBalance contract. If so, this would allow one to withdraw
more ether, as the resulting calculatedFibNumber is dependent on
the start variable (as seen in the library contract). In actual fact, the
setStart()function does not (and cannot) modify the start variable in
the FibonacciBalance contract. The underlying vulnerability in this
contract is significantly worse than just modifying the start variable.
Preventative Techniques
Let’s look at the relevant aspects of this contract. There are two
contracts of interest contained here, the library contract and the
wallet contract.
5. Default Visibilities
The Vulnerability
In the first Parity multi-sig hack, about $31M worth of Ether was
stolen from primarily three wallets. A good recap of exactly how
this was done is given by Haseeb Qureshi in this post.
6. Entropy Illusion
The Vulnerability
Preventative Techniques
Arseny Reutov wrote a blog post after he analysed 3649 live smart
contracts which were using some sort of pseudo random number
generator (PRNG) and found 43 contracts which could be exploited.
This post discusses the pitfalls of using block variables as entropy
in further detail.
7. External Contract Referencing
The Vulnerability
Rot13Encryption.sol
This code simply takes a string (letters a-z, without validation) and
encrypts it by shifting each character 13 places to the right
(wrapping around ‘z’); i.e. ‘a’ shifts to ’n’ and ‘x’ shifts to ‘k’. The
assembly in here is not important, so don’t worry if it doesn’t make
any sense at this stage.
Consider the following contract which uses this code for its
encryption
The issue with this contract is that the encryptionLibrary address is
not public or constant. Thus the deployer of the contract could have
given an address in the constructor which points to this contract:
which implements the rot26 cipher (shifts each character by 26
places, get it? :p). Again, thre is no need to understand the
assembly in this contract. The deployer could have also linked the
following contract:
Preventative Techniques
constructor() {
encryptionLibrary = new Rot13Encryption();
}
This post by one reddit user explains how they lost 1 ether to this
contract trying to exploit the re-entrancy bug they expected to be
present in the contract.
8. Short Address/Parameter Attack
The Vulnerability
Preventative Techniques
Real-World Example: Unknown
For further reading, see DASP Top 10 and Scanning Live Ethereum
Contracts for the “Unchecked-Send” Bug.
The Vulnerability
The bug exists on line [11] where a send() is used without checking
the response. In this trivial example, a winner whose transaction
fails (either by running out of gas, being a contract that intentionally
throws in the fallback function or via a call stack depth attack)
allows payedOut to be set to true (regardless of whether ether was
sent or not). In this case, the public can withdraw the winner's
winnings via the withdrawLeftOver() function.
Preventative Techniques
A more serious version of this bug occurred in the King of the Ether.
An excellent post-mortem of this contract has been written which
details how an unchecked failed send() could be used to attack the
contract.
The Vulnerability
Let’s see how this could work with a simple example. Consider the
contract
FindThisHash.sol
Imagine this contract contains 1000 ether. The user who can find
the pre-image of the sha3 hash
0xb5b5b97fafd9855eec9b41f74dfb6c38f5951141f9a3ecd7f44d5479b630ee0
can submit the solution and retrieve the 1000 ether. Lets say one
user figures out the solution is Ethereum!. They call solve() with
Ethereum! as the parameter. Unfortunately an attacker has been
clever enough to watch the transaction pool for anyone submitting
a solution. They see this solution, check it's validity, and then
submit an equivalent transaction with a much higher gasPrice than
the original transaction. The miner who solves the block will likely
give the attacker preference due to the higher gasPrice and accept
their transaction before the original solver. The attacker will take
the 1000 ether and the user who solved the problem will get
nothing (there is no ether left in the contract).
Preventative Techniques
There are two classes of users who can perform these kinds of
front-running attacks. Users (who modify the gasPrice of their
transactions) and miners themselves (who can re-order the
transactions in a block how they see fit). A contract that is
vulnerable to the first class (users), is significantly worse-off than
one vulnerable to the second (miners) as miner's can only perform
the attack when they solve a block, which is unlikely for any
individual miner targeting a specific block. Here I'll list a few
mitigation measures with relation to which class of attackers they
may prevent.
The Vulnerability
Some useful references for this are: The Solidity Docs, this Stack
Exchange Question,
The Vulnerability
Roulette.sol
Preventative Techniques
The Vulnerability
Preventative Techniques
Real-World Example: Rubixi
To read more about storage and memory in the EVM, see the
Solidity Docs: Data Location, Solidity Docs: Layout of State
Variables in Storage, Solidity Docs: Layout in Memory.
This section is based off the excellent post by Stefan Beyer. Further
reading on this topic can be found from Sefan’s inspiration, which is
this reddit thread.
The Vulnerability
The Vulnerability
Lets begin with a code example (lets ignore any over/under flow
issues for simplicity).
This simple token buying/selling contract has some obvious
problems in the buying and selling of tokens. Although the
mathematical calculations for buying and selling tokens are
correct, the lack of floating point numbers will give erroneous
results. For example, when buying tokens on line [7], if the value is
less than 1 ether the initial division will result in 0, leaving the final
multiplication 0 (i.e. 200 wei divided by 1e18 weiPerEth equals 0).
Similarly, when selling tokens, any tokens less than 10 will also
result in 0 ether. In fact, rounding here is always down, so selling
29 tokens, will result in 2 ether.
The issue with this contract is that the precision is only to the
nearest ether (i.e. 1e18 wei). This can sometimes get tricky when
dealing with decimals in ERC20 tokens when you need higher
precisions.
Preventative Techniques
You should ensure that any ratios or rates you are using allow for
large numerators in fractions. For example, we used the rate
tokensPerEth in our example. It would have been better to use
weiPerTokens which would be a large number. To solve for the
amount of tokens we could do msg.sender/weiPerTokens. This
would give a more precise result.
Another tactic to keep in mind, is to be mindful of order of
operations. In the above example, the calculation to purchase
tokens was msg.value/weiPerEth*tokenPerEth. Notice that the
division occurs before the multiplication. This example would have
achieved a greater precision if the calculation performed the
multiplication first and then the division, i.e.
msg.value*tokenPerEth/weiPerEth.
Real-World Example: Ethstick
The Vulnerability
Preventative Techniques
Ethereum Quirks
keccak256(rlp.encode([<account_address>,
<transaction_nonce>])
What does this all mean? This means that you can send ether to a
pre-determined address (one which you don’t own the private key
to, but know that one of your accounts can create a contract to).
You can send ether to that address and then retrieve the ether by
later creating a contract which gets spawned over the same
address. The constructor could be used to return all your pre-sent
ether. Thus if someone where to obtain all your Ethereum private
keys, it would be difficult for the attacker to discover that your
Ethereum addresses also have access to this hidden ether. In fact,
if the attacker spent too many transaction such that the nonce
required to access your ether is used, it is impossible to recover
your hidden ether.
contract KeylessHiddenEthCreator {
uint public currentContractNonce = 1; // keep track of this contracts nonce p
function futureAddresses(uint8 nonce) public view returns (address) {
if(nonce == 0) {
return address(keccak256(0xd6, 0x94, this, 0x80));
}
return address(keccak256(0xd6, 0x94, this, nonce));
// need to implement rlp encoding properly for a full range of nonces
}
Consider now, that we don’t own a private key, but instead make up
values for r and s for an arbitrary transaction. Consider we have a
transaction, with the parameters:
The idea is to create a Merkle Tree which contains (as leaf nodes)
all the addresses and balances of users to be credited tokens. This
will be done off-chain. The merkle tree can be given out publicly
(again off-chain). A smart contract can then be created containing
the root hash of the merkle tree which allows users to submit
merkle-proofs to obtain their tokens. Thus a single transaction (the
one used to create the contract, or to simply store the Merkle tree
root hash), allows all credited users to redeem their airdropped
tokens.
Hold down the clap button if you liked the content! It helps me gain
exposure .
ConsensusPedia: An Encyclopedia of 30
Consensus Algorithms
A complete list of all consensus algorithms.
hackernoon.com
vasa
Jun 25
# Blockchain
Apr 26
# Blockchain
# Blockchain
Hackernoon Newsletter curates great stories by real
tech professionals
Get solid gold sent to your inbox. Every week!
Email Address *
TOPICS OF INTEREST
# Ethereum
vasa # Solidity