## Anyone can execute selfdestruct via delegatecall **Severity**: High risk Context: [`Implementation.sol#L17-L21`](https://github.com/spearbit-audits/writing-exercise/blob/develop/contracts/Implementation.sol#L17-L21) Anyone can execute the delegatecallContract function in Implementation contract. This means that evil user can attack for Implementation contract by `selfdestruct`. If Implementation contract was broken, the users using Proxy cannot moving their ETH and ERC20 tokens *permanently*. - The attacker will attack in the following ways 1. The attacker deploy a evil contract. It has `selfdestruction` function 2. Evil execute their own `selfdestruct` function via delegatecallContract function of Implementation.sol This code is below. ``` solidity address payable eve; function pwn(address imp) external { bytes memory data = abi.encodeWithSignature("destroy()"); Imp(imp).delegatecallContract(address(this), data); } function destroy() external { selfdestruct(eve); } /* Code Explain eve: evil address(need to be payable to send ETH by selfdestruct) pwn(): The function this code executing 2 above. destroy(): The function to destroy Implementation contract by selfdestruct */ ``` **Recommendation**: ■ Solution.1: **Using Call not Delegatecall** ① Don't using the `delegatecall` and using `call` from Proxy-contract to Implementation-contract. ② Deleting the delegatecallContract function in Impelementation.sol. This code is below. ① Context: [`Proxy.sol#L34`](https://github.com/spearbit-audits/writing-exercise/blob/develop/contracts/Proxy.sol#L34) ``` solidity // Delete this code let success := delegatecall(gas(), _implementation, 0, calldatasize(), 0, 0) // And this code takes the place of the above code. let success := call(gas(), _implementation, callvalue(), 0, calldatasize(), 0, 0) ``` ② Context: [`Implementation.sol#L17-#L21`](https://github.com/spearbit-audits/writing-exercise/blob/develop/contracts/Implementation.sol#L17-L21) ``` solidity // Delete this all code function delegatecallContract(address a, bytes calldata _calldata) payable external returns (bytes memory) { (bool success, bytes memory ret) = a.delegatecall(_calldata); require(success); return ret; } ``` ■ Solution.2: **Restricting the Executor not Anyone** This problem is caused by the fact that anyone can execute a delegatecallContract function. To prevent this, you should create a list of contracts address that can execute delegatecallContract of the implementation contract. This code is below. Context: [`Implementation.sol`](https://github.com/spearbit-audits/writing-exercise/blob/develop/contracts/Implementation.sol) ``` solidity import "@openzeppelin/contracts/ownership/Ownable.sol"; contract Implementation is Ownable { mapping(address => uint256) public proxies; //gas saving compared with (address => bool) function addProxy(address _proxy) public onlyOwner { proxies[_proxy] = 1; } function deleteProxy(address _proxy) public onlyOwner { proxies[_proxy] = 2; //gas saving compared with changing from 1 to 0 // nonZero => nonZero is cheap } function delegatecallContract(address a, bytes calldata _calldata) payable external returns (bytes memory) { require(proxies[address(this)], "You are not proxies!!!"); ... } } ``` ■ Solution.3: **Being Library, not a Contract.** The purpose of Impelementation-contract is using the funds of Proxy. > To use these funds, users can execute arbitrary calls and arbitrary delegatecalls by using the implementation contract. Quote: [Documenetaion](https://github.com/spearbit-audits/writing-exercise#wallet-protocol) This means that it doesn't matter Implemetation is contract or not.  If this contract is changed to a library, selfdestruct attack can be prevented because selfdestruct can destroy only contract. This code is below. Context: [`Implementation.sol`](https://github.com/spearbit-audits/writing-exercise/blob/develop/contracts/Implementation.sol) ``` solidity // You need to change the word from contract to library and delete the two payable modifier. library Implementation { function callContract(address a, bytes calldata _calldata) external returns (bytes memory) { (bool success, bytes memory ret) = a.call{ value: msg.value }(_calldata); require(success); return ret; } function delegatecallContract(address a, bytes calldata _calldata) external returns (bytes memory) { (bool success, bytes memory ret) = a.delegatecall(_calldata); require(success); return ret; } } ``` ***Thank you for reading 😌***