选择器碰撞
digest
以太坊智能合约中,函数选择器是函数签名 "<function name>(<function input types>)"
的哈希值的前4
个字节(8
位十六进制)。当用户调用合约的函数时,calldata
的前4
字节就是目标函数的选择器,决定了调用哪个函数。
由于函数选择器只有4
字节,非常短,很容易被碰撞出来:即我们很容易找到两个不同的函数,但是他们有着相同的函数选择器。比如transferFrom(address,address,uint256)
和gasprice_bit_ether(int128)
有着相同的选择器:0x23b872dd
。当然你也可以写个脚本暴力破解。
大家可以用这两个网站来查同一个选择器对应的不同函数:
你也可以使用下面的Power Clash
工具进行暴力破解:
- PowerClash: https://github.com/AmazingAng/power-clash
相比之下,钱包的公钥有256
字节,被碰撞出来的概率几乎为0
,非常安全。
example
下面我们来看一下有漏洞的合约例子。SelectorClash
合约有1
个状态变量 solved
,初始化为false
,攻击者需要将它改为true
。合约主要有2
个函数,函数名沿用自 Poly Network 漏洞合约。
putCurEpochConPubKeyBytes()
:攻击者调用这个函数后,就可以将solved
改为true
,完成攻击。但是这个函数检查msg.sender == address(this)
,因此调用者必须为合约本身,我们需要看下其他函数。executeCrossChainTx()
:通过它可以调用合约内的函数,但是函数参数的类型和目标函数不太一样:目标函数的参数为(bytes)
,而这里调用的函数参数为(bytes,bytes,uint64)
。
1 | contract SelectorClash { |
攻击方法
我们的目标是利用executeCrossChainTx(bytes,bytes,bytes)
函数调用合约中的putCurEpochConPubKeyBytes(bytes)
,目标函数的选择器为:0x41973cd9
。观察到executeCrossChainTx(bytes,bytes,bytes)
中是利用_method
参数和"(bytes,bytes,uint64)"
作为函数签名计算的选择器。因此,我们只需要选择恰当的_method
,让这里算出的选择器等于0x41973cd9
,通过选择器碰撞调用目标函数。
Poly Network黑客事件中,黑客碰撞出的_method
为 f1121318093
,即f1121318093(bytes,bytes,uint64)
的哈希前4
位也是0x41973cd9
,可以成功的调用函数。接下来我们要做的就是将f1121318093
转换为bytes
类型:0x6631313231333138303933
,然后作为参数输入到executeCrossChainTx(bytes,bytes,bytes)
中。executeCrossChainTx(bytes,bytes,bytes)
函数另3
个参数不重要,都填 0x
就可以。
演示
- 部署
SelectorClash
合约。 - 调用
executeCrossChainTx()
,参数填0x6631313231333138303933
,0x
,0x
,0
,发起攻击。 - 查看
solved
变量的值,被修改为ture
,攻击成功。