Submitted on Dec 2nd 2023 at 22:05:37 UTC by @SentientX for Boost | DeGate
Report ID: #26431
Report type: Smart Contract
Report severity: Insight
Target: https://etherscan.io/address/0x54D7aE423Edb07282645e740C046B9373970a168#code
Impacts:
- Permanent freezing of funds from the Default Deposit Contract that requires malicious actions from the DeGate Operator.
- Theft of funds from the Default Deposit Contract that requires malicious actions from the DeGate Operator.
The function:
function transferProxyOwnership(address newOwner) public onlyProxyOwner {
require(newOwner != address(0));
emit ProxyOwnershipTransferred(proxyOwner(), newOwner);
setUpgradabilityOwner(newOwner);
}
handles the transfer of ownership to new proxy owner, however the function fails to recognize two fundamental risks:
- Transfer of ownership to compromised current owner address
- Transfer of ownership to invalid address.
both of which can be caused by operational error or malicious actions of Degate Operator.
Risk 1 Scenario:
- Current owner address is compromised. (eg. private key leaked)
- In state of panic ownership is transferred to same owner
- Risk exposure has not changed
Risk 2 Scenario
- Current owner address is compromised
- In state of Panic transfer is made to in valid address
- Funds can be stolen if malicious Degate Operator transfers proxy to an address he controls
- Funds can be permanently locked up if an error in transfer is made to in valid address
Difficulty to Exploit: Easy Weakness: CVSS2 Score:
-
Consider use of 2-step transferOwnersip Proxy
-
Ensure proxy address is not the same address as current owner
-
transferProxyOwnership by changing pending owner to newOwner
example:
* @dev Allows the current owner to transfer control of the contract to a newOwner.
*changes the pending owner to newOwner. But doesn't actually transfer
* @param newOwner The address to transfer ownership to.
*/
function transferProxyOwnership(address newOwner) external onlyProxyOwner {
require(newOwner != address(0));
_setPendingUpgradeabilityOwner(newOwner);
emit NewPendingOwner(proxyOwner(), newOwner);
}
- Let pending Owner claim ownership:
/**
* @dev Allows the pendingOwner to claim ownership of the proxy
*/
function claimProxyOwnership() external onlyPendingProxyOwner {
emit ProxyOwnershipTransferred(proxyOwner(), pendingProxyOwner());
_setUpgradeabilityOwner(pendingProxyOwner());
_setPendingUpgradeabilityOwner(address(0));
}
Finally internal function,
function _setUpgradeabilityOwner(address newProxyOwner) internal {
bytes32 position = proxyOwnerPosition;
assembly {
sstore(position, newProxyOwner)
}
}
Reference implementation:
-
Current Proxy owner calls
transferProxyOwnership(address newOwner) public onlyProxyOwner
setting new owner to compromised address accidently or maliciously. -
Protocol Default Deposit contract is compromised
-
Funds Stolen or Funds Frozen