author:sf197 tl;dr 国内并没有一个文档有讲述该漏洞的,正好闲着没事。就写下这篇文章。在网上也搜寻了一些资料,通过自己的翻译才有今天的这篇文章。该漏洞在DASP TOP
10中可以查看到。至于查看的资料,将会在文章末尾贴上。转载请注明作者及文章来源。 什么是“未检查发送”漏洞? 简洁来说,就是一个或多个Ether发送到其他地址时,其他合约拒绝,或发生错误时,会返回一个布尔值false,并且代码将会继续。 倘若未给这些返回值做检测,可能会造成意想不到的结果。 漏洞函数: call()、
callcode()、 delegatecall()、
send(); 这里我们用send()函数做一次测试。 测试代码如下:
pragma solidity ^0.4.19;
contract test
{
bool etherLeft;
mapping (address => uint256) balances;
function Deposit(address addrs,uint256 _value)
public
payable
{
balances[addrs]+=_value;
}
function withdraw(address addr,uint256 _amount) public returns(bool
etherLeft)
{
require(balances[addr] >= _amount);
balances[addr] -= _amount;
if(addr.send(_amount)){
etherLeft = true;
}else{
etherLeft = false;
}
return etherLeft;
}
function balanceOf(address _owner) constant returns (uint256 balance) {
return balances[_owner];
}
}
首先我们利用Deposit函数给自己合约地址充值,再利用send()向合约地址转账。
可以看到这里转账的结果是false 这里其实有个坑,官方给出send函数关联fallback函数是必须的!
Contracts that receive Ether directly (without a function call, i.e. using
send or transfer) but do not define a fallback function throw an exception,
sending back the Ether (this was different before Solidity v0.4.0). So if you
want your contract to receive Ether, you have to implement a fallback
function.
换句话说,通过send,transfer这些函数转移代币的时候,若被接收方是合约的话,则该合约必须存在fallback函数 我们就正好利用这个情况,复现我们的未检测发送漏洞。 代码如下:
pragma solidity ^0.4.19;
contract test
{
mapping (address => uint256) balances;
function Deposit(address addrs,uint256 _value)
public
payable
{
balances[addrs]+=_value;
}
function withdraw(address addr,uint256 _amount) public
{
require(balances[addr] >= _amount);
addr.send(_amount);
balances[addr] -= _amount;
}
function balanceOf(address _owner) constant returns (uint256 balance) {
return balances[_owner];
}
}
上述代码进过修改后,写合约的人逻辑是,先转账_amount金额,再扣除自身这么多的金额。 然而并未在此做返回检查,致使下一行的balances[addr]
-=
_amount;代码继续执行。最终得到金额未转账成功,但余额中又被扣除的现象 先给合约地址充值10代币
再给自身合约转账5代币,由于没有fallback函数,转账会失败。
再去查询余额,竟然变成5代币
漏洞防御: 最主要的防御是尽量避免使用send函数,因为sand函数并不是一个相对安全的函数。如若要使用,可以参考以下几种方案: (1)
function withdraw(address addr,uint256 _amount) public returns(bool
etherLeft)
{
require(balances[addr] >= _amount);
if(addr.send(_amount)){
balances[addr] -= _amount;
etherLeft = true;
}else{
etherLeft = false;
}
return etherLeft;
}
就是在发送的时候做个判断,这是对当前示例的适当修复,但有时它不是正确的解决方案。 (2) 我们可以定义一个宏,CaltSkasiSunType()
function withdraw(address addr,uint256 _amount) public returns(bool
etherLeft)
{
require(balances[addr] >= _amount);
if(callStackIsEmpty()){
addr.send(_amount)
balances[addr] -= _amount;
}else throw;
return etherLeft;
}
posted @
2018-07-01 17:15
admin-神风
阅读(
348 )
评论()
编辑
收藏
举报
点击右上角即可分享
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步