Please note, this content is no longer actively maintained.
The content of the SWC registry has not been thoroughly updated since 2020. It is known to be incomplete and may contain errors as well as crucial omissions.
For currently maintained guidance on known Smart Contract vulnerabilities written primarily as guidance for security reviewers, please see the EEA EthTrust Security Levels specification. As well as the latest release version, an Editor's draft is available, that represents the latest work of the group developing the specification.
General guidance for developers on what to consider to ensure security, that is currently maintained, is also available through the Smart Contract Security Verification Standard (SCSVS).
Title
Delegatecall to Untrusted Callee
Relationships
CWE-829: Inclusion of Functionality from Untrusted Control Sphere
Description
There exists a special variant of a message call, named delegatecall
which is identical to a message call apart from the fact that the code at the target address is executed in the context of the calling contract and msg.sender
and msg.value
do not change their values. This allows a smart contract to dynamically load code from a different address at runtime. Storage, current address and balance still refer to the calling contract.
Calling into untrusted contracts is very dangerous, as the code at the target address can change any storage values of the caller and has full control over the caller's balance.
Remediation
Use delegatecall
with caution and make sure to never call into untrusted contracts. If the target address is derived from user input ensure to check it against a whitelist of trusted contracts.
References
- Solidity Documentation - Delegatecall / Callcode and Libraries
- How to Secure Your Smart Contracts: 6 Solidity Vulnerabilities and how to avoid them (Part 1) - Delegate Call
- Solidity Security: Comprehensive list of known attack vectors and common anti-patterns - Delegatecall
Samples
proxy.sol
pragma solidity ^0.4.24;
contract Proxy {
address owner;
constructor() public {
owner = msg.sender;
}
function forward(address callee, bytes _data) public {
require(callee.delegatecall(_data));
}
}
proxy_fixed.sol
pragma solidity ^0.4.24;
contract Proxy {
address callee;
address owner;
modifier onlyOwner {
require(msg.sender == owner);
_;
}
constructor() public {
callee = address(0x0);
owner = msg.sender;
}
function setCallee(address newCallee) public onlyOwner {
callee = newCallee;
}
function forward(bytes _data) public {
require(callee.delegatecall(_data));
}
}
proxy_pattern_false_positive.sol
pragma solidity ^0.4.24;
contract proxy{
address owner;
function proxyCall(address _to, bytes _data) external {
require( !_to.delegatecall(_data));
}
function withdraw() external{
require(msg.sender == owner);
msg.sender.transfer(address(this).balance);
}
}
/*
You can't use proxyCall to change the owner address as either:
1) the delegatecall reverts and thus does not change owner
2) the delegatecall does not revert and therefore will cause the proxyCall to revert and preventing owner from changing
This false positive may seem like a really edge case, however since you can revert data back to proxy this patern is useful for proxy architectures
*/