《我学区块链》—— 二十三、以太坊安全之短地址漏洞

二十三、以太坊安全之短地址漏洞

1、预备知识

       EVM 虚拟机在解析合约的字节码时,依赖的是ABI的定义,从而去识别各个字段位于字节码的什么地方。关于ABI,可以阅读这个文档:https://github.com/ethereum/wiki/wiki/Ethereum-Contract-ABI
  这里以 transfer 方法为例,一般 ERC-20 token 标准的合约都会实现 transfer 方法,这个方法在 ERC-20 标准中的定义为:function transfer(address to, uint tokens) public returns (bool success);第一个参数是发送合约的目的地址,第二个参数是发送token的数量。当我们调用 transfer 函数向某个地址发送 N 个 ERC-20 的时候,交易的 input 数据分为 3 个部分:

  • 4 字节,是方法名的哈希:a9059cbb
  • 32字节,放以太坊地址,目前以太坊地址是20个字节,高危补0
    000000000000000000000000abcabcabcabcabcabcabcabcabcabcabcabcabca
  • 32字节,是需要传输的数量,这里是1*10^18 GNT
    0000000000000000000000000000000000000000000000000de0b6b3a7640000

       所有这些加在一起就是交易数据:a9059cbb000000000000000000000000abcabcabcabcabcabcabcabcabcabcabcabcabca0000000000000000000000000000000000000000000000000de0b6b3a7640000

2、以太坊短地址

       当调用 transfer 方法提币时,如果允许用户输入了一个短地址,通常是交易所这里没有做处理,比如没有校验用户输入的地址长度是否合法。

  如果一个以太坊地址如下,注意到结尾为0,比如:0x1234567890123456789012345678901234567800,当我们将后面的 00 省略时,EVM 会从下一个参数的高位拿到 00 来补充,这就会导致一些问题了。

  这时,token 数量参数其实就会少了 1个字节,即 token 数量左移了一个字节,使得合约多发送很多 dai bi 出来。我们看个例子:

pragma solidity ^0.4.23;

contract NPToken {
    mapping(address => uint) balances;

    event Transfer(address indexed_from, address index_to, uint256 _value);

    constructor () public {
        balances[tx.origin] = 10000;
    }

    function sendCoin(address to, uint amount) returns (bool sufficient) {
        if (balances[msg.sender] < amount) return false;
        balances[msg.sender] -= amount;
        balances[to] += amount;
        Transfer(msg.sender, to, amount);
        return true;
    }

    function getBalance(address addr) constant returns (uint) {
        return balances[addr];
    }
}

       这里调用 sendCoin 方法时,传入的参数如下:

  0x90b98a11

  00000000000000000000000062bec9abe373123b9b635b75608f94eb8644163e

  0000000000000000000000000000000000000000000000000000000000000002

       这里的0x90b98a11是 method 的 hash值,第二个是地址,第三个是 amount 参数。当我们调用sendCoin方法的时候,传入地址0x62bec9abe373123b9b635b75608f94eb8644163e,并且把这个地址最后的“3e”丢掉,即扔掉末尾的一个字节,参数就变成了:

  0x90b98a11

00000000000000000000000062bec9abe373123b9b635b75608f94eb86441600

                                   ^^

00000000000000000000000000000000000000000000000000000000000002

       缺失的 1个字节,EVM 会把 amount 的高位的一个字节的 0填充到 address 部分,这样使得amount 向左移位了 1个字节,即向左移位 8。这样,amount 就成了2 << 8 = 512。

3、构造短地址攻击

(1)首先需要生成一个 ETH 的靓号,这个账号末尾为 2个0,使用一些跑号工具就可以做到,比如MyLinkToken 工具,可以很轻易跑出末尾两个 0的。笔者写这篇文章时 MyLinkToken 已正式改名为 “币合钱包”,且最新版本为 2.4.3.0,下载地址:https://bbs.bihe.one/thread-322.htm,打开钱包后,选择“靓号生成器选项卡”

MyLinkToken

(2)找一个交易所钱包,该钱包里 token 数量为 256000;

(3)往这个钱包发送 1000 个币;

(4)然后再从这个钱包中提出 1000 个币,当然这时候写地址的时候把最后两个 0 去掉,如果交易所并没有校验用户填入的以太坊地址,则 EVM 会把所有函数的参数一起打包,会把 amount 参数的高位 1 个字节吃掉;

(5)这三个参数会被传入到 msg.data 中,然后调用合约的 transfer 方法,这时 amount 由于高位的 1 个字节被吃掉了,因此 amount = amount << 8,即扩大了 256 倍,这样就把 25600 个币全部提出来了。

4、总结

       针对这个漏洞,说实话以太坊有不可推卸的责任,因为 EVM 并没有严格校验地址的位数,并且还擅自自动补充消失的位数。此外,交易所在提币的时候,需要严格校验用户输入的地址,这样可以尽早在前端就禁止掉恶意的短地址。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值