英文原文:https://arvanaghi.com/blog/pentesting-ethereum-dapps/
译文来自安全客,译者:興趣使然的小胃
链接:https://www.anquanke.com/post/id/146602

一、前言

所谓的以太坊(Ethereum)去中心化应用(decentralized application,dApp),指的就是基于共识协议(consensus protocol)交互的应用。在本文中,我们选择的目标为dApp最常见的某类应用场景:与一个或者多个智能合约(smart contracts)交互的常规Web应用。

当我们通过Web访问某个dApp时(比如使用MetaMask之类的扩展),我们就可以使用自己的私钥与网站交互,通过web接口对交易进行签名。

典型的dApp如下所示,这里我使用了web接口以及包含以太坊钱包的Chrome扩展来购买Cryptokitty(区块链养猫):

当浏览器与常规Web应用进行交互时,Web应用可能会与其他内部服务器、数据库或者云端再次交互。最后在我们看来,整个交互过程非常简单:

对于dApp,大多数交互过程与之类似,但这里还涉及到第三个元素:所有人都可以公开访问的智能合约。

与Web应用的某些交互操作涉及到读写以太坊区块链上的某个或者多个智能合约。

二、多管齐下

dApp之所以会出现,目的是为了让最终用户能更加方便地与智能合约进行交互。但现在并没有明确的规定,要求我们必须通过dApp的Web接口来与dApp的智能合约交互。由于大家都可以公开访问智能合约,因此我们可以直接与智能合约交互,不受Web服务器处理逻辑的限制,这些处理逻辑可能会限制我们发起的具体交易。

目前为止,我们可以通过两种方式进行渗透测试:

  1. 标准的Web应用渗透测试,涉及身份认证、访问控制以及会话管理等方面技术。
  2. 智能合约审计。

换句话说,我们可以检查Web应用以及智能合约的处理逻辑,看这两者中是否存在逻辑错误。

由于以太坊中存在modifiers(修改器)特性,因此其实我们还可以挖掘出另一种方式进行渗透测试。

三、Modifiers

在以太坊中,我们可以编写只能从特定以太坊地址调用才能执行的函数。onlyOwner就是modifier的一个典型实例,如果我们正确实现了onlyOwner,那么只有合约的所有者(owner)才能运行某些函数。

contract mortal {
    /* Define variable owner of the type address */
    address owner;

    modifier onlyOwner {
        require(msg.sender == owner);
        _;
    }

    function writeData(bytes32 data) public onlyOwner returns (bool success) {
        // will only run if owner sent transaction
    }
    ...
}

虽然我们可以直接与智能合约进行交互,但如果正确实现了类似onlyOwner之类的modifier,我们就无法执行某些函数。然而在处理dApp时,Web服务器基本上都会保存对应特权地址的私钥数据,并且Web应用总是具备特定逻辑,可以接受用户在Web端的输入,然后使用其中某个私钥来调用智能合约中的某个特权函数。

由于dApp的确可以访问这些特权以太坊地址,那么第三条渗透测试分支就变成:“我们应该如何才能让dApp帮我们写入智能合约中的这些特权函数?”

考虑到这一点,现在我们的攻击面已经扩展为:

  1. 标准的Web应用评估(身份认证、访问控制、会话管理)。这个过程可能不涉及到智能合约,只涉及到横向或者纵向的权限提升、数据库注入、XSS等。
  2. 智能合约审计。权限问题、向上或者向下溢出、竞争条件等等。
  3. 尝试通过Web接口伪造智能合约的写入权限。我们是否能找到一种方式,使Web应用以非预期的方式来与智能合约交互?

我之前写过一段话,可以用来描述dApp的攻击面:

想象一下有个普通的Web应用,可能带有各种安全漏洞。现在,想象一下有一款应用,具备如下特点:

1、从头开始构建自己的数据库软件;

2、将这些数据库对外开放;

3、有些时候这些数据库中会存有真金白银。

这就是以太坊dApp开发者所需要面临的安全挑战。

— Brandon Arvanaghi (@arvanaghi) 2018年5月28日

四、使用Burp拦截请求

当我们注册Cryptokitties账户时,Web应用的处理逻辑会从用户的MetaMask扩展中提取用户所公开的以太坊地址,随后,网站会要求我们输入邮箱地址以及账户昵称。

接下来这一步比较重要:由于dApps面向以太坊账户,因此基于的是公钥认证机制,而非密码认证机制。

因此,Cryptokitties会要求我们签名某条消息(“Cryptokitties”),确保我们拥有与该地址对应的私钥。

如果我们拦截这个请求,可以看到如下数据:

从理论上来讲,Cryptokitties会验证sign参数中的数据(即已签名的“Cryptokitties”消息)是否与address参数中的数据(即我们的以太坊地址)对应。

这个验证过程位于Web应用的处理逻辑中。我之前碰到过有些dApp没有正确处理签名验证过程,因此我可以将请求中的以太坊地址替换成与签名不匹配的以太坊地址,如下所示:

Cryptokitties可以正确处理签名验证逻辑,但如果某个dApp无法做到这一点,我就可以通过伪造的以太坊地址发起拒绝服务攻击,也可以在该应用上伪造账户身份。

五、登录过程

Bloom是最为高级的dApp之一,我们可以利用其创建一个去中心化的身份标识。背后的原理就是将身份标识与用户的以太坊地址绑定,这样就不会像社会安全码(SSN)那样被窃取或者伪造。由于Bloom是迄今为止最为成熟的dApp之一,因此我向大家隆重推荐这款应用,如果大家还没有创建自己的BloomID,可以考虑尝试一下。

Bloom在认证处理方面非常严格。注册账户后,后续过程中如果我们需要从以太坊地址进行登录,都需要签名一条消息,该消息中包含用户的意图(我正在登录)、邮箱地址以及当前时间。

Bloom为什么考虑对这些字段进行签名?原因有以下几点:

1、操作意图:如果用户不了解他们所签名的具体内容,那么签名以及发送签名数据有时候是比较危险的操作。Bloom在消息文本中明确给出了签名操作的具体意图。

2、邮箱地址:Bloom会从签名中恢复邮箱地址,检查该地址是否为签署消息的以太坊地址的注册邮箱。如果两者匹配,则此次登录为有效登录操作(在当前登录环节)。

3、时间戳:用来阻止重放攻击。如果签名中未包含当前时间信息,获得该签名数据的攻击者可以随时重放这些数据,以目标用户的身份进行认证。成功签名后,如果Web应用在几分钟的时间窗口内收到签名数据,Bloom会认为这个签名为有效签名。

如果任何一个字段被篡改,就会出现错误信息,这表明Bloom在认证机制方法处理得很好。

六、智能合约漏洞

前面我们讨论了智能合约的某种攻击方式,直接分析了智能合约中是否存在安全漏洞。接下来我们来看一下现实环境中已挖掘出的某些漏洞。

batchOverflow

这个漏洞称之为batchOverflow漏洞,从名字中我们不难猜到这是一个溢出漏洞。

我发起了一次小测试,想看看大家是否有解决这个溢出漏洞的方法。在查看答案之前,大家可以试一下自己能否找到利用该漏洞的具体方法。

大家可以访问此链接查看详细分析过程。

重新初始化钱包所有者

我不会把这个漏洞归为“黑客攻击”范畴,但Parity的确因为没有正确实现某个modifier(我们前面分析过这方面内容)而损失惨重。此时任何人都可以调用initWallet函数,将自己的地址设置为该钱包的所有者。

通常情况下,钱包(或者合约)的所有者需要在合约的构造函数中进行设置,构造函数只会被调用一次。如果后续想修改该地址,就需要在某个函数的处理逻辑中进行操作,而该函数需要原先所有者的签名数据。这种情况下,由于不存在这类modifier,因此攻击者可以随时调用initWallet。

大家可以访问此链接查看详细分析过程。

七、智能合约审计工具

在审计智能合约代码方面,已经存在一些非常强大的开源工具,其中包括Trail of Bits的Manticore以及ConsenSys的Mythril,我会在另一篇文章中详细介绍这些工具细节。

八、总结

希望阅读本文后,大家可以更好地理解dApp的攻击面,也能了解dApp与标准Web应用之间的差异。

如果大家想了解以太坊或者区块链安全方面的更多内容,欢迎关注我的推特,我会经常更新相关内容。


本文经安全客授权发布,转载请联系安全客平台。


源链接

Hacking more

...