Skip to main content

Overview

Malicious external calls are contract interactions that can be exploited to steal funds, manipulate state, or enable other attacks.

Types of Malicious External Calls

Arbitrary External Call

Allows calling any contract with any data, enabling theft.
// DANGEROUS: Arbitrary external call
function execute(address target, bytes memory data) public onlyOwner {
    target.call(data);  // Can call anything!
}
Attack: Owner calls token.transfer(attacker, balance) on any token.

Unprotected Delegatecall

Delegatecall executes external code in the context of the calling contract.
// DANGEROUS: Unprotected delegatecall
function upgrade(address impl) public {
    impl.delegatecall(abi.encodeWithSignature("initialize()"));
}
Attack: Attacker provides malicious implementation that modifies storage.

Callback to Untrusted Contract

Calling user-provided addresses without validation.
// DANGEROUS: Callback to user address
function processPayment(address callback) public {
    // ... payment logic ...
    ICallback(callback).onPayment();  // User controls callback
}
Attack: Callback contract re-enters or manipulates state.

Approve and Call Patterns

Combining approvals with external calls.
// DANGEROUS: Approve then external call
function swapTokens(address router, uint256 amount) public {
    token.approve(router, type(uint256).max);  // Unlimited approval
    IRouter(router).swap(amount);  // Router is user input
}
Attack: Malicious router drains all approved tokens.

Safe Patterns

Whitelist External Calls

// SAFE: Only whitelisted targets
mapping(address => bool) public approvedTargets;

function execute(address target, bytes memory data) public onlyOwner {
    require(approvedTargets[target], "Target not approved");
    target.call(data);
}

Validate Callback Addresses

// SAFE: Validate callback contract
function processPayment(address callback) public {
    require(callback.code.length > 0, "Not a contract");
    require(trustedCallbacks[callback], "Untrusted callback");
    ICallback(callback).onPayment();
}

Limited Approvals

// SAFE: Exact approval amount
function swapTokens(address router, uint256 amount) public {
    require(trustedRouters[router], "Untrusted router");
    token.approve(router, amount);  // Exact amount only
    IRouter(router).swap(amount);
    token.approve(router, 0);  // Reset approval
}

Detection Tags

TagSeverityDescription
external_callMediumArbitrary external call detected
dangerous_delegatecallHighUnprotected delegatecall
untrusted_callbackMediumCallback to user-provided address
unlimited_approvalMediumInfinite token approval

API Response Example

{
  "issues": [
    {
      "tag": "external_call",
      "severity": "medium",
      "description": "Arbitrary external call with user-controlled target",
      "location": "execute(address,bytes)"
    },
    {
      "tag": "dangerous_delegatecall",
      "severity": "high",
      "description": "Delegatecall to unvalidated address",
      "location": "upgrade(address)"
    }
  ]
}

Prevention Checklist

  • Whitelist allowed external call targets
  • Validate all callback addresses
  • Use exact approval amounts, not unlimited
  • Avoid delegatecall to user-provided addresses
  • Implement reentrancy guards around external calls