Skip to content

Commit

Permalink
fixes, thanks to Tim
Browse files Browse the repository at this point in the history
  • Loading branch information
zamrokk committed Jan 29, 2024
1 parent 653768f commit b9af052
Show file tree
Hide file tree
Showing 4 changed files with 44 additions and 33 deletions.
25 changes: 13 additions & 12 deletions docs/tutorials/security.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,15 @@ Offchain attacks are a serious threat to the security and reliability of decentr
- Bugs: A bug is a flaw or error in the code or logic of a smart contract or a frontend. For instance, a frontend may point to a non-existent smart contract address, or invoke an entrypoint with incorrect parameters. This can result in loss of funds, incorrect execution, or denial of service. Bugs can be avoided by proper testing, auditing, and maintenance of the code.
- Impersonation: An impersonation attack is when an attacker pretends to be someone else, such as a legitimate service provider, a trusted party, or a user. For example, an attacker may create a phishing UI that mimics the appearance and functionality of a real frontend, but sends the user's funds or data to the attacker's address. Alternatively, an attacker may deploy a copy of a contract on the network, with slight modifications that benefit the attacker. Impersonation attacks can be prevented by verifying the identity and authenticity of the parties involved, such as using digital signatures, checksums, or domain verification.
- Replay attacks: A replay attack is when an attacker reuses a valid transaction from one context to another, without the consent or knowledge of the original sender. For example, an attacker may copy a L1 transaction to a L2 transaction, and execute it on a different chain or layer. This can result in double-spending, unauthorized actions, or inconsistent states. Replay attacks can be mitigated by introducing nonce and chain_id fields in the transactions, which ensure that each transaction is unique and valid for a specific context. Alternatively, a timestamp can be used as a nonce, which makes it easy to detect outdated or replayed transactions.
- Trusting and no verifying: This situation occurs when a user or a contract blindly accepts or relies on data or information from an offchain source, without verifying its accuracy or integrity. For example, a user may trust an offchain API that provides market data from oracle or exchange, without checking if the data is correct, manipulated or even inexistent. Similarly, a user may sign any transaction payload from a wallet, without inspecting its content or destination. Trust and no verification can lead to false assumptions, incorrect decisions, or malicious actions. Trust and no verification can be avoided by applying the principle of "trust but verify", which means that any offchain data or information should be validated by multiple sources, cross-checked with onchain data, or confirmed by the user before using it.
- Trusting and no verifying: This situation occurs when a user or a contract blindly accepts or relies on data or information from an offchain source, without verifying its accuracy or integrity. For example, a user may trust an offchain API that provides market data from an oracle or an exchange, without checking if the data is correct, manipulated or even inexistent. Similarly, a user may sign any transaction payload from a wallet, without inspecting its content or destination. Trust and no verification can lead to false assumptions, incorrect decisions, or malicious actions. Trust and no verification can be avoided by applying the principle of "trust but verify", which means that any offchain data or information should be validated by multiple sources, cross-checked with onchain data, or confirmed by the user before using it.
- FrontRunning and MEV (Maximum Extractable Value): MEV is an economic phenomenon that can be exploited by miners, validators, and sequencers who can arbitrarily include, exclude, or re-order transactions within the blocks they produce. MEV strategies involve executing a set of on-chain interactions prepared by offchain actors like humans or bots.
It can be done by the baker itself as the list is known in advance at each period, or any bots listening to the gossip network.
Examples of common MEV strategies :

- Sandwiching: When a large order is spotted on mempool
- the attacker buys before and sells the tokens after. The large transaction will raise the price of the token, if you buy before the price increases and sell after the price rises, then you make the difference on your pocket. If you cannot reorder the transaction yourself like a baker does, you will have to put higher gas, it is called PGA or Priority Gas Auctions.
- Sandwiching: When a large order is spotted on mempool, the attacker buys before and sells the tokens after. The large transaction will raise the price of the token, if you buy before the price increases and sell after the price rises, then you make the difference on your pocket. If you cannot reorder the transaction yourself like a baker does, you will have to put higher gas, it is called PGA or Priority Gas Auctions.

- Front running: Bakers and sequencers can simulate any transaction extracted from the mempool and look at the wallet increase of balance. If it is an arbitrage opportunity, then the attacker can decide to copy and place his own transaction before the others
- Arbitrage: Arbitrage is the process of buying and selling the same assets across different markets to capitalize on price discrepancies between them. It is not even considered as a hack as it is the job of common traders and regulate the global market price
- Arbitrage: Arbitrage is the process of buying and selling the same assets across different markets to capitalize on price discrepancies between them. It is not even considered a hack as it is the job of common traders and regulate the global market price

→ **SOLUTION**: Against MEV, there are a few solutions :

Expand All @@ -54,21 +54,22 @@ In this training session, we will use a hands-on approach to learn how to identi

## Prerequisites

To run the code, a Ligo compiler is required and can be installed at this [location](https://ligolang.org/docs/intro/installation/?lang=jsligo)

For later usage, to compile a contract with the Ligo compiler, run this command :

```bash
ligo compile contract ./contracts/<MY_CONTRACT_FILE>.jsligo
```
In the next sections, some code needs to be executed. [Taqueria requires to be installed on your machine](https://taqueria.io/docs/getting-started/installation/).

or through Taqueria
To compile the code, run

```bash
npm i
TAQ_LIGO_IMAGE=ligolang/ligo:1.1.0 taq compile <MY_CONTRACT_FILE>.jsligo
```

> Alternative : You can use the Ligo compiler directly and it can be installed [here](https://ligolang.org/docs/intro/installation/?lang=jsligo)
> You will have to compile the parameters and the storage yourself. Instead of using the taq command, use this one :
>
> ```bash
> ligo compile contract ./contracts/<MY_CONTRACT_FILE>.jsligo
> ```
---
When you're ready, go to [Part 1: Programming errors](./security/part-1)
26 changes: 19 additions & 7 deletions docs/tutorials/security/part-1.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,30 +13,38 @@ Programming errors in web3 are mistakes or bugs that occur when writing smart co

Writing Michelson code requires careful attention to detail and rigorous testing. If the code contains errors or inconsistencies, it may result in a failed transaction that consumes gas and storage fees. Therefore, it is advisable to use high-level languages that compile to Michelson, such as LIGO, and to verify the generated code before deploying it on the node.

In the below example, LIGO is verifying that the subtraction is failing as it returns an optional value.
In the below example, LIGO is verifying that the subtraction is failing as it returns an optional value. **tez / mutez** are only positive values

```ligolang
option<tez> = 1mutez - 2mutez;
option<tez> = 0mutez - 1mutez;
```

Run the code and watch the generated Michelson code
In the source code `.contracts/1-bugs.jsligo`, we have also a subtraction returning an optional

```ligolang
match(store - delta) {
```

Compile the code and watch the generated Michelson code

```bash
taq init
taq compile 1-bugs.jsligo
more ./artifacts/1-bugs.tz
```

After dividing `SUB_MUTEZ`, a check will be done with `IF_NONE` instruction.
LIGO is not compiling if you forget to manage the optional value and return the result directly
Look on line 6 for the decrement entrypoint, after dividing `SUB_MUTEZ`, a check will be done with `IF_NONE` instruction.
LIGO is not compiling if you forget to manage the optional value and you return the result directly

Run the code
Run the code for the decrement of 0 by 1. 0 is the default value of the storage you can find on the **1-bugs.storageList.jsligo** file. 1 is the parameter passed on the simulation you can find on the **1-bugs.parameterList.jsligo** file.

```bash
taq simulate 1-bugs.tz --param 1-bugs.parameter.default_parameter.tz
```

Modify the Michelson file **./artifacts/1-bugs.tz** to not check the diff and run again
All goes well as if there is an error on the subtraction, it is caught and returns an unchanged value

Modify directly the Michelson file **./artifacts/1-bugs.tz** to not check the diff. Using **SUB** will not do specific checks for mutez and will not wrap it into an optional.

```michelson
{ parameter (or (unit %reset) (or (mutez %decrement) (mutez %increment))) ;
Expand All @@ -49,10 +57,14 @@ Modify the Michelson file **./artifacts/1-bugs.tz** to not check the diff and ru
PAIR } }
```

Run it again

```bash
taq simulate 1-bugs.tz --param 1-bugs.parameter.default_parameter.tz
```

This time you have an error

```logs
Underflowing subtraction of 0 tez and 0.000001 tez
```
Expand Down
16 changes: 8 additions & 8 deletions docs/tutorials/security/part-2.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ Then, the Tezos will detect the flaw

## Memory overflow

Memory overflow is a kind of attack that fulfills the memory of a smart contract resulting in making this contract unusable. Even simply loading the data into memory and deserializing it at the beginning of the call, could use so much gas that any call to the contract would fail. All the funds would be forever locked into the contract.
Memory overflow is a kind of attack that overloads the memory of a smart contract resulting in making this contract unusable. Even simply loading the data into memory and deserializing it at the beginning of the call could use so much gas that any call to the contract would fail. All the funds would be forever locked into the contract.

Here is the list of dangerous types to use carefully :

Expand Down Expand Up @@ -89,13 +89,13 @@ sequenceDiagram
Note right of LedgerContract: ... Once finish ... UpdateBalance
```

Why this scenario is not possible on Solidity?
On Solidity, the operation will call directly the smart contract like doing a stop, calling a synchronous execution and continuing the flow.
Why this scenario is possible on Solidity?
On Solidity, the operation will call directly the smart contract, stopping the execution, calling a synchronous execution and resuming the flow. If someone calls several times and very quickly the withdraw operation, as the state is not updated yet, the call will succeed and drain more than the developer expected.

Why this scenario is not possible on Tezos?
On Tezos, the first transaction will update the state and will execute a list of operations at the end of execution. Next executions will encounter an updated state

Let's implement a more complex scenario, now :
Let's implement a more complex scenario where the OfferContract and LedgerContract are separated. The OfferContract will naively send the money back to MaliciousContract because it relies on the **not yet modified** state of the LedgerContract. There are two operations and the modification of the state will come in second position.

```mermaid
sequenceDiagram
Expand Down Expand Up @@ -208,10 +208,10 @@ taq compile 3-reentrancyLedgerContract.jsligo
taq call 3-reentrancyLedgerContract --param 3-reentrancyLedgerContract.parameter.default_parameter.tz -e testing
```

Context is ready :
Context is ready:

- the Malicious contract has cookies on the Ledger contract
- all deployed contract points to the correct addresses
- The Malicious contract has cookies on the Ledger contract
- All deployed contract points to the correct addresses

Now the Malicious contract will try to steal funds from the Offer contract, run the command to start the attack the transaction flow

Expand Down Expand Up @@ -259,7 +259,7 @@ Manipulating arithmetic operations can lead to overflows and underflows

&rarr; **SOLUTION** :

- For large Tez values, do operation on int or nat as it has larger memory values
- For large tez values, do operation on int or nat as it has larger memory values
- There is no other solution than using types with larger values as the default behavior is to reject the transaction in case of overflow

> Do not confuse with [Ligo MathLib library](https://packages.ligolang.org/package/@ligo/math-lib) providing manipulation of floats and rationals instead of using basic types.
Expand Down
10 changes: 4 additions & 6 deletions docs/tutorials/security/part-3.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@ last_update:
date: 11 January 2024
---

> Note : clone this [project](https://github.com/marigold-dev/training-security-3.git) for compiling and testing this tutorial
## Governance

A decentralized system is not enough to ensure the security and efficiency of a blockchain network. It also requires a robust governance model that can handle conflicts, upgrades, and innovations. Moreover, the distribution of the native token that powers the network should be fair and balanced, avoiding the concentration of power and wealth among a few actors. If these conditions are not met, the decentralized system may suffer from instability, stagnation, or manipulation. Therefore, it is important to design and implement a governance model and a token distribution strategy that align with the goals and values of the network and its users
Expand All @@ -15,7 +13,7 @@ One of the challenges of designing and deploying a smart contract is to define t

- Who can create, modify, or terminate the contract?
- Creation: A smart contract is generally deployed by a DevOps person or a CI pipeline. Apart from knowing the creator's address and the fees he paid, there is no critical event that can appear at this moment. The only hack here would be to impersonate another company's smart contract, as discussed in the introduction of this training
- Update: By design, a smart contract code cannot be modified because it is immutable. There is an exception below in the Chapter 2
- Update: By design, a smart contract code cannot be modified because it is immutable. However, lambdas are an exception, as described in the next section.
- Deletion: By design, a smart contract cannot be deleted because the code is stored on a block forever and can be called/executed at any time. The only way to terminate a smart contract would be to programmatically have a boolean to enable/disable all entrypoints
- Who can invoke, monitor, or verify the contract functions?
- Invocation: This depends on the role-based access set on each entrypoints. By default, all annotated `@entry` functions are exposed and are callable.
Expand All @@ -36,13 +34,13 @@ A common way to change the behavior of a smart contract is to store some mutable
This feature presents a **high risk and breaks the trust** you can have in the execution of the contract. If the governance is not clear and transparent, an administrator can potentially push any function and drain your funds from this contract. It is recommended to read carefully the code to see which kind of action can be done through this lambda. A lambda can access the contract's state and call other functions.
For example, if the lambda is called in a function to update a static data configuration that has few impacts, then it is not necessarily dangerous. But if the lambda can create an operation that can be executed or is returning false information to fool the user over a transaction, then it is a red flag

> Other technics exist to update a dapp like the proxy pattern. It does not change the code of the smart contract, but deployed a new version of it and a proxy contract will redirect the user transaction to this new contract. The risk is located on the proxy contract. If this contract is hacked and badly protected, anyone can deploy a malicious contract and redirect to it
> Other techniques exist to update a dApp, like the proxy pattern. It does not change the code of the smart contract, but deployed a new version of it and a proxy contract will redirect the user transaction to this new contract. The risk is located on the proxy contract. If this contract is hacked and badly protected, anyone can deploy a malicious contract and redirect to it
[An excellent tutorial here about smart contract upgrades using Lambda and proxies](https://github.com/marigold-dev/training-dapp-4)
For an example of upgrading smart contracts with lambdas and proxies, see [Create your minimum dapp on Tezos](../dapp).

## (Trustable) oracles

Blockchain oracles are third-party services that provide smart contracts with external information1. They serve as bridges between blockchains and the outside world, allowing smart contracts to access off-chain data. Oracles verify, query, and authenticate external data sources, and transmit any valuable data.
Blockchain oracles are third-party services that provide smart contracts with external information. They serve as bridges between blockchains and the outside world, allowing smart contracts to access off-chain data. Oracles verify, query, and authenticate external data sources, and transmit any valuable data.

An Oracle is made of two parts:

Expand Down

0 comments on commit b9af052

Please sign in to comment.