# Spearbit Writing Exercise Bug Report # Author **Name**: Parth Patel **Email**: parth4321patel@gmail.com # Findings ## delegatecall to a input-controlled function id **Severity**: High Context: [`Implementation.sol#L17-L21`] (https://github.com/spearbit-audits/writing-exercise/blob/develop/contracts/Implementation.sol#L17-L21) As `Implementation` is deployed contract on its own, bad actor can call `delegatecallContract` method and delegate the execution to the malicious contract by passing arguments as address `a` of their malicious contract and the method they need to call in the malicious contract as `_calldata`. A malicious contract can then destructs `Implementation` contract. If `Implementation` contract gets destructed, all the Proxy contract freezes and can't send call to `Implementation` contract. **Recommendation**: Using `delegatecallContract` bad actors can delegate the calls to their contract, I recommend to **remove this method or making it callable only by trusted entities**. Both the options have pros and cons. If the `delegatecallContract` method is removed, there won't be any way for `Proxy` contract to change the `implementation` state variable. So, if this way is to be taken, the setter should be put into `Proxy` contract for setting `implementation` state variable. If the option of making it callable by only trusted entities is chosen, then `Implementation` contract needs to store the trusted entities in the contract. And condition needs to be added to check whether the `delegatecallContract` is called only by a trusted entity. Also, if `Implementation` contract needs to be changed, the whole state of the contract needs to be migrated to the new contract. Another recommendation to mitigate this by just modifying `Implementation` contract is to use a modifier which only allows delegatecall and not the direct call to the function. Find the attached code below: ``` contract Implementation { address private immutable self = address(this); modifier onlyDelegateCall() { require(address(this) != self); _; } function callContract(address a, bytes calldata _calldata) payable external onlyDelegateCall 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) payable external onlyDelegateCall returns (bytes memory) { (bool success, bytes memory ret) = a.delegatecall(_calldata); require(success); return ret; } } ``` **BONUS**: Since the contract Implementation is not intended to hold any state, it can be converted into `library`. The library doesn't have any state on blockchain and can't hold any ether. So, `payable` keyword from both the functions needs to be removed. ``` 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; } } ``` ## No zero-checks on address **Severity**: Informational Context: [`Proxy.sol#L18-L19`](https://github.com/spearbit-audits/writing-exercise/blob/develop/contracts/Proxy.sol#L18-L19) There are no zero-checks for setting the addresses to the state variable. **Recommendation**: Before setting the state variable(i.e `owner` and `implementation`) check whether the arguments provided to the constructor are non-zero.