Project

One World
NFTDeFi
15,000 USDC
View results
Submission Details
Severity: high
Invalid

Unprotected low-level calls in function `callExternalContract()` in both `MemberFactory.sol` and `MembershipERC1155.sol`.

Summary

The contracts MembershipERC1155.sol and MembershipFactory.sol contain unsafe low-level external calls in their respective callExternalContract() functions. These calls use the .call() method on addresses, which bypasses Solidity's extcodesize check, allowing calls to non-existent contracts to succeed without reverting.

Vulnerability Details

In Solidity, the .call() function performs a low-level external call, operating directly on an address rather than a contract instance. Due to the fact that the EVM considers a call to a non-existing contract to always succeed, Solidity uses the extcodesize opcode to check that the contract that is about to be called actually exists and it indeed contains code otherwise causes an exception. Note that this check is not performed in case of low-level calls like .call().

function callExternalContract(address contractAddress, bytes memory data) external payable onlyRole(OWP_FACTORY_ROLE) returns (bytes memory ) {
(bool success, bytes memory returndata) = contractAddress.call{value: msg.value}(data);
require(success, "External call failed");
return returndata;
}

This issue is referenced in the documentation of solidity language official website.

Since this low-level call approach is used in function callExternalContract() found in both MembershipERC1155.sol and MembershipFactory.sol, neither of the contracts can guarantee that the target contract exists, resulting in successful external calls to non-existing contracts.

Impact

  • Funds may be sent to non-existent addresses leading to permanent loss of funds.

Tools Used

  • Manual code review and Solidity documentation.

Recommendations

Implement mechanisms to verify the external contract really exists and has code before calling it.

function callExternalContract(address contractAddress, bytes memory data) external payable onlyRole(EXTERNAL_CALLER) returns (bytes memory ) {
require(contractAddress.code.length > 0, "Error!");
(bool success, bytes memory returndata) = contractAddress.call{value: msg.value}(data);
require(success, "External call failed");
return returndata;
Updates

Lead Judging Commences

0xbrivan2 Lead Judge 12 months ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.