合约可以有一个未命名函数,该函数不能有参数,也不能有返回值。fallback函数在以下情况会被调用:
payable:function() payable public{},否则合约无法接收,如果通过转账函数transfer发送到没有定义payable的合约,会抛出错误,导致后面的代码无法执行!但如果有合约通过自毁selfdestruct(address)的方式发送,即使没有定义为payable都得收下!)。通过MetaMask的Send向合约地址转以太币触发(ethernaut环境下可通过集成函数contract.sendTransaction({value: 1})转以太币;通过address.call.gas(1000000).value(msg.value)();在合约中转以太币)。构造函数无法显式调用,但如果构造函数和contract名字不一致,就能被直接调用,solidity 0.4.22引入了关键词constructor来指定构造函数。
block.coinbase/difficulty/gaslimit/number/timestamp,或基于过往块的哈希值block.blockhash(block.number – 1),由于区块变量在同一区块是共用的,通过攻击合约调用目标合约,即可实现预测1 | |
1 | |
解决该问题可选的方案有RANDAO或Oraclize等,以去中心化的方式或是与外界互联网交互的方式得到安全的随机数。
tx.origin和msg.sendermsg.sender是函数的直接调用方,在用户手动调用该函数时是发起交易的账户地址,但也可以是调用该函数的一个智能合约的地址。而tx.origin则必然是这个交易的原始发起方,无论中间有多少次合约内/跨合约函数调用,而且一定是账户地址而不是合约地址。所以如果存在用户通过合约A调用合约B,那么对应合约B而言,msg.sender?是合约 A 地址,但tx.origin?是用户的账户地址。
如果目标合约使用tx.origin作为校验的依据,攻击者以钓鱼等方式,欺骗目标合约拥有者向攻击协议发送以太币,调用fallback函数,然后在fallback函数调用目标合约,由于tx.origin会是交易原始发起方,也就是目标合约拥有者,满足校验条件,从而实现攻击。
解决方案:通过require(tx.origin == msg.sender)限制外部合约对内部合约的调用。
uint默认为256位无符整型,可表示范围[0, 2**256-1]。如果对0减1,则由256位的0,变成256位1,整数下溢,变成一个最大的整数。同理,如果对256位1加1,则变成256位0,变成最小的整数。同理:uint8,只能存储在范围[0,255]的数字。
解决方案:
a=a+b;,就可以写成if(a+b>a) a=a+b;SafeMath库,如果整数溢出漏洞发生时,函数将进行回退操作,如加法操作写为:a=a.add(b);使用call函数来进行合约交互,对目标合约发送数据。delegatecall跟call主要的不同:通过delegatecall调用,仅使用目标地址的代码,其他信息则使用当前合约(如:msg.sender等)。delegatecall是危险函数,他可以在被调用合约完全操作原始合约的状态,谨慎使用!
通过call/delegatecall调用函数,传入的第一个参数是四个字节时,会把这四个字节当作函数的id来寻找被调用函数,而一个函数id的生成规则是其函数签名的sha3的前4个bytes。因此通过:web3.sha3("pwn()").slice(0,10)=0xdd365b8b,加上0x,总共取前10个字符。
被攻击函数withdraw()在发送以太币msg.sender.call.value(_amount)() 之后才更新余额balances[msg.sender] -= _amount; ,因此通过攻击合约调用withdraw时,攻击合约在fallback函数中接收以太币时再次调用withdraw,则可以在更新余额之前无限递归调用withdraw。
1 | |
原文:https://www.cnblogs.com/lijianming180/p/12268129.html