前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >智能合约:跨合约调用漏洞

智能合约:跨合约调用漏洞

作者头像
yichen
修改2020-05-15 14:30:19
2.6K0
修改2020-05-15 14:30:19
举报

漏洞概述

在 solidity 中合约之间的相互调用有两种方式:

  • 使用封装的方式,将合约地址封装成一个合约对象来调用它的函数
  • 直接使用函数来调用其他合约

solidity 提供了 call()、delegatecall()、callcode() 三个函数来实现合约直接的调用及交互,这些函数的滥用导致了各种安全风险和漏洞。在使用第二种方式时,如果处理不当很可能产生致命的漏洞 —— 跨合约调用漏洞,主要就是 call() 注入函数导致的

call() 函数对某个合约或者本地合约的某个方法的调用方式:

  • <address>.call(方法选择器,arg1,arg2,...)
  • <address>.call(bytes)

通过传递参数的方式,将方法选择器、参数进行传递,也可以直接传入一个字节数组(bytes要自己构造)

举一个简单的例子

代码语言:javascript
复制
contract sample_1{
  function info(bytes data){
    this.call(data);
  }
  function secret() public{
    require(this == msg.sender);
    //secret operations
  }
}

合约的两个函数中 secret 函数必须是合约自身调用的,然而有个 info 函数,调用了 call(),并且外界是可以直接控制 call 函数的字节数组的

this.call(bytes4(keccak256("secret()"))); 这样就调用了 secret

第二个例子

代码语言:javascript
复制
contract sample2{
 ...
  function logAndCall(address _to,uint _value,bytes data,string _fallback){
  ...
  assert(_to.call(bytes4(keccak256(_fallback)),msg.sender,_value,_data));
    ...
 ...
}

在 logAndCall 函数中,我们的 _falback 参数可以控制,所以我们可以控制 _to 的任何方法。另外 assert 有三个参数,我们没必要调用完全符合三个参数类型的合约,因为在 EVM 中,只要找到了方法需要的参数,就会去执行,其他参数就会被忽略,不会产生任何影响

漏洞分析

代码语言:javascript
复制
function transferFrom(address _from,address _to,uint256 _amount,bytes _data,string_custom_fallback) public returns (bool success){
  //Alerts the token controller of the transfer
  if(isContract(controller)){
    throw;
  }
  require(super.transferFrom(_from,_to,_amount));
  if(isContract(_to)){
    ERC223ReceivingContract receiver = ERC223ReceivingContract(_to);
    receiver.call.value(0)(bytes4(keccack256(_custom_fallback)),_from,_amount,_data);
  }
  ERC223Transfer(_from,_to,_amount,_data);
  return true;
}
function setOwner(address owner_) public auth{
  owner - owner_;
  LogSetOwner(owner);
}
modifier auth{
  require(isAuthorized(msg.sender,msg.sig));
  _;
}
function isAuthorized(address src,bytes4 sig) internal view returns (bool){
  if(src==address(this)){
    return true;
  } else if (src == owner){
    return true;
  } else if (authority == DSAuthority(0)){
    return false;
  } else {
    return authority.canCall(src,this,sig);
  }
}

核心漏洞代码片段

代码语言:javascript
复制
function transferFrom(address _from,address _to,uint256 _amount,bytes _data,string_custom_fallback) public returns (bool success){
  //Alerts the token controller of the transfer
  if(isContract(controller)){
    throw;
  }
  require(super.transferFrom(_from,_to,_amount));
  if(isContract(_to)){
    ERC223ReceivingContract receiver = ERC223ReceivingContract(_to);
    receiver.call.value(0)(bytes4(keccack256(_custom_fallback)),_from,_amount,_data);
  }

代码含义:如果目标地址是智能合约,就调用目标的 custom 回退函数,并依次填入参数 _from,_amount,_data,这些都是我们可控的,另外 _to 参数也仅仅进行了是否是合约地址的判断,所以我们可以通过 _to 来控制合约本身,并调用该合约的任意 public 函数

代码调试

在这里复制代码到 Remix IDE

https://cn.etherscan.com/address/0x461733c17b0755ca5649b6db08b3e213fcf22546#code

由于这个合约的计算比较多,所以在 Gas limit 值加上个 0 让他大一点

点击 owner 查看合约所有者的地址,返回了默认账户的地址

0xCA35b7d915458EF540aDe6068dFe2F44E8fa733c

调用带有 _custom_fallback 参数的 transferFrom() 函数,我们的目的是让合约属于第二个账户,所以填写如下参数:

  • _from 参数为第二个账户的地址
  • _to 参数为合约地址
  • _custom_fallback 参数为 setOwner() 函数
  • 另外两个参数随意

再看一下,合约所有者已经成了第二个账户的地址了

漏洞防范

虽然 call()、delegatecall()、callnode() 三个函数为合约间调用提供了很大的便利,但是存在很大隐患,所以防范跨合约调用漏洞的方法就是减少对这三个函数的使用。很多功能都可以用高级函数来实现

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-05-03,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 陈冠男的游戏人生 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档