> ## Documentation Index
> Fetch the complete documentation index at: https://docs.tenderly.co/llms.txt
> Use this file to discover all available pages before exploring further.

# 调查一起黑客攻击（Cover Protocol）

> 在 Tenderly 中分析 Cover Protocol 漏洞，方法是导入合约、模拟存款，并在 Debugger 中追踪缓存漏洞。

此示例将简要展示如何使用 Tenderly 分析甚至预防像 [Cover Protocol](https://twitter.com/CoverProtocol) 所发生的黑客攻击。

Cover Protocol 遭到黑客攻击是因为代码无法更新存储中的缓存。这本可以通过在存款/提款等重要函数和特定状态变化上[**设置警报**](/monitoring/alerts/introduction)来预防攻击。

为了手动分析此黑客攻击，我们需要[**导入智能合约**](/developer-explorer/contracts) `Blacksmith`，漏洞就在其中被发现。如果您知道受影响协议或易受攻击合约的确切名称，也可以直接搜索它。

![](https://lh3.googleusercontent.com/BVJobjB-JHrSGyTHuC7v3cPLBbaUnNJH3pda8uJrHU2qCSV6ms-84BC614vdozpemjg5FO8J4cu-B9RjTTLfYWRQyBneYVBbLy5fv5_OQA1N-svrza9ZF6Q6xsUA5o37YyYPnaP4)

<Warning>
  注意——您只能搜索已在 Tenderly 或 Etherscan 上公开验证的合约。[**请在此处阅读更多关于如何在 Tenderly 上验证合约的信息。**](/contract-verification/overview)
</Warning>

如果合约未公开验证，您可以自行验证以便能够在 Tenderly 中使用它。如果项目是开源的（如 Cover），您可以转到该项目的 [GitHub 仓库](https://github.com/CoverProtocol/cover-token-mining)，找到您要使用的合约（及其源代码）并自行验证。

![](https://lh4.googleusercontent.com/vWVW6dAj1J6d83juyoXZ3cR9DTMmVgXu9PYHUUL4_Mv5LRAJ8wnXSckv72tEv-C96zOeutweZaLAx4OEaVBon1PkZOGMBmPwKLlTBJuPEAaPkZSDjwwtip3Kzx1d8B-UzYA7wRkE)

![](https://lh4.googleusercontent.com/C6BHcXHp8NHQmkb7kJP2vwNDi86mVgb-GyHb5Fp9OtddF3anpTTCdkERiNXAGYXnuhippPJKPuxf6a9RW1GIu91nECQWoV033C7gg1GHnz5tsoEP3BkkcMMiAHDCUspbmMz-BVUf)

现在我们有了要导入到 Tenderly 的合约，转到您的 Dashboard 并点击左侧的 **Contracts** 选项卡，然后点击右上角的 **Add Contract** 按钮：

<img src="https://mintcdn.com/tenderly/XsEZlaGXYskrtN68/images/Screenshot%202021-12-01%20at%2010.28.37.webp?fit=max&auto=format&n=XsEZlaGXYskrtN68&q=85&s=51b1b58ec98df00a2b0526ec24d6a581" alt="" width="2521" height="787" data-path="images/Screenshot 2021-12-01 at 10.28.37.webp" />

您可以通过 Dashboard 上传并验证合约的代码，或通过 CLI [\[更多信息在此\]](/contract-verification/overview)：

<img src="https://mintcdn.com/tenderly/XsEZlaGXYskrtN68/images/Screenshot%202021-12-01%20at%2010.30.25.webp?fit=max&auto=format&n=XsEZlaGXYskrtN68&q=85&s=24ce923a3732175540cccc93975e4001" alt="" width="2521" height="787" data-path="images/Screenshot 2021-12-01 at 10.30.25.webp" />

<img src="https://mintcdn.com/tenderly/XsEZlaGXYskrtN68/images/Screenshot%202021-12-01%20at%2010.30.30.webp?fit=max&auto=format&n=XsEZlaGXYskrtN68&q=85&s=07faeb1cb135bdf6fca956ac178441cf" alt="" width="2521" height="927" data-path="images/Screenshot 2021-12-01 at 10.30.30.webp" />

现在，在导入并验证合约后，让我们分析黑客攻击本身。我们知道漏洞存在于 deposit 函数的缓存中，所以我们模拟该函数。如下面的截图所示，执行后它失败，报错为 `Blacksmith: pool does not exist`。

![](https://lh5.googleusercontent.com/JlFsuawkRAxjCnd7sHefFA9n-WpUOPMWIlnXb_IBOrdvQI8OFvRvTsWPH02p9qy1tmqDf8HnwVgJM2hK3x2iAmvWXNLj-qNLqDzyUmBBG4nbRAmkjfkpRGkh8748cFr052jEoaBL)

现在，让我们在 Tenderly Debugger 中打开这个错误。在第 118 行，我们看到 deposit 函数中的 `Pool memory pool = pools[_lpToken];`，数据在此被缓存，然后在第 125 行更新，我们看到 `_claimCoverRewards(pool, miner);`

合约使用了缓存数据，因为数据本身留在内存中；这就是为什么后来合约使用这些数据计算函数。缓存没有更新导致了这次黑客攻击：

```solidity title="example.sol" showLineNumbers theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
miner.rewardWriteoff = miner.amount.mul(pool.accRewardsPerToken).div(CAL_MULTIPLIER);
```

```solidity title="example.sol" showLineNumbers theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
miner.bonusWriteoff = miner.amount.mul(bonusToken.accBonusPerToken).div(CAL_MULTIPLIER);
```

![](https://lh3.googleusercontent.com/6jPAgWs9ZLNkM7PzW_g-0kR0Wy5kYMtk80huWk_KzsfpF7WBTrhE3VIWvPTI0bwVQmR25TLsARV-Ea3f2mK_kwcYESQEjxKebHv19YbNEuG-Jr0MJQ4t_LsxV-Q83rB_SC1NGV1v)

```solidity title="example.sol" showLineNumbers theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
function deposit(address _lpToken, uint256 _amount) external override {
    require(block.timestamp >= START_TIME, "Blacksmith: not started");
    require(_amount > 0, "Blacksmith: amount is 0");

    Pool memory pool = pools[_lpToken];

    require(pool.lastUpdatedAt > 0, "Blacksmith: pool does not exists");
    require(IERC20(_lpToken).balanceOf(msg.sender) >= _amount, "Blacksmith: insufficient balance");

    updatePool(_lpToken);

    Miner storage miner = miners[_lpToken][msg.sender];
    BonusToken memory bonusToken = bonusTokens[_lpToken];

    _claimCoverRewards(pool, miner);
    _claimBonus(bonusToken, miner);

    miner.amount = miner.amount.add(_amount);

    // update writeoff to match current acc rewards/bonus per token
    miner.rewardWriteoff = miner.amount.mul(pool.accRewardsPerToken).div(CAL_MULTIPLIER);
    miner.bonusWriteoff = miner.amount.mul(bonusToken.accBonusPerToken).div(CAL_MULTIPLIER);

    IERC20(_lpToken).safeTransferFrom(msg.sender, address(this), _amount);
    emit Deposit(msg.sender, _lpToken, _amount);
}

```

* 在攻击发生前几个小时，一个新池被批准用于流动性挖矿。这个池完全正常，但由于它是新的，Blacksmith 合约中没有此池的任何 LP 代币。
* 攻击者将此池的一些代币存入了 Blacksmith 合约。
* Blacksmith 合约按每代币基础跟踪奖励。如果锁定了大量代币，每代币的奖励将很小。如果锁定的代币很少，每代币的奖励将很大。相关变量称为 `accRewardsPerToken`，计算为 `totalPoolRewards / totalTokenBalance`。
* 然后攻击者从 Blacksmith 合约中提取了几乎所有的 LP 代币，将 `totalTokenBalance` 数量减少到几乎为零。
* 然后攻击者再次将此池的一些代币存入 Blacksmith 合约。这就是漏洞暴露出真面目的地方。由于 `totalTokenBalance` 在前一交易中被大幅减少，新计算的 `accRewardsPerToken` 飙升。合约使用 `rewardWriteoff` 来控制 `accRewardsPerToken` 的影响。然而，由于漏洞，在计算 `rewardWriteoff` 值时使用了 `accRewardsPerToken` 的旧（小）值。因此，`accRewardsPerToken` 的大值没有被控制。
* 然后攻击者提取了他们的奖励。由于 `accRewardsPerToken` 中存在一个大的、未被控制的值，从系统支付的总奖励被抬高，**合约最终铸造了 40,796,131,214,802,500,000 枚 COVER 代币**。

<Note>
  感谢 Mudit Gupta 对此次黑客攻击最后部分的分析——[在他的博客上阅读更多信息](https://mudit.blog/cover-protocol-hack-analysis-tokens-minted-exploit/) :)
</Note>
