当前位置: 首页 > news >正文

历年跨链合约恶意交易详解(四)——Chainswap20210711

  漏洞合约函数

function receive(uint256 fromChainId, address to, uint256 nonce, uint256 volume, Signature[] memory signatures) virtual external payable {
        _chargeFee();
        require(received[fromChainId][to][nonce] == 0, 'withdrawn already');
        uint N = signatures.length;
        require(N >= MappingTokenFactory(factory).getConfig(_minSignatures_), 'too few signatures');
        for(uint i=0; i<N; i++) {
            for(uint j=0; j<i; j++)
                require(signatures[i].signatory != signatures[j].signatory, 'repetitive signatory');
            bytes32 structHash = keccak256(abi.encode(RECEIVE_TYPEHASH, fromChainId, to, nonce, volume, signatures[i].signatory));
            bytes32 digest = keccak256(abi.encodePacked("\x19\x01", _DOMAIN_SEPARATOR, structHash));
            address signatory = ecrecover(digest, signatures[i].v, signatures[i].r, signatures[i].s);
            require(signatory != address(0), "invalid signature");
            require(signatory == signatures[i].signatory, "unauthorized");
            _decreaseAuthQuota(signatures[i].signatory, volume);
            emit Authorize(fromChainId, to, nonce, volume, signatory);
        }
        received[fromChainId][to][nonce] = volume;
        _receive(to, volume);
        emit Receive(fromChainId, to, nonce, volume);
    }
    function _decreaseAuthQuota(address signatory, uint decrement) virtual internal updateAutoQuota(signatory) returns (uint quota) {
        quota = _authQuotas[signatory].sub(decrement);
        _authQuotas[signatory] = quota;
        emit DecreaseAuthQuota(signatory, decrement, quota);
    }
    modifier updateAutoQuota(address signatory) virtual {
        uint quota = authQuotaOf(signatory);
        if(_authQuotas[signatory] != quota) {
            _authQuotas[signatory] = quota;
            lasttimeUpdateQuotaOf[signatory] = now;
        }
        _;
    }
    function authQuotaOf(address signatory) virtual public view returns (uint quota) {
        quota = _authQuotas[signatory];
        uint ratio  = autoQuotaRatio  != 0 ? autoQuotaRatio  : Factory(factory).getConfig(_autoQuotaRatio_);
        uint period = autoQuotaPeriod != 0 ? autoQuotaPeriod : Factory(factory).getConfig(_autoQuotaPeriod_);
        if(ratio == 0 || period == 0 || period == uint(-1))
            return quota;
        uint quotaCap = cap().mul(ratio).div(1e18);
        uint delta = quotaCap.mul(now.sub(lasttimeUpdateQuotaOf[signatory])).div(period);
        return Math.max(quota, Math.min(quotaCap, quota.add(delta)));
    }

相关交易与地址 

攻击者地址:0xeda5066780de29d00dfb54581a707ef6f52d8113

漏洞合约地址:0x089165ac9a7Bf61833Da86268F34A01652543466

执行的恶意交易:

具体细节分析

在receive函数里,仅仅对signature做了完整性判断,而没有判断它的正确性,是通过_decreaseAuthQuota函数里的sub函数,根据减后的quota值是否小于0来判断该签名是否正确,并决定是否revert。不过,_decreaseAuthQuota包含updateAutoQuota修饰器,该修饰器会先调用authQuotaOf函数对签名的quota进行更新。在更新过程中,忽略了solidity对于key不存在的情况,并不会报错,而是返回一个空值,所以对于quotaCap.mul(now.sub(lasttimeUpdateQuotaOf[signatory]))语句,攻击者传入一个并不存在于lasttimeUpdateQuotaOf数组的signatory,令该数组返回零,会让quotaCap乘上等同于当前时间戳的倍数,使得delta的值巨大,最后莫名令不存在于数组里的signatory拥有了一笔quota,从而绕过了验证。攻击者即可调用receive函数,获得任意多的代币。

攻击产生的事件:

参考链接

https://etherscan.io/

https://zhuanlan.zhihu.com/p/389738041

 

相关文章:

  • 什么是EXR透视贴图 ?
  • 【云计算】打造高效容器云平台:规划、部署与架构设计
  • Linux第四章练习
  • 【Docker基础】--查阅笔记1
  • C语言中单链表操作:查找节点与删除节点
  • 滑动窗口例题
  • 通过 axios 请求回来的 HTML 字符串渲染到 Vue 界面上并添加样式
  • 五分钟快速清晰理解作用域和闭包以及封装
  • CPU 压力测试命令大全
  • 问问lua怎么写DeepSeek,,,,,
  • 基于连接池与重试机制的高效TDengine写入方案
  • IDEA 使用Maven打包时内存溢出
  • 服务器虚拟化技术深度解析:医药流通行业IT架构优化指南
  • 基于 Spring Boot 瑞吉外卖系统开发(二)
  • php调用大模型应用接口实现流式输出以及数据过滤
  • Redis常见问题排查与解决方案指南
  • HCIA二层综合实验
  • 一款轻量级的Docker日志查看器!!
  • LeetCode 每日一题 2025/3/31-2025/4/6
  • 网络攻防快速入门笔记web | 02 SQL注入
  • 时隔3年,持续近2小时,俄乌在土耳其谈成了什么?
  • 世界数字教育大会发布“数字教育研究全球十大热点”
  • 七猫征文大赛颁出112万奖金,非遗题材作品斩获金奖
  • “朱雀玄武敕令”改名“周乔治华盛顿”?警方称未通过审核
  • 梅花奖在上海|舞剧《朱鹮》,剧里剧外都是生命的赞歌
  • 上海“城市文明开放麦”全城总动员,樊振东担任首位上海城市文明大使