Cointime

Download App
iOS & Android

Runtime Environment and Smart Contract Security Modeling

Validated Project

The design and runtime of different blockchains alter the security of smart contracts built on top of them. There are a number of reasons for this: developers must use a new domain-specific language; transaction executions may involve asynchronous function finality; and tooling is not always equal for different blockchain environments.

In this blog post, we will explore how security changes based on different runtime models. In particular we will compare the runtime assumptions for EVM smart contracts and a similar set of assumptions for NEAR smart contracts. We will then look into how this alters the design of a secure smart contract.

We will also highlight an attack vector found while auditing NEAR contracts which we will name an insolvency attack. While this attack is known to the some in the NEAR community, it is still not a well known attack. Further, it can effect any blockchain that requires additional fees for creating new storage.

Abstract Liquid Staking Contracts

We will begin by defining an abstract liquid staking contract. We will use this contract to understand how the runtime of a blockchain effects the security of a smart contract.

Our staking contract will have some common functionalities found in an ERC20-type contract. The common functionalities will be to track the token balance of a given user and allow for transfers of tokens between users. There are three functions that change state: transfer; transferFrom; and approve. Between two user’s Bob and Alice they are able to generally change the state as follows:

  • transfer

a. Call - Bob calls transfer with input amount x and to address Alice.

b. Effect - The contract updates Alice’s balance with x tokens and removes x tokens from Bob’s balance.

  • approve

a. Call - Bob calls approve with input allowance x and to address Alice.

b. Effect - The contract updates Alice’s allowance x with respective to Bob’s balance to allow Alice to call transferfrom on Bob's tokens.

  • transferFrom

Call - Alice calls transferFrom with input amount x input, from address Bobs account, and to address an Sally's address

Effect - The contract updates Sallys’s balance with x tokens and removes x tokens from Bob’s balance and removes Alice’s allowance x with respective to Bob’s balance.

The remaining functions of our liquid staking will be the following.

  • depositAndStake

a. Call - Alice calls depositAndStake with x input amount

b. Effect - The contract checks whether there is sufficient attached GAS as input x. If there is then the contract mints Alice stGAS tokens and deposits the GAS into a node. It then calls depositandStakeCallback

  • depositAndStakeCallback

a. Call - The contract can only call this function internally with no inputs.

b. Effect - It checks whether the Gas deposit is successful. If successful it returns true and otherwise it reverts the state updates and sends the user their GAS tokens back.

  • withdraw

a. Call - Alice calls withdraw with input amount x

b. Effect - The contract unstakes Alice’s GAS tokens from the node and calls transferFrom on an equivalent amount of stGAS tokens that Alice owns. If Alice does not hold a sufficent amount of stGAS then the transaction reverts.

EVM Runtime Model and Asynchronous Function Calls

The runtime model for the EVM is well known. A general set of axioms assumed by Solidity developers are seen below:

  • If there is sufficient amount of gas then transactions may contain numerous and complex function calls;
  • If a transaction is executed then function calls are synchronous;
  • There is no difference in gas cost of a transaction if a function call increases the storage usage of an existing contract.

This model lead to an interesting family of attack surfaces called reentrancy attacks. While solutions for reentrancy attacks are now well known, there are still many different examples of reentrancy that are more difficult to detect such as multifunction reentrancy and read only reentrancy.

Using our liquid staking contract, let us review how a simple reentrancy attack would work on a EVM-style smart contract using our psuedo-code below.

    function withdraw(uint amount) {
        require(balances[msg.sender] >= amount);
        msg.sender.transfer(amount);       
        balances[msg.sender] -= amount;   
        ...
        ...
    }

Reentrancy attack flow for synchronous function calls:

  • Suppose Bob is a smart contract and our liquid staking contract controls 100 GAS tokens.
  • If Bob holds at least 1 stGAS token then Bob can call withdraw and re-enter via the fallback.
  • The final withdraw will then complete the function call.

The check-effect-interact pattern is one solution to this type of reentrancy. In the example this is done by switching the line responsible for transferring with the line that updates balances. However, is this solution still secure if function calls are no longer synchronous? No! A concrete example can be found in Near smart contracts.

Lets look at the following psuedocode for a liquid staking contract. This example, we will assume that all external function calls are assumed to be finalized in the next block. Further note that the follow is based off the documentation from NEAR and is only meant to model a re-entrancy attack.

   fn depositAndStake(){
     let user = msg.sender();
     let amount = msg.value();
     balance[user]= balance[user] + amount;

     #External contract call  - Takes 1 block to finalize

     validator.deposit_NEAR_and_stake()
     .attachNEAR(amount).thenCallback(depositAndStakeCallback)

   }

   fn withdraw(amount: u32){
     if user has sufficent balance:
       balance[user] = balance[user] - amount
       msg.sender.transfer(amount); 
       ...
       ...
   }

   function depositAndStakeCallback{
   if deposit Failed:
       # Also assume not reverts on underflows 
       balance[user] = balance[user] - amount
       msg.sender.transfer(amount);
   }
   ...
   ...

} 

Notice that in the above contract, the withdraw function follows check-effect-interact pattern. However it is still vulnerable to reentrancy as follows:

  • Suppose Bob is a smart contract on Near and suppose our liquid staking contract controls 100 Near tokens.
  • Bob calls depositAndStake with 49 Near attached at block zero.

Bob calls the withdraw function at block zero then Bob will receive 49 Near.

However, the callback function depositAndStakeCallback is executed at block one and Bob will receive another 49 Near.

Note that Bob is able to call depositAndStake and withdraw within the same transaction. This is because the NEAR blockchain allows for batched function calls. The key here is that the withdraw function is finalized before the depositAndStake has completed the external function call.

Reentrancy attacks are still permissible when function calls are asynchronous however in a slightly different form. One solution to protect against such an attack one is to follow a Check-Interact-Effect in combination with a local Mutex for certain situations. See below for how the Check-Interact-Effect would work here. Further to see the full example, please check the excellent explanation found in the NEAR documentation.

    fn depositAndStake(){
      let user = msg.sender();
      let amount = msg.value();

      #External contract call  - Takes 1 block to finalize

      validator.deposit_NEAR_and_stake()
      .attachNEAR(amount).thenCallback(depositAndStakeCallback)

    }

    fn withdraw(amount: u32){
      if user has sufficent balance:
        balance[user] = balance[user] - amount
        msg.sender.transfer(amount); 
        ...
        ...
    }

    function depositAndStakeCallback{

    if deposit Successful:
    balance[user]= balance[user] + amount;

    if deposit Failed:
        # Also assume not reverts on underflows 
        msg.sender.transfer(amount);
    }
    ...
    ...

} 

Near Runtime Model and Storage Staking

In the previous section, we saw how the security model of our smart contract can change if function calls are no longer synchronous. In this section, lets see how security will change if gas increases when the state of a smart contract increases.

On the NEAR blockchain, the storage of on-chain data requires the deposit of NEAR tokens into the account that contains such data. This mechanism is called Storage Staking. Any piece of data is subject to the deposit including: account metadata; smart contract bytecode; data generated by function calls on smart contracts. The amount of Near needed for Storage Staking is defined to be the stored data length multiplied by the cost in byte.

As such we can update the general axioms for Near developers as follows:

  • If there is sufficient amount of gas then transactions may contain numerous and complex function calls;
  • If a transaction is executed then external function calls are asynchronous;
  • The gas cost of a transaction is defined to be the sum of the gas cost of the function call and a storage staking fee. If there is not enough storage staking fee then all function calls will revert and all other funds deposited in the contract are locked.

The introduces a new problem not found when writing EVM smart contracts. If a generic user can call a function that stores new on-chain data then the smart contract logic must verify there is sufficient funds for the Storage Staking. Otherwise a generic user can cause a denial of service attack on the smart contract. This attack is referred to as the Million Small Deposits attack.

Evgeny Kuzyakov from the Near Social team has provided a solution for storage staking found in NEP-145. However, even with this solution if storage staking is not properly calculated between contracts this opens up a new attack vector which we will call an insolvency attack.

Insolvency Attack on Liquid Staking Contract

To minimize complexity let us suppose that our Liquid staking contract does not enforce a storage staking fee in the function depositAndStake. However the withdraw function refunds a storage staking fee.

    fn depositAndStake(){

      let user = msg.sender();
      let amt = msg.value();
      # increases contract size if user is calling for the first time.
      balance[user]= balance[user] + amt;

      #External contract call   

      validator.deposit_NEAR_and_stake()
      .attachNEAR(amount).thenCallback(depositAndStakeCallback)

    }

    fn withdraw(amount: u32){
      if user has sufficent balance:
        balance[user] = balance[user] - amount
        msg.sender.transfer(amount);
        refund storage fee  
        ...
        ...
    }

Insolvency attack flow:

  • Suppose our liquid staking contract contains 100 Near such that 50 Near is the necessary storage fee for 200 accounts. The remaining 50 is rewards obtained by staking.
  • Further suppose that a new account requires 0.5 Near for storage staking to call depositAndStake.
  • Bob calls depositAndStake with the input amount 0.01.
  • Bob then batch calls the function withdraw with input amount 0.0001.
  • Bob is able to drain 50 NEAR from the Liquid staking and the contract will revert function calls that increase state.

While the above example attack is trivial to see, in practice it is much more difficult to detect for a large system of smart contracts. Further, this scenario does not appear to be logical. Why would a contract refund NEAR when calling withdraw if the balance[user] is not even 0?

This scenario has appeared while auditing NEAR smart contracts. In particular, a bridge can have escrow contracts on either side such that one side of the bridge will collect storage fees and the other side of the bridge will release storage fees. However if both sides do not have the correct storage accounting then a user can abuse the storage fee release. That was an issue with Calimero bridge which we will explore in more detail in the next post. 

Conclusion

Smart contract security is influenced by the underlying blockchain's runtime and the virtual machines that define the programming language. Different models of security are needed for different blockchains.

While there is already a large number of well known security models for different blockchains, the number of models will continue to increase as new blockchains are launched.

Further, we have seen that smart contract security is not composable between different models. In this blog, we provided a number of examples illustrating how NEAR and EVM smart contracts can differ.

One aspect that remains important however not explored in this post is how different smart contract security models interact with each other. These intersections remain critically important as they are found in bridges and other inter-blockchain communication systems. This topic will be explored in another blog post.

Read more: https://www.certik.com/resources/blog/3eSV9gLNNCcIwTt8Qe78Kb-runtime-environment-and-smart-contract-security-modeling

Get the latest news here: Cointime channel — https://t.me/cointime_en

Comments

All Comments

Recommended for you

  • Russia to impose cryptocurrency restrictions, exempting miners and central bank projects

    Russia will implement cryptocurrency restrictions, exempting miners and central bank projects. Starting from September 1st, Russia will impose strict restrictions on the circulation of cryptocurrencies such as Bitcoin, only allowing the issuance of digital financial assets within its jurisdiction. Anatoly Aksakov, Chairman of the Financial Market Committee of the State Duma, led this initiative. This is part of a wider government effort to control the cryptocurrency ecosystem in the face of escalating geopolitical tensions. Aksakov stated that the upcoming legislation aims to restrict non-Russian cryptocurrency transactions to strengthen the dominance of the ruble. Meanwhile, recent reports indicate that Russian entities have used cryptocurrencies, particularly Tether's USDT, to purchase key components for military technology.

  • Ethereum stablecoin transaction volume exceeds $1 trillion so far in April, setting a new record

    On April 29th, The Block data shows that as of April 28th, the trading volume of stablecoins on the Ethereum blockchain reached a record high of $1.08 trillion in April, with DAI trading volume ranking first at $578.07 billion, followed by USDC at $268.15 billion in second place, and USDT at $198.62 billion in third place.

  • Shenyu: Up to one billion users' cloud input methods may have leaked input content. Please take immediate measures to reduce the risk.

    On April 29th, Cobo co-founder and CEO Shen Yu wrote on X platform that the cloud input method used by up to one billion users may have leaked input content. If you have entered mnemonic words or other sensitive information through any of the following cloud input methods, please take immediate measures to reduce the risk.

  • EU member states prepare to enforce landmark crypto law, MiCA

    The European Union is set to enforce MiCA, a crypto law that mandates national regulators to license and supervise service providers. While the regulation is EU-wide, countries can implement slightly different technical standards that crypto firms must adhere to. MiCA's specialized rules for stablecoin issuers will take effect in a few months, followed by licensing and other requirements for crypto firms broadly in December. Each jurisdiction must transpose the EU regulation into local law, select which of their regulators will oversee crypto, and prepare to authorize token issuers and other service providers. Regulators are facing challenges in implementing the new legislation, particularly in terms of licensing requirements, and each country's crypto industry has its own concerns about implementation and proposed laws.

  • The total open interest of BTC contracts on the entire network dropped to $29.83 billion

    According to Coinglass data, the total open position of BTC futures contracts on the entire network is 478,180 BTC, equivalent to 29.83 billion US dollars.

  • An independent Bitcoin miner obtained the entire 3.125 BTC block reward by verifying block 841,286

    On April 29th, independent mining pool ckpool's software engineer and administrator Con Kolivas posted on social media that a miner had mined the 282nd independent block in Bitcoin history. The miner's computing power at the time was about 120PH, which is equivalent to about 0.12 EH, with an average of about 12 PH per week, accounting for about 0.02% of the total network hash rate.

  • South Korea to formally establish an investigation unit focused on digital asset crimes

    The South Korean Ministry of Justice and the Ministry of Interior and Safety will begin discussions in early May to elevate the Joint Investigation Team for Virtual Asset Crimes to an official department. The purpose of this promotion is to solidify the department's position, as it currently operates as a temporary organization under the Seoul Southern District Prosecutor's Office and may be dissolved. The change is expected to improve efficiency through the appointment of new prosecutors and budget allocation, according to Sae-ki. The department is composed of about 30 experts from seven financial and tax regulatory agencies and was established in July 2023 as South Korea's first investigative agency focused on digital asset crimes.

  • Hong Kong virtual asset spot ETF debuts today

    Today, six virtual asset spot ETFs were launched online in Hong Kong. The six virtual asset spot ETFs issued this time are from Huaxia (Hong Kong), Boshi International, and Jiashi International. The three institutions have certain differences in product fees, trading, issuance, and virtual asset platforms.

  • Shanghai Municipal Party Committee Secretary: Welcome Standard Chartered to establish more new institutions, new businesses and new platforms such as blockchain in Shanghai

    Chen Jinong, the Secretary of the Shanghai Municipal Party Committee, met with Weihao Si, the Chairman of the Board of Directors of Standard Chartered Bank, and Mark William D'Arcy, the Executive Director, and some members of the Board of Directors yesterday morning. Chen Jinong stated that he welcomes Standard Chartered Bank to leverage its own advantages, strengthen strategic connections, place more new institutions, businesses, and platforms such as wealth management and blockchain in Shanghai, focus on deepening pragmatic cooperation in technology finance, green finance, digital finance, and create more application scenarios, and provide comprehensive and professional service support for enterprises to go abroad.

  • Vitalik: Humanity needs to create a world where blockchain and artificial intelligence work together

    Vitalik Buterin, the founder of Ethereum, stated at BiddleAsia 2024 held at Signiel Seoul in the Songpa district on March 28 that artificial intelligence is a huge market and its importance is increasing day by day. We need to create a world where blockchain and artificial intelligence work together. Artificial intelligence can now create applications with 100 to 500 lines of code. Vitalik also stated that the ability to write 10,000 lines of code can eliminate most of the bugs in the Ethereum virtual machine.