标签:pos 表示 协议 dao 回退 base network 目标 账户
合约可以有一个未命名函数,该函数不能有参数,也不能有返回值。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.sender
msg.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 |
|
标签:pos 表示 协议 dao 回退 base network 目标 账户
原文地址:https://www.cnblogs.com/lijianming180/p/12268129.html