漏洞条件:
- 当地址的长度不足 20 byte (40个十六进制字符) 时,以太坊虚拟机 EVM 会进行高位补0x0
- 转发以太币时没有对地址长度进行验证
1. input 数据
交易的 input 数据分为三个部分:
1)4字节,是方法名的Hash值:a9059cbb。
2)32字节,存放以太坊地址,目前以太坊地址是20个字节(40个十六进制字符),高位补0x0,满足32字节,比如 0000000000000000000000001234567890123456789012345678901234567800。
3)32字节,是需要传输的代币数量,不足32字节,高位补0x0,满足32字节,比如00000000000000000000000000000000000000000000000000000adba0ce5362。
这三部分合起来就是交易数据:
a9059cbb000000000000000000000000123456789012345678901234567890123456780000000000000000000000000000000000000000000000000000000adba0ce5362
2. 补足机制
当以太地址长度不够时,EVM会从下一个参数(代币数量)的高位拿到 00 来补充以补成预期的长度,而代币数量不够后,会在其高位补 0x00
例如:以太地址 0000000000000000000000001234567890123456789012345678901234567800 去掉最后一个字节 0x00 ,EVM 就会将代币数量的前一个字节 0x00 作为地址的内容,后面的代币数量缺少的位数就会用 0x00 补充,也就是代币数量末尾添加了 0x00,这里代币数量末尾为 0x2,补位后变成了 0x200,这样就导致代币数量增加了 2^8 倍:000000000000000000000000000000000000000000000000000adba0ce536200
以太地址变化
0000000000000000000000001234567890123456789012345678901234567800 => 去掉00
00000000000000000000000012345678901234567890123456789012345678 => 补足00
0000000000000000000000001234567890123456789012345678901234567800
代币数量变化
00000000000000000000000000000000000000000000000000000adba0ce5362 => 高位00被借
000000000000000000000000000000000000000000000000000adba0ce5362 => 末尾补足
000000000000000000000000000000000000000000000000000adba0ce536200
pragma solidity ^0.4.11;contract ShortAddress{mapping (address => uint) balances;event Transfer(address indexed _from , address indexed _to, uint256 _value);function ShortAddress() {balances[tx.origin] = 10000;}function transfer(address to, uint amount) returns(bool success) {require(balances[msg.sender] > amount);balances[msg.sender] -= amount;balances[to] += amount;Transfer(msg.sender, to, amount);return true;}function getBalance(address addr) constant returns(uint) {return balances[addr];}
}