本文分析了 Tornado Cash Governance 被攻击的事件,攻击者利用 CREATE、CREATE2 和 selfdestruct 等 Solidity 特性,通过部署包含 selfdestruct 函数的恶意提案,在提案通过后删除原合约并在相同地址部署恶意合约,从而控制了 Governance 合约,盗取了大量资金,并详细解释了攻击的步骤和技术原理,以及如何利用 CREATE 和 CREATE2 部署相同地址的合约。
5月13日,Tornado Cash 的治理系统被黑客攻击。
让我们了解一下这是如何发生的,以及漏洞是什么。这篇文章不会涉及统计数据,更多的是关于攻击的技术路线。
简而言之——攻击者主要使用 CREATE、CREATE2 和 selfdestruct 来利用治理系统。他们提出了一个与之前通过的提案相同的合约,但这个提案有一个 selfdestruct 函数,没有被注意到。在被接受后,黑客删除了提案合约,并在相同的地址部署了一个恶意合约。由于治理系统已经接受了这个地址,他们获得了对治理合约的完全控制权。
首先,治理的工作方式是成员提交他们的提案,其他成员投票赞成或反对该提案。要参与治理,你需要锁定特定的代币(在这种情况下是 TORN 代币)。
在投票或创建提案后,代币将被完全锁定,直到提案执行或被拒绝。提案必须以经过验证的智能合约的形式提交,如果获得 DAO 的批准,代码将通过 delegatecall 在治理合约中执行。
来源 — https://tornado-cash.medium.com/tornado-cash-governance-proposal-a55c5c7d0703
到目前为止一切似乎都很好,除非我们了解到攻击者使用的主要漏洞是 selfdestruct()
以及 CREATE2 操作码。
https://etherscan.io/tx/0xf93536162943bd36df11de6ed11233589bb5f139ff4e9e425cb5256e4349a9b4
emergencyStop
,该函数应该执行 selfdestruct
函数并删除合约。我们可以用“特洛伊木马”这个词来形容它。攻击者部署提案合约。来源 — https://youtu.be/whjRc4H-rAc
selfdestruct
,然后在相同的地址部署一个新的恶意合约。攻击者删除提案合约。来源 — https://youtu.be/whjRc4H-rAc
攻击者在相同的地址部署一个新的恶意合约并劫持合约,来源 — https://youtu.be/whjRc4H-rAc
让我们理解上面提到的步骤 2。他们如何能够在相同的地址上部署具有不同代码的新合约?
为了回答这个问题,我们应该了解的最基本的概念是在使用 CREATE 和 CREATE2 操作码时智能合约地址生成的流程。
当我们使用 CREATE 操作码来部署合约时,为该合约生成的地址取决于创建者的地址和创建者的 nonce。
以下是使用发送者的地址和 nonce 生成地址的方式:
address of contract = last 20 bytes of sha3(rlp(sender,nonce))
你知道吗? 智能合约地址也有一个 nonce。但它与 EOA nonce 不同,因为它只在合约部署另一个合约时才会增加,而不是每次合约调用我们称之为“内部交易”的其他合约函数时都会增加。在 EIP 161 之后,nonce 从 1 开始,而不是新部署的合约中的 0。请记住这一点,当我们继续前进。
在 CREATE2 中,不需要 nonce。地址生成取决于 4 个参数。
我们甚至可以在部署之前计算地址。Here 是一个你可以查看的小例子。
用于生成地址的 solidity 代码如下所示。
address of contract =
address(
uint160(
uint256(
keccak256(
abi.encodePacked(
bytes1(0xFF),
address(this),
salt,
keccak256(creation code)
)
)
)
)
);
我们现在可以说,为了在与之前相同的地址上部署合约,我们需要满足以下 2 个条件。
现在让我们看看黑客如何在这种情况下结合 selfdestruct
、CREATE 和 CREATE2 的潜力。我将再次写下这些步骤,但这次是以更容易理解的方式。
到目前为止,流程如下所示:
这些框是合约
虚线框是销毁(删除)的合约
CREATE 取决于发送者的地址和 nonce,每次合约部署另一个合约时,nonce 都会增加 1。
CREATE2 取决于发送者的地址、salt 和创建代码。这意味着 nonce 不需要相同,但合约创建代码应该相同。
很明显,为了在与提案合约地址相同的地址上部署恶意合约,我们不能使用 CREATE2,因为代码已经更改,创建代码也是如此。
这意味着我们需要使用默认的部署方法,即 CREATE。
但这里我们又遇到了另一个问题,即在部署提案合约后,发送者合约的 nonce 将增加 1,并且在部署恶意合约时,将生成不同的合约地址。
这就是黑客也销毁发送者合约的原因,self-destruct 也会将地址 nonce 重置为 0。这样,在使用 CREATE2 部署发送者合约后,nonce 将再次为 1,并且地址已经相同。这导致在之前部署已接受的提案合约的相同地址上部署恶意合约。
就这样,攻击者获得了完全控制权。通过这次接管,他们解锁了锁定的 TORN 代币,并且每个受攻击者控制的地址都分配了 10,000 个治理代币,总计 120 万张选票。
有了这么多选票,攻击者完全控制了 Tornado Cash 治理系统,因为只有大约 70,000 张合法的选票存在。他们拿走了价值约一百万美元的资产。还使用了 Tornado Cash 本身来取出 ETH。
- 原文链接: decipherclub.com/tornado...
- 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!