原标题:以太坊代理中的恶意后门 代理所有者成为唯一可能发生冲突的帐户
关于如何利用智能合约可升级性的代理模式的详细说明。
我们最近审查了Zeppelinos的初始版本,发现了代理模式中的一个漏洞,该模式用于实现几乎所有可升级的智能合约。
此漏洞允许攻击者隐藏恶意代码,如果不深入瞭解Solidity和Proxy Pattern的工作方式,很难发现恶意代码。这已在ZeppelinOS上修复。
如果您是以太坊的开发人员,那么您最有可能使用Solidity进行编写和考虑如何设计智能合约。
从网络的角度来看,智能合约是一个与其相关联的单个代码块的帐户。如果任何其他帐户向合同发送消息,其代码将在EVM上执行。
那么如果合约只有一段连续的代码,如何调用不同的函数呢?
以太坊定义了其组件之间通信的标准方式,即应用程序二进制接口或简称ABI。您可以将其视为低级API,不仅指定系统中可用的功能,还指定我们通常认为合理的工作量。其中一些是如何调用函数,如何传递参数以及它们如何返回值。
以太坊ABI规定您的事务的数据参数必须以函数选择器开始,该函数选择器标识您尝试调用的方法。使用选择器,合约的代码会跳转到实现您尝试调用的函数的部分。
函数选择器只是函数签名的sha3散列的前四个字节。例如,get的选择器计算为sha3(“get()”)[0:4],它给出了0x6d4ce63c。类似地,set的一个是sha3的结果(“set(uint256)”)[0:4]。
函数选择器只有一个例外,这是因为在没有选择器的每个智能合约中都存在一个回退函数。当没有提供数据参数,或者给定的选择器与契约的任何方法都不匹配时,它具有被调用的特殊行为。
关于代理模式,它拥有多种变化和权衡模式。 无论您选择哪种代理模式,其核心功能都是相同的:它将收到的所有消息转发给当前的合同实现。
我们来看看它是如何工作的。
你不需要理解装配块是如何工作的。它把当前消息实现转发并将接收到的数据参数发送出去。
将转发逻辑放在回退函数中,可以让我们将任何调用转发到代理中。事实证明,这种情况并不总是发生。
代理还需要它自己的元功能(meta-functionality),因为代理需要可升级性。因此,如果存在并且不执行回退功能,则不会转发implementation()和proxyOwner()等函数。
代理选择器冲突
您可能已经意识到代理智能合约中的任何函数(其选择器与应用智能合约中的某个函数匹配)都将被直接调用,完全跳过应用码。
因为函数选择器使用固定数量的字节,所以总是存在冲突的可能性。 这不是日常开发的问题,因为Solidity编译器将检测合同中的选择器冲突,但是当选择器用于交叉智能合约交互时,这就变得可攻击了。冲突可以被滥用来创建一个看似很好的合同,但实际上隐藏了一个后门。
在使用rust代码,我们发现clash550254402()与proxyOwner()具有相同的选择器。 我们在Macbook Pro中可以很轻易能找到它。 但是黑客们可以利用该流程去寻找代码漏洞进行攻击。
代理可利用性
代理模式是在以太坊生态系统中常见的智能合约升级方法,选择器冲突攻击者可以使用它来获取升级机制的访问权攻击或部署隐藏恶意功能的代码。
例如大多数可升级实现都有一些状态迁移的概念,这是升级智能合约的存储功能。这些对于伪装选择器冲突特别有用,因为自动生成的字符串(如commit number)可以作为这些函数的可接受名称,从而使选择器冲突攻击易于隐藏。
在我们对ZeppelinOS进行的安全审计的背景下,我们发现这可以被任何人利用,而不仅仅是代理所有者,因为他们打算让网络的任何用户部署实现供其他用户使用。另一个例子是,一个函数调用似乎可以转移资金,从而实现窃取某人的资金,但实际上该函数根本不被调用。
解决方案
在我们发现此漏洞之前,Zeppelin的Francisco Giordano已经开始研究透明代理。这是一种改进的技术,旨在让实现智能合约使用与代理相同的函数名,而不会出现选择器冲突,这就消除了攻击。
新代理通过转发任何函数调用来工作,只要它们不是来自代理所有者。但是冲突仍然存在,如果调用方不是代理所有者,则会转发调用。这使得代理所有者成为唯一可能发生冲突的帐户,因此用户不会受到隐藏攻击。
唯一的缺点是其他用户将无法使用代理的ABI读取代理的自身状态(即所有者和实现)。他们将需要使用web3.eth.getstorageat()。这是一个相当小的代价,因为要确保可升级的合同完全按照其实现源代码所显示的方式执行。
总结
对于那些想更深入地瞭解如何利用这个漏洞的人,我们做了一个小练习。你的任务是试图在合同中窃取ropsten-ETH
并弄清楚发生了甚么。 请记住,这是一个代理合同,所以你也应该看看它的实施。
你可以用这些合同做你想做的任何事情,只是不要完全清空它的余额,这样其他人也可以玩。
消息是帐户之间的通信方式。当您发送交易时,您正在向另一个帐户发送消息。当发送方是智能合约时,它们通常称为内部交易。
消息实际上并不像传统代理那样转发。所发生是在我们执行应用码时,就好像它是通过delegatecall执行代理的代码一样。