昨天一位博友提到短地址攻击的问题,感觉挺有意思的,就花了点时间研究了一下。
1.什么是短地址攻击
大家都知道,如果我们想调用智能合约的函数,需要在交易的payload字段中填充一段字节码。以ERC20的transfer()的函数为例,函数原型为:
function transfer(address to, uint amount) public returns (bool success);
我们需要通过一段68个字节的字节码来调用该函数进行转账,比如:
a9059cbb000000000000000000000000146aed09cd9dea7a64de689c5d3ef73d2ee5ca000000000000000000000000000000000000000000000000000000000000000001
具体可以分解为3个部分:
- 4字节函数签名:a9059cbb
- to参数:000000000000000000000000146aed09cd9dea7a64de689c5d3ef73d2ee5ca00
- amount参数:0000000000000000000000000000000000000000000000000000000000000001
大家可能注意到,这个转账地址有点特殊:最后两个数字为0。
假如有个用户“不小心”忘记输入最后这两个0了怎么办?这样我们的输入就只有67个字节了。EVM是通过CALLDATALOAD指令从输入数据中获取函数参数的,因此它会先从后面的amount参数里“借”两个0来补足前面的地址参数。当它要加载amount参数的时候,发现位数不够,会在右边补0,参见以太坊源码:
所以,经过这么一折腾,实际上EVM看到是下面这些参数:
- 4字节函数签名:a9059cbb
- to参数:000000000000000000000000146aed09cd9dea7a64de689c5d3ef73d2ee5ca00(借0)
- amount参数:0000000000000000000000000000000000000000000000000000000000000100(补0)
看到问题了没?转账地址没变,但是转账金额增大了256倍!如果你的转账地址后面有足够多的0,那么转账金额将会大得惊人~
但是有人会说,这没啥毛用啊,难道智能合约的作者会傻到不检查你地址的余额,就直接让你提币走人吗?我猜想这跟目前中心化交易所的运营机制相关。考虑下面的场景:用户充币到交易所钱包,交易所又把这些币转移到了它们内部的合约账户中。等用户发起提币申请,并通过人工审核后,再从合约中把币打到用户的账户中。
![](https://i-blog.csdnimg.cn/blog_migrate/303af631183e670818517ab8834c66f0.png)
在这种情况下,交易的msg.sender就是交易所本身,因此可以通过余额检查。当然,这里有个前提:你必须能够通过人工审核!也就是审核员失职。实际上,从没有人成功利用过这个漏洞,最先发现这个问题的GNT项目组,也仅仅是观察到一笔异常交易而已,并没有产生任何实质性损失。网络上流传的攻击方法是:先找到一个里面有足够数量代币的交易所账户,充1000个币进去,然后再申请提1000个币,就可以提出来256000个币。但是,在我看来这似乎并不可行,如果有读友能想出可行