# Defi安全挑战系列-Damn Vulnerable DeFi(#1 Unstoppable)

By [skye](https://paragraph.com/@skye-3) · 2023-06-20

---

> 有一个以百万DVT代币抵押的代币化保险库。在宽限期结束之前，它提供免费的闪电贷款服务。
> 
> 为了完成挑战，使该保险库停止提供闪电贷款服务。
> 
> 你的初始余额为10个DVT代币。

[https://www.damnvulnerabledefi.xyz/challenges/unstoppable/](https://www.damnvulnerabledefi.xyz/challenges/unstoppable/)

### 分析合约

挑战合约代码分为两部分，分别是提供闪电贷的金库合约（UnstoppableVault.sol）和执行闪电贷的合约(ReceiverUnstoppable.sol)。

通过阅读合约代码就可以了解到 Flashloan原理，俗称闪电贷，原理简单来说就是你的合约先调用金库合约，例如aave、uniswap等等这些合约提供闪电贷功能，金库合约先转币给你的智能合约，并且回调你的智能合约，你的智能合约收到币和被回调后执行合约逻辑去套利，执行完你的合约逻辑后要还币加手续费给金库合约。如果金库合约检查收到的币足够就交易完成，否则就触发revert中断交易。

**闪电贷的金库合约（UnstoppableVault.sol）**

其中金库合约继承了ERC4626协议。简单来说，存入100ETH，协议mint 一定量 xETH。xETH就是存入计数的，更多细节查关于ERC4626资料吧。

金库代码中还有一段Solidity的内联汇编:

        /**
         * @inheritdoc ERC4626
         */
        function totalAssets() public view override returns (uint256) {
            assembly {
                // better safe than sorry
                if eq(sload(0), 2) {
                    mstore(0x00, 0xed3ba6a6)
                    revert(0x1c, 0x04)
                }
            }
            return asset.balanceOf(address(this));
        }
    

如果不熟悉Solidity的内联汇编，通过chatGPT翻译得到：

![](https://storage.googleapis.com/papyrus_images/e431d897372eac4c2e6ac287c4f97ae1c8b7e6e511d8de2b55bfb272c3ab116c.png)

sload(0)表示从存储器中加载位置0的值。 这里涉及到Ethereum Virtual Machine (EVM)储存布局。简单来说就是合约中的全局变量是按顺序标记的。这是ERC20合约代码例子：

![](https://storage.googleapis.com/papyrus_images/b4fae4cd6fc5f1b3eaa7f8f3e8ba9f5635e4f1ceaf862718c26b447b0e4fa6ea.png)

同样constant修饰因为是确定值也是直接将值写死在代码中，不会占用插槽。 回头看ReceiverUnstoppable.sol中代码，发现ReetrancyGuard.sol中是第一个占用插槽。

![](https://storage.googleapis.com/papyrus_images/9c86545430479989c7dfe77c26a8b49d31322394344955689cb9d4cd91d19593.png)

sload(0)指的就是全局变量locked。

![](https://storage.googleapis.com/papyrus_images/be5ab66d3a5ddeba42a582ad1d1e6f2b3e58660af14d316681532b7426d4e2bc.png)

    if eq(sload(0), 2) {//其实这句代码本意是防止重入攻击
          mstore(0x00, 0xed3ba6a6)
          revert(0x1c, 0x04)
    }
    

通过挑战的关键就是UnstoppableVault.sol的104行的断言

    if (convertToShares(totalSupply) != balanceBefore)
                revert InvalidBalance(); // enforce ERC4626 requirement
    

unstoppable.challenge.js，给金库转币token，上面的断言就无法通过，金库的闪电贷功能就废了。

### 解决方案

    it('Execution', async function () {
            /** CODE YOUR SOLUTION HERE */
            await token.transfer(vault.address, 1);
        });

---

*Originally published on [skye](https://paragraph.com/@skye-3/defi-damn-vulnerable-defi-1-unstoppable)*
