💀Understanding the 'isContract()` vulnerability

What is the isContract() Function?

The isContract() function is utilized within Solidity to ascertain whether a specific address pertains to a contract or an externally owned account (EOA). It usually employs the extcodesize opcode to check if an address has associated contract code.

solidityCopy codefunction isContract(address _addr) private view returns (bool) {
    uint32 size;
    assembly {
        size := extcodesize(_addr)
    }
    return (size > 0);
}

The Vulnerability Explained

Bypassing During Contract Creation

The vulnerability emerges during the contract creation phase. When a contract is in its construction process, the extcodesize for that contract’s address is zero. Consequently, if a contract interacts with another contract in its constructor, the isContract() function will inaccurately return false, allowing it to bypass any restrictions that should apply to contract accounts.

Unintended Access

Due to this vulnerability, a malicious actor could instantiate a contract that interacts with the target contract within its constructor, effectively bypassing the isContract() restriction and executing functions intended only for EOAs or specifically whitelisted addresses.

Exploiting the Vulnerability: A Breakdown

  1. Deploying a Malicious Contract: An attacker creates and deploys a contract that interacts with the target contract within its constructor.

  2. Bypassing the isContract() Check: Since the extcodesize is zero during construction, the malicious contract bypasses restrictions, enabling it to execute functions that should be inaccessible to contract accounts.

  3. Executing Unauthorized Functions: The attacker can execute and manipulate functions within the target contract that rely on the isContract() modifier for access control.

Example

Consider a target contract that uses the isContract() function to restrict access to a specific function, allowing only EOAs or certain addresses to call it:

solidityCopy codemodifier onlyEOA() {
    require(!isContract(msg.sender), "Contracts not allowed");
    _;
}

function sensitiveFunction() public onlyEOA {
    // Function logic here
}

A malicious actor could exploit the vulnerability as follows:

solidityCopy codecontract MaliciousContract {
    TargetContract target;

    constructor(TargetContract _target) {
        target = _target;
        target.sensitiveFunction();
    }
}

Mitigating the Vulnerability

  • More Robust Checks: Consider employing a more holistic approach to discerning contracts from EOAs, not solely relying on the isContract() function.

  • Additional Modifiers: Incorporate additional modifiers and checks that leverage multiple facets of access control, reducing dependency on a single function.

  • External Call Limitations: Limit the impact of external calls within constructors or consider delaying certain interactions until after a contract's construction phase.

Last updated