Skip to content

Commit

Permalink
Add SCSFG references
Browse files Browse the repository at this point in the history
  • Loading branch information
dmuhs committed Aug 4, 2023
1 parent 96355f3 commit 31d945d
Show file tree
Hide file tree
Showing 57 changed files with 226 additions and 631 deletions.
82 changes: 3 additions & 79 deletions docs/attacks/force-feeding.md
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/
111 changes: 4 additions & 107 deletions docs/attacks/frontrunning.md
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/).
62 changes: 3 additions & 59 deletions docs/attacks/griefing.md
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/
4 changes: 4 additions & 0 deletions docs/attacks/index.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
!!! tip

Seeking more detailed information on smart contract attacks? The [Smart Contract Security Field Guide](https://scsfg.io/hackers/) offers an extensive range of attack strategies with in-depth explanations on vulnerabilities, including new code samples for a hands-on learning experience. Enhance your understanding and stay ahead of potential threats by visiting this continuously updated resource.

The following is a list of known attacks which you should be aware of, and defend against when
writing smart contracts.

Expand Down

0 comments on commit 31d945d

Please sign in to comment.