Solidity Forcibly Send Ether Vulnerability to a Smart Contract continuation of the list of general EcoSystem security from attac

Do repost and rate:

CRYPTO DEEP TECH

Earlier we touched on the topic: “Improving the overall security of the ecosystem from attacks on smart contracts”. In this article, we will continue to develop this painful topic of ecosystem security. Occasionally, it is unwanted for users to be able to send Ether to a smart contract. Unfortunately for these circumstances, it’s possible to bypass a contract fallback function and forcibly send Ether.

contract Vulnerable {    function () payable {        revert();    }    function somethingBad() {        require(this.balance > 0);        // Do something bad    }}

Though it seems like any transaction to the Vulnerable contract should be reverted, there are actually a couple ways to forcibly send Ether.

The first method is to call the selfdestruct method on a contract with the Vulnerable contract address set as the beneficiary. This works because selfdestruct will not trigger the fallback function.

Another method is to precompute a contract’s address and send Ether to the address before the contract is even deployed. Surprisingly enough, this is possible.

Force Feeding

Forcing a smart contract to hold an Ether balance can influence its internal accounting and security assumptions. There are multiple ways a smart contract can receive Ether. The hierarchy is as follows:

  1. Check whether a payable external  function is defined.
  2. If not, check whether a payable external fallback function is defined.

The precedence of each function is explained in this great graphic from the Solidity by Example

Which function is called, fallback() or receive()?           send Ether               |         msg.data is empty?              / \            yes  no            /     \receive() exists?  fallback()         /   \        yes   no        /      \    receive()   fallback()

Consider the following example:

pragma solidity ^0.8.13;contract Vulnerable {    receive() external payable {        revert();    }    function somethingBad() external {        require(address(this).balance > 0);        // Do something bad    }}

The contract’s logic seemingly disallows direct payments and prevents “something bad” from happening. However, calling  in both fallbackcannot prevent the contract from receiving Ether. The following techniques can be used to force-feed Ether to a smart contract.

Selfdestruct

SELFDESTRUCT opcode is called, funds of the calling address are sent to the address on the stack, and execution is immediately halted. Since this opcode works on the EVM-level, Solidity-level functions that might block the receipt of Ether will not be executed

Pre-calculated Deployments

Additionally, the target address of newly deployed smart contracts is generated in a deterministic fashion. The address generation can be looked up in any EVM implementation, such as the py-evm reference implementation by the Ethereum Foundation:

def generate_contract_address(address: Address, nonce: int) -> Address:    return force_bytes_to_address(keccak(rlp.encode([address, nonce])))

An attacker can send funds to this address before the deployment has happened. This is also illustrated by this 2017 Underhanded Solidity Contest submission

Block Rewards and Coinbase

Depending on the attacker’s capabilities, they can also start proof-of-work mining. By setting the target address to their , block rewards will be added to its balance. As this is yet another EVM-level capability, checks performed by Solidity are ineffective.

The above effects illustrate that relying on exact comparisons to the contract’s Ether balance is unreliable. The smart contract’s business logic must consider that the actual balance associated with it can be higher than the internal accounting’s value.

In general, we strongly advise against using the contract’s balance as a guard.

Insufficient Gas Griefing

Griefing is a type of attack often performed in video games, where a malicious user plays a game in an unintended way to bother other players, also known as trolling. This type of attack is also used to prevent transactions from being performed as intended.

This attack can be done on contracts which accept data and use it in a sub-call on another contract. This method is often used in multisignature wallets as well as transaction relayers. If the sub-call fails, either the whole transaction is reverted, or execution is continued.

Let’s consider a simple relayer contract as an example. As shown below, the relayer contract allows someone to make and sign a transaction, without having to execute the transaction. Often this is used when a user can’t pay for the gas associated with the transaction.

contract Relayer {    mapping (bytes => bool) executed;    function relay(bytes _data) public {        // replay protection; do not call the same transaction twice        require(executed[_data] == 0, "Duplicate call");        executed[_data] = true;        innerContract.call(bytes4(keccak256("execute(bytes)")), _data);    }}

The user who executes the transaction, the ‘forwarder’, can effectively censor transactions by using just enough gas so that the transaction executes, but not enough gas for the sub-call to succeed.

There are two ways this could be prevented. The first solution would be to only allow trusted users to relay transactions. The other solution is to require that the forwarder provides enough gas, as seen below.

// contract called by Relayercontract Executor {    function execute(bytes _data, uint _gasLimit) {        require(gasleft() >= _gasLimit);        ...    }}

This attack may be possible on a contract which accepts generic data and uses it to make a call another contract (a ‘sub-call’) via the low level address.call() function, as is often the case with multisignature and transaction relayer contracts.

If the call fails, the contract has two options:

  1. revert the whole transaction
  2. continue execution.

Take the following example of a simplified  contract which continues execution regardless of the outcome of the subcall:

Detection Techniques

In general, a detailed manual inspection of the smart contracts code is what is needed to detect reentrancy vulnerabilities. But some of the smart contracts security tools like MythX and Mythril, can also help detecting reentrancy bugs, with the following limitations:

  • These detection tools analyze the smart contract code based on predefined attack patterns, and if the patterns match any part in the code, then the tools discover the vulnerability. Thus, these approaches mainly rely on complete patterns and the specific quality of these patterns.
  • The patterns these solutions rely on are based on the observation of the previous attacks and known vulnerabilities, which makes them limited and difficult to generalize.
  • All the solutions are only applicable before the deployment of smart contracts. This means once the smart contract is deployed on the Ethereum network, these solutions cannot prevent reentrancy attacks and cannot detect the attacker.
  • If a new reentrancy pattern is introduced after the deployment of the smart contracts, these solutions need to be updated; otherwise, they will not be able to detect the new attack patterns.

However, there are projects and researches about static analysis tools and frameworks that, given a contract’s source code, can identify functions vulnerable to reentrancy attacks. At a high level, these tools parse contract source code to extract particular keywords such as global variables and modifiers, tokenize the source code of each function in the contract, and feed embedded representations of these tokens through a model which classifies the function as reentrant or safe.

Smart Contract Vulnerabilities

  • Insufficient Gas Griefing
  • Reentrancy
  • Integer Overflow and Underflow
  • Timestamp Dependence
  • Authorization Through tx.origin
  • Floating Pragma
  • Outdated Compiler Version
  • Unsafe Low-Level Call
  • Uninitialized Storage Pointer
  • Assert Violation
  • Use of Deprecated Functions
  • Delegatecall to Untrusted Callee
  • Signature Malleability
  • Incorrect Constructor Name
  • Shadowing State Variables
  • Weak Sources of Randomness from Chain Attributes
  • Missing Protection against Signature Replay Attacks
  • Requirement Validation
  • Write to Arbitrary Storage Location
  • Incorrect Inheritance Order
  • Presence of Unused Variables
  • Unencrypted Private Data On-Chain
  • Inadherence to Standards
  • Asserting Contract from Code Size
  • Transaction-Ordering Dependence
  • DoS with Block Gas Limit
  • DoS with (Unexpected) revert
  • Unexpected  null address
  • Default Visibility
  • Insufficient Access Control
  • Off-By-One
  • Lack of Precision

Further Reading

  • https://github.com/ethereum/wiki/wiki/Safety
  • https://swcregistry.io/
  • https://eprint.iacr.org/2016/1007.pdf
  • https://www.dasp.co/
  • https://consensys.github.io/smart-contract-best-practices/
  • https://github.com/sigp/solidity-security-blog
  • https://solidity.readthedocs.io/en/latest/bugs.html

Telegram: https://t.me/cryptodeeptech

Video: https://youtu.be/lqjsHB2r6gU

Source: https://cryptodeeptech.ru/solidity-forcibly-send-ether-vulnerability

Cryptanalysis

Regulation and Society adoption

Ждем новостей

Нет новых страниц

Следующая новость