在 solidity 中合约之间的相互调用有两种方式:
使用封装的方式,将合约地址封装成一个合约对象来调用它的函数
直接使用函数来调用其他合约
solidity 提供了 call()、delegatecall()、callcode() 三个函数来实现合约直接的调用及交互,这些函数的滥用导致了各种安全风险和漏洞。在使用第二种方式时,如果处理不当很可能产生致命的漏洞 —— 跨合约调用漏洞,主要就是 call() 注入函数导致的
call() 函数对某个合约或者本地合约的某个方法的调用方式:
.call(方法选择器,arg1,arg2,...) .call(bytes) 通过传递参数的方式,将方法选择器、参数进行传递,也可以直接传入一个字节数组(bytes要自己构造) 举一个简单的例子 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 第二个例子 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 中,只要找到了方法需要的参数,就会去执行,其他参数就会被忽略,不会产生任何影响 漏洞分析 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); } }