Author: NEVILLE GRECH

  • Rising Gas Prices Are Threatening Our Security (No, It’s Not the Saudi Attack)

    Rising Gas Prices Are Threatening Our Security (No, It’s Not the Saudi Attack)

    Mr. Out of gas exception

    EIP 1884 is set to be implemented into the upcoming Ethereum ‘Istanbul’ hard fork. It:

    • increases the cost of opcode SLOAD from 200 to 800 gas
    • increases the cost of BALANCE and EXTCODEHASH from 400 to 700 gas
    • adds a new opcode SELFBALANCE with cost 5.

    Due to a fixed gas limit (2300) imposed by the .send(..) and .transfer(..) Solidity functions, fallback functions that use these opcodes may now start to fail due to an out-of-gas exception. This has significant implications for contracts relying heavily on default functions, which might not execute as expected under the new gas constraints.

    Analysis by Contract-library.com team

    Contract-library.com, an automated security resource-intensive service, performs sophisticated static analysis on all deployed smart contracts (over 20 million of them). As static analysis is a technique that takes into account all (or almost all) possible program executions, it is expected to return the most comprehensive list of smart contracts affected by security vulnerabilities.

    On Friday August 16th Martin Holst Swende of the Ethereum foundation asked a question on the ETHSecurity channel on telegram about how to go about finding smart contracts whose fallback function may fail due to EIP-1884. Since contract-library.com already had gas consumption analysis built into its core static analyses, we reached out on the same day with a list of contracts (continuously updated) that may be affected.

    Over the subsequent days, also with the input of Martin Holst Swende, the gas cost analysis computation was updated and improved, over several iterations. The analysis currently reveals over 800 contracts that are highly likely to fail if called with 2300 gas (whereas they would succeed prior to EIP-1884). A subsequent, more general, analysis was also developed. This would be the most comprehensive list of possibly affected smart contracts for this particular issue, but also contains many false positives. This more general “may” analysis reveals that 7000 currently deployed smart contracts may fail under some execution paths with 2300 gas.

    In addition, since our analysis is fully automated, we have also performed experiments to see whether these issues can be simply avoided by repricing the LOG0, LOG1 ... opcodes. Note that these opcodes tend to occur quite often in fallback functions. By halving the Glog and Glogtopic gas costs (refer to the yellow paper), the number of flagged contracts is reduced by approximately half!

    Although repricing opcodes can always break contracts, the EVM should be able to evolve too. These changes are crucial to support the ongoing growth of the Ethereum network, ensuring scalability and future compatibility with diverse applications.

    Clearly, a decent number of contracts will be broken due to this change, so care must be taken to lessen the impact on the overall ecosystem. In this case, we recommend repricing the LOGx opcodes, which seem to be mispriced anyway. This way, there will be fewer contracts affected.

    A more interesting, but perhaps equally serious side-effect of EIP-1884 and EIP-2200 combined is that it lowers the cost of performing an unbounded mass iteration attack, which is currently quite high. This attack is described in MadMax. In summary, this is an attack carried out by an unauthorized user, to increase the size of an array or data structure, which is iterated upon by any other user, rendering the functionality inaccessible by increasing gas cost beyond the block gas limit. The combined effect of EIP-1884 and EIP-2200 make this kind of attack around 7 times cheaper on average, rendering it much more feasible. This attack requires 2 SSTOREs per array element that is added by the attacker. This array is then iterated upon by the victim, requiring an additional SLOAD. For a list of contracts that may be susceptible to unbounded iteration, we have you covered. The list contains approximately 15k contracts.

    Which contracts will be affected? What about the one I’m currently developing?

    If your contract does not have fallbacks which may fail with 2300 gas) or is not susceptible to unbounded iteration, then you’re most probably fine. If it is, you may still be ok, but further investigation is necessary. If you would like to see whether the contract you are developing may be affected, deploy it to one of the Ethereum testnets and check your results at contract-library.com.

    Below are sample contracts with a non-zero Ether balance that are affected by the repricing of SLOAD operations, so that their fallback is no longer runnable under the send/transfer gas allowance of 2300.

    KyberNetwork

    function() public payable {
            require(reserveType[msg.sender] != ReserveType.NONE);
            EtherReceival(msg.sender, msg.value);
        }

    NEXXO crowdsale :

    modifier onlyICO() {
            require(now >= icoStartDate && now < icoEndDate, "CrowdSale is not running");
            _;
        }
        function () public payable onlyICO{
            require(!stopped, "CrowdSale is stopping");
        }

    For NEXXO, it checks three slots, icoStartDateicoEndDate and stopped, totalling 2400 with new gas rules.

    Crowd Machine Compute Token crowdsale:

    modifier onlyIfRunning
      {
        require(running);
        _;
      }
      function () public onlyIfRunning payable {
        require(isApproved(msg.sender));
        LogEthReceived(msg.sender, msg.value);
      }

    Important reminder: The crowdsales above do not inherently break, it just means that callers need to add some more gas than 2300 to partake in the ICO contracts.

    CappedVault

    • Fallback function:
    function () public payable {
            require(total() + msg.value <= limit);
        }

    Unknown Harvester with 5 ETH

    require((msg.value >= stor___function_selector__));
      emit 0xafd096c64445a293507447c2ecb78f03b4f5459ec28b8e9bfe113c35b75d624a(address(msg.sender), msg.value, 0x447);
      exit();

    No source code available. Note that this contract would work if LOGx gas cost is reduced.

    Aragon’s DepositableDelegateProxy

    function isDepositable() public view returns (bool) {
            return DEPOSITABLE_POSITION.getStorageBool();
        }
        event ProxyDeposit(address sender, uint256 value);
        function () external payable {
            // send / transfer
            if (gasleft() < FWD_GAS_LIMIT) {
                require(msg.value > 0 && msg.data.length == 0);
                require(isDepositable());
                emit ProxyDeposit(msg.sender, msg.value);
            } else { // all calls except for send or transfer
                address target = implementation();
                delegatedFwd(target, msg.data);
            }
        }
    }

    Note that this contract would work if LOGx gas cost is reduced. According to the contract-library analysis, the fallback function may fail due to anything between 2308 and 2438 gas. Issue at Aragon

    How does the static analysis on contract-library.com work?

    Static program analysis is a technique that considers all of a program’s behaviors without having to execute the program. Static analysis is generally thought to be expensive, but over the years we have developed techniques to counter this. Firstly, we developed new techniques in the area of “declarative program analysis”, which simplifies analysis implementations. Secondly, we have applied our analyses at scale, which makes them worth the effort. Contract-library’s internal analysis framework decompiles all smart contracts on the main Ethereum network and most popular testnets to an IR representation, amenable to analysis. The decompilation framework is described in a 2019 research paper. Following this analysis, many “client analyses”, are applied. These analyses all benefit from a rich suite of analysis primitives, such as gas cost analysis (similar to worst-case execution analysis), memory contents analysis, etc. These are instantiated and customized in each client analysis. Finally, we encode all our analyses, decompilers, etc. in a declarative language, and automatically synthesize a fast C++ implementation using Soufflé.

    For illustration, the FALLBACK_WILL_FAIL static analysis is encoded in the following simplified datalog spec, deployed on contract-library.com:

    % Restrict the edges that form the possible paths to those in fallback functions
    FallbackFunctionBlockEdge(from, to) :-
       GlobalBlockEdge(from, to), 
       InFunction(from, f), FallbackFunction(f),
       InFunction(to, g), FallbackFunction(g).
    % Analyze the fallback function paths with the
    % conventional gas semantics, taking shortest paths
    GasCostAnalysis = new CostAnalysis(
      Block_Gas, FallbackFunctionBlockEdge, 2300, min
    ).
    % Analyze the fallback function paths with the
    % updated gas semantics, taking shortest paths
    EIP1884GasCostAnalysis = new CostAnalysis(
      EIP1884Block_Gas, FallbackFunctionBlockEdge, 2300, min
    ).
    FallbackWillFailAnyway(n - 2300) :-
       GasCostAnalysis(*, n), n > 2300.
    % fallback will fail with n - m additional gas
    EIP1884FallbackWillFail(n - m) :-
       EIP1884GasCostAnalysis(block, n), n > 2300,
       GasCostAnalysis(block, m),
       !FallbackWillFailAnyway(*).

    The analysis performs a gas cost computation over all possible paths in the fallback functions, using the gas cost semantics of both PRE and POST EIP-1884. In cases where there is a path that can complete in the former semantics but not the latter, we flag the smart contract.

  • Gigahorse: Thorough, Declarative Decompilation of Smart Contracts

    Gigahorse: Thorough, Declarative Decompilation of Smart Contracts

    The rise of smart contract—autonomous applications running on blockchains—has led to a growing number of threats, necessitating sophisticated program analysis. However, smart contracts, which transact valuable tokens and cryptocurrencies, are compiled to very low-level bytecode. This bytecode is the ultimate semantics and means of enforcement of the contract.

    We present the Gigahorse toolchain. At its core is a reverse compiler (i.e., a decompiler) that decompiles smart contracts from Ethereum Virtual Machine (EVM) bytecode into a high- level 3-address code representation.

    Read more

  • Chronicle of an Attack Foretold

    Chronicle of an Attack Foretold

    Co-written with 

    Neville Grech

    In a few hours, an attacker will claim the prize for the first Consensys Diligence Ethereum hacking challenge. Here’s how they’ll do it, why nobody else can perform the same attack (any longer), and why the attacker has to wait…

    The challenge consisted of a smart contract submitted to the mainnet, without sources. The contract is meant to be decoded, attacked, and drained of its minimal funds. The draining account will then get an off-contract bounty.

    At this point in time, an attacker has not just entered the house but also locked the door behind them, so nobody else can enter. (Which is also why we stopped looking into the challenge and are are instead writing this text.) But, interestingly, the attacker has to wait until the Constantinople rollout enables the CREATE2 opcode, for the second step of the attack to take place!

    To understand the challenge, let’s look at a decompiled version of the contract. We are using our favorite decompiler — our own service, contract-library.com, applied on the challenge contract.

    As it turns out, the challenge requires solving two sub-problems: first, gaining ownership of the contract, in order to enable a delegatecallto a contract that the attacker controls, and, second, circumventing checks over the bytecode of the contract getting delegatecall-ed: the contract cannot contain instructions createcallcallcodedelegatecallstaticcallselfdestruct. Let’s look at both sub-problems in detail, and see how they are solved.

    Challenge Problem 1

    In the decompiled code, one can notice that there are two arrays, with guessed names array_0 and owners. The latter is used to check whether the caller has the required privileges to perform the final part of the attack. Although there are no setters for owners, one can still pollute the data stored in it, as all arrays are stored in the same address space. The length of the first array in the deployed contract was set to maxint: a size that allows overflow, so that an attacker can write anywhere in storage.

    Per standard convention for (dynamic) storage arrays, their lengths are stored in storage locations 0 and 1, while their contents are stored at storage locations keccak256(0) and keccak256(1), respectively. One can therefore compute the offset of the contents of owners and of the length of owners (as well as that of array_0) relative to the start of the contents of array_0, as can be seen in the following “attacker’s” code:

    function offsets() private returns (uint, uint, uint) {
      uint array0start = uint(keccak256(abi.encodePacked(uint(0))));
      uint array1start = uint(keccak256(abi.encodePacked(uint(1))));
      uint contentOffset  = array1start - array0start;
      uint lengthOffset = uint(-array0start);
      return (contentOffset, lengthOffset, lengthOffset + 1);
    }

    Since the challenge contract allows overflow of the array_0 contents area, these offsets let us write into owners, and also change the length of owners. In fact, the attacker did not stop there! They also set the length of array_0 to 0, so that no future attacker can employ the same overflow.

    function attack() public {
      address attackerAddress = address(...);
      address victim = address(0x68Cb...);
      (uint contentOffset, uint lengthOffset0, uint lengthOffset1) 
          = offsets();
      bool success;
      bytes memory result;
      // set address I control as one of the owners
      victim.call(abi.encodeWithSelector(
          bytes4(0x4214352d), uint(attackerAddress), contentOffset)
      );
      
      // set length of array 0 to 0 (no more out of bounds)
      victim.call(abi.encodeWithSelector(
          bytes4(0x4214352d), uint(0), lengthOffset0)
      );
      
      // set length of array 1 to 1 (make attacker the only owner)
      victim.call(abi.encodeWithSelector(
          bytes4(0x4214352d), uint(1), lengthOffset1)
      );  
    }

    The contract registered as owner (attackerAddress) can be any that the attacker controls. Now the attacker has both entered and secured the door! But the more serious challenge is still up ahead.

    Challenge Problem 2

    The second part of the challenge is the actual draining of the contract’s funds. This involves creating yet another attacker contract that will simply drain the contract’s balance. If one checks function 0x2918435f of the challenge contract, the code calls delegatecall on an attacker-supplied address parameter, effectively handing it full control of the account. There is a small twist to this plot however. The delegatecall is preceded by checks of all the bytecodes of the called contract, to ensure that they never match values 0xf0, 0xf1, 0xf2, 0xf4, 0xfa, or 0xff. This precludes use of instructions createcallcallcodedelegatecallstaticcall, and selfdestruct.

    Currently (Feb. 27), these are the only instructions that can be used to drain a contract from its funds. In a few hours, however, a new bytecode instruction (create2) will be available and it can also move funds! Hence the attacker now only needs to pass the address to a smart contract implementing something similar to this:

    contract BountyClaimer {    
        function() external {
            uint res;
            assembly {
                let res := create2(balance(address), 0, 1, 0)
            }
        }
    }

    A minor challenge is that byte value 0xff arises commonly in Solidity compilation, so the attacker has to use roundabout ways to compute some values, but this is little more than a nuisance.

    We would like to salute the clever attacker that will be executing this attack within the next few hours.

    Happy hunting!