-
Notifications
You must be signed in to change notification settings - Fork 1.4k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
57 changed files
with
226 additions
and
631 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,81 +1,5 @@ | ||
Forcing a smart contract to hold an Ether balance can influence its internal accounting and security assumptions. | ||
There are multiple ways a smart contract can receive Ether. The hierarchy is as follows: | ||
!!! tip | ||
|
||
1. Check whether a payable external `receive` function is defined. | ||
2. If not, check whether a payable external `fallback` function is defined. | ||
3. Revert. | ||
Thank you for visiting the Smart Contract Security Best Practices. Please note that this resource is no longer actively maintained. Instead, we recommend visiting the [Smart Contract Security Field Guide](https://scsfg.io/). The Field Guide is regularly updated and curated by the same security engineer who previously contributed to the Best Practices guide. | ||
|
||
The precedence of each function is explained in this great graphic from the [Solidity by Example](https://solidity-by-example.org/sending-ether/) article: | ||
|
||
``` | ||
Which function is called, fallback() or receive()? | ||
send Ether | ||
| | ||
msg.data is empty? | ||
/ \ | ||
yes no | ||
/ \ | ||
receive() exists? fallback() | ||
/ \ | ||
yes no | ||
/ \ | ||
receive() fallback() | ||
``` | ||
|
||
Consider the following example: | ||
|
||
```sol | ||
pragma solidity ^0.8.13; | ||
contract Vulnerable { | ||
receive() external payable { | ||
revert(); | ||
} | ||
function somethingBad() external { | ||
require(address(this).balance > 0); | ||
// Do something bad | ||
} | ||
} | ||
``` | ||
|
||
The contract's logic seemingly disallows direct payments and prevents "something bad" from happening. | ||
However, calling `revert` in both `fallback` and `receive` **cannot prevent the contract from receiving Ether**. | ||
The following techniques can be used to force-feed Ether to a smart contract. | ||
|
||
### Selfdestruct | ||
|
||
When the `SELFDESTRUCT` opcode is called, funds of the calling address are sent to the address on the stack, and execution is immediately halted. | ||
Since this opcode works on the EVM-level, Solidity-level functions that might block the receipt of Ether [will not be executed](https://solidity.readthedocs.io/en/develop/security-considerations.html#sending-and-receiving-ether). | ||
|
||
|
||
### Pre-calculated Deployments | ||
|
||
Additionally, the target address of newly deployed smart contracts is generated in a deterministic fashion. | ||
The address generation can be looked up in any EVM implementation, such as the [py-evm reference implementation](https://github.com/ethereum/py-evm/blob/e924f63992a35212616b4e20355d161bc4348925/eth/_utils/address.py#L17-L18) by the Ethereum Foundation: | ||
|
||
```python | ||
def generate_contract_address(address: Address, nonce: int) -> Address: | ||
return force_bytes_to_address(keccak(rlp.encode([address, nonce]))) | ||
``` | ||
|
||
An attacker can send funds to this address before the deployment has happened. | ||
This is also illustrated by [this 2017 Underhanded Solidity Contest submission](https://github.com/Arachnid/uscc/tree/master/submissions-2017/ricmoo). | ||
|
||
|
||
### Block Rewards and Coinbase | ||
|
||
Depending on the attacker's capabilities, they can also start proof-of-work mining. | ||
By setting the target address to their `coinbase`, block rewards will be added to its balance. | ||
As this is yet another EVM-level capability, checks performed by Solidity are ineffective. | ||
|
||
|
||
### Solution | ||
|
||
The above effects illustrate that relying on exact comparisons to the contract's Ether balance is unreliable. | ||
The smart contract's business logic must consider that the actual balance associated with it can be higher than the internal accounting's value. | ||
|
||
**In general, we strongly advise against using the contract's balance as a guard.** | ||
|
||
More information can be found in [SWC-132](https://swcregistry.io/docs/SWC-132). | ||
The resource on unexpected Ether transfers can be found here: https://scsfg.io/hackers/unexpected-ether/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,109 +1,6 @@ | ||
Since all transactions are visible in the mempool for a short while before being executed, | ||
observers of the network can see and react to an action before it is included in a block. An | ||
example of how this can be exploited is with a decentralized exchange where a buy order transaction | ||
can be seen, and second order can be broadcast and executed before the first transaction is | ||
included. Protecting against this is difficult, as it would come down to the specific contract | ||
itself. | ||
!!! tip | ||
|
||
Front-running, coined originally for traditional financial markets, is the race to order the chaos | ||
to the winner's benefit. In financial markets, the flow of information gave birth to intermediaries | ||
that could simply profit by being the first to know and react to some information. These attacks | ||
mostly had been within stock market deals and early domain registries, such as whois gateways. | ||
Thank you for visiting the Smart Contract Security Best Practices. Please note that this resource is no longer actively maintained. Instead, we recommend visiting the [Smart Contract Security Field Guide](https://scsfg.io/). The Field Guide is regularly updated and curated by the same security engineer who previously contributed to the Best Practices guide. | ||
|
||
The resource on frontrunning attacks can be found here: https://scsfg.io/hackers/frontrunning/ | ||
|
||
!!! cite "front-run·ning (/ˌfrəntˈrəniNG/)" | ||
*noun*: front-running; | ||
|
||
1. *STOCK MARKET* | ||
|
||
> the practice by market makers of dealing on advance information provided by their brokers and investment analysts, before their clients have been given the information. | ||
<!-- [[OXFORD](https://www.lexico.com/en/definition/front-running)] --> | ||
|
||
### Taxonomy | ||
|
||
By defining a [taxonomy](https://arxiv.org/abs/1902.05164) and differentiating each group from | ||
another, we can make it easier to discuss the problem and find solutions for each group. | ||
|
||
We define the following categories of front-running attacks: | ||
|
||
1. Displacement | ||
1. Insertion | ||
1. Suppression | ||
|
||
#### Displacement | ||
|
||
In the first type of attack, *a displacement attack*, it is **not important** for Alice’s (User) | ||
function call to run after Mallory (Adversary) runs her function. Alice’s can be orphaned or run | ||
with no meaningful effect. Examples of displacement include: | ||
|
||
- Alice trying to register a domain name and Mallory registering it first; | ||
- Alice trying to submit a bug to receive a bounty and Mallory stealing it and submitting it first; | ||
- Alice trying to submit a bid in an auction and Mallory copying it. | ||
|
||
This attack is commonly performed by increasing the `gasPrice` higher than network average, often | ||
by a multiplier of 10 or more. | ||
|
||
#### Insertion | ||
|
||
For this type of attack, it is **important** to the adversary that the original function call runs | ||
after her transaction. In an insertion attack, after Mallory runs her function, the state of the | ||
contract is changed and she needs Alice’s original function to run on this modified state. For | ||
example, if Alice places a purchase order on a blockchain asset at a higher price than the best | ||
offer, Mallory will insert two transactions: she will purchase at the best offer price and then | ||
offer the same asset for sale at Alice’s slightly higher purchase price. If Alice’s transaction is | ||
then run after, Mallory will profit on the price difference without having to hold the asset. | ||
|
||
As with displacement attacks, this is usually done by outbidding Alice's transaction in the gas | ||
price auction. | ||
|
||
!!! info "Transaction Order Dependence" | ||
Transaction Order Dependence is equivalent to race | ||
condition in smart contracts. An example, if one function sets the reward percentage, and the | ||
withdraw function uses that percentage; then withdraw transaction can be front-run by a change | ||
reward function call, which impacts the amount that will be withdrawn eventually. | ||
|
||
See [SWC-114](https://swcregistry.io/docs/SWC-114) | ||
|
||
<!-- Based on Geth default ordering, it's easy to sandwich a transaction by sending two transactions each with 1 wei higher or lower. --> | ||
|
||
<!-- Cite theo/daniel's talk --> | ||
|
||
#### Suppression | ||
|
||
In a suppression attack, a.k.a *Block Stuffing* attacks, after Mallory runs her function, she tries | ||
to delay Alice from running her function. | ||
|
||
This was the case with the first winner of the "Fomo3d" game and some other on-chain hacks. The | ||
attacker sent multiple transactions with a high `gasPrice` and `gasLimit` to custom smart contracts | ||
that assert (or use other means) to consume all the gas and fill up the block's `gasLimit`. | ||
|
||
!!! note "Variants" | ||
Each of these attacks has two variants, *asymmetric* and *bulk*. | ||
|
||
In some cases, Alice and Mallory are performing different operations. For example, Alice is trying to cancel an offer, and Mallory is trying to fulfill it first. We call this *asymmetric displacement*. In other cases, Mallory is trying to run a large set of functions: for example, Alice and others are trying to buy a limited set of shares offered by a firm on a blockchain. We call this *bulk displacement*. | ||
|
||
|
||
### Mitigations | ||
|
||
Front-running is a pervasive issue on public blockchains such as Ethereum. | ||
|
||
The best remediation is to **remove the benefit of front-running in your application**, mainly by | ||
removing the importance of transaction ordering or time. For example, in markets, it would be | ||
better to implement batch auctions (this also protects against high-frequency trading concerns). | ||
Another way is to use a pre-commit scheme (“I’m going to submit the details later”). A third option | ||
is to mitigate the cost of front-running by specifying a maximum or minimum acceptable price range | ||
on a trade, thereby limiting price slippage. | ||
|
||
**Transaction Ordering:** Go-Ethereum (Geth) nodes order the transactions based on their | ||
`gasPrice` and address nonce. This, however, results in a gas auction between participants in the | ||
network to get included in the block currently being mined. | ||
|
||
**Confidentiality:** Another approach is to limit the visibility of the transactions, this can be | ||
done using a "commit and reveal" scheme. | ||
|
||
<!-- cite and properly define commit and reveal --> | ||
|
||
A simple implementation is to store the keccak256 hash of the data in the first transaction, then | ||
reveal the data and verify it against the hash in the second transaction. However note that the | ||
transaction itself leaks the intention and possibly the value of the collateralization. There are | ||
enhanced commit and reveal schemes that are more secure, however require more transactions to | ||
function, e.g. [submarine sends](https://libsubmarine.org/). |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,61 +1,5 @@ | ||
This attack may be possible on a contract which accepts generic data and uses it to make a call | ||
another contract (a 'sub-call') via the low level `address.call()` function, as is often the case | ||
with multisignature and transaction relayer contracts. | ||
!!! tip | ||
|
||
If the call fails, the contract has two options: | ||
Thank you for visiting the Smart Contract Security Best Practices. Please note that this resource is no longer actively maintained. Instead, we recommend visiting the [Smart Contract Security Field Guide](https://scsfg.io/). The Field Guide is regularly updated and curated by the same security engineer who previously contributed to the Best Practices guide. | ||
|
||
1. revert the whole transaction | ||
1. continue execution. | ||
|
||
Take the following example of a simplified `Relayer` contract which continues execution regardless | ||
of the outcome of the subcall: | ||
|
||
```sol | ||
contract Relayer { | ||
mapping (bytes => bool) executed; | ||
function relay(bytes _data) public { | ||
// replay protection; do not call the same transaction twice | ||
require(executed[_data] == 0, "Duplicate call"); | ||
executed[_data] = true; | ||
innerContract.call(bytes4(keccak256("execute(bytes)")), _data); | ||
} | ||
} | ||
``` | ||
|
||
This contract allows transaction relaying. Someone who wants to make a transaction but can't | ||
execute it by himself (e.g. due to the lack of ether to pay for gas) can sign data that he wants to | ||
pass and transfer the data with his signature over any medium. A third party "forwarder" can then | ||
submit this transaction to the network on behalf of the user. | ||
|
||
If given just the right amount of gas, the `Relayer` would complete execution recording the | ||
`_data`argument in the `executed` mapping, but the subcall would fail because it received | ||
insufficient gas to complete execution. | ||
|
||
!!! Note | ||
When a contract makes a sub-call to another contract, the EVM limits the gas forwarded to | ||
[to 63/64 of the remaining gas](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-150.md), | ||
|
||
An attacker can use this to censor transactions, causing them to fail by sending them with a low | ||
amount of gas. This attack is a form of "[griefing](https://en.wikipedia.org/wiki/Griefer)": It | ||
doesn't directly benefit the attacker, but causes grief for the victim. A dedicated attacker, | ||
willing to consistently spend a small amount of gas could theoretically censor all transactions | ||
this way, if they were the first to submit them to `Relayer`. | ||
|
||
One way to address this is to implement logic requiring forwarders to provide enough gas to finish | ||
the subcall. If the miner tried to conduct the attack in this scenario, the `require` statement | ||
would fail and the inner call would revert. A user can specify a minimum gasLimit along with the | ||
other data (in this example, typically the `_gasLimit` value would be verified by a signature, but | ||
that is omitted for simplicity in this case). | ||
|
||
```sol | ||
// contract called by Relayer | ||
contract Executor { | ||
function execute(bytes _data, uint _gasLimit) { | ||
require(gasleft() >= _gasLimit); | ||
... | ||
} | ||
} | ||
``` | ||
|
||
Another solution is to permit only trusted accounts to relay the transaction. | ||
The resource on griefing attacks can be found here: https://scsfg.io/hackers/griefing/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.