Project

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

Unrestricted External Calls in callExternalContract

Summary

The callExternalContract function allows any address with the EXTERNAL_CALLER role to make arbitrary calls to external contracts with no restrictions. This can be exploited if an attacker gains access to the EXTERNAL_CALLER role, enabling them to interact with any external contract using arbitrary data. Additionally, the role can be misused to alter the state of this contract or other vulnerable contracts it interacts with

Exploit Scenario

  1. Compromised EXTERNAL_CALLER: Assume an attacker compromises an account with the EXTERNAL_CALLER role. They could now use callExternalContract to interact with any contract of their choosing. For example, they could:

    • Call a malicious contract that appears harmless but actually reverts in a way that disrupts contract functionality.

    • Interact with a financial contract to manipulate asset states or drain funds if improperly secured.

  2. Unintended Access to Sensitive Contracts: The caller could unintentionally call sensitive functions in trusted contracts, resulting in unintended state changes or loss of funds.

Impact

If compromised, the EXTERNAL_CALLER role could be leveraged to drain funds, interact with unintended contracts, or otherwise misuse the factory’s permissions for malicious purposes.

Tools Used

Manual Review

Recommendations

Restrict callExternalContract to an allowlist of safe addresses or specific contracts the factory is meant to interact with. Alternatively, make this function callable only by the DEFAULT_ADMIN_ROLE and carefully manage role assignment.

mapping(address => bool) public allowedExternalContracts;
function setAllowedExternalContract(address contractAddress, bool allowed) external onlyRole(DEFAULT_ADMIN_ROLE) {
allowedExternalContracts[contractAddress] = allowed;
}
function callExternalContract(address contractAddress, bytes memory data) external payable onlyRole(EXTERNAL_CALLER) returns (bytes memory) {
require(allowedExternalContracts[contractAddress], "Unauthorized external call");
(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 about 1 year ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Support

FAQs

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

Give us feedback!