# Array Finance 攻击策略

2021年7月18号，我们的一直实时运行的攻击检测工具向我们报了多笔疑似攻击交易，经过[人工分析](https://www.zhihu.com/search?q=%E4%BA%BA%E5%B7%A5%E5%88%86%E6%9E%90\&search_source=Entity\&hybrid_search_source=Entity\&hybrid_search_extra=%7B%22sourceType%22%3A%22article%22%2C%22sourceId%22%3A390916234%7D)，我们确定了这是一次针对Array Finance 的攻击事件。下面我们首先根据一笔攻击交易分析攻击这的攻击流程，然后再分析Array Finance 的代码来展现攻击原理。

### 样例[攻击交易](https://www.zhihu.com/search?q=%E6%94%BB%E5%87%BB%E4%BA%A4%E6%98%93\&search_source=Entity\&hybrid_search_source=Entity\&hybrid_search_extra=%7B%22sourceType%22%3A%22article%22%2C%22sourceId%22%3A390916234%7D)

[0xa17bbc7c9ab17aa88fdb5de83b41de982845e9c9c072efff6709dd29febf0daa](https://link.zhihu.com/?target=https%3A//cn.etherscan.com/tx/0xa17bbc7c9ab17aa88fdb5de83b41de982845e9c9c072efff6709dd29febf0daa)

### 攻击流程

<br>

![](https://pic1.zhimg.com/80/v2-12a31c6cf418120ec03734acfab93bac_720w.jpg)

<br>

图一

如图一所示，我们首先忽略了中间大量冗余的细节，只看这笔交易的头尾部分，可以发现，这笔交易的攻击者通过AAVE大量的闪电贷获利了 186.62 WETH （本文不区分Ether和WETH）。

<br>

![](https://pic1.zhimg.com/80/v2-549299fc9641884c6005b08c9e00731c_720w.jpg)

<br>

图二

具体的攻击细节如图二所示, - 攻击者首先调用Array Finance的`buy`函数：用45.91个WETH，获得了430个由Array Finance铸出来的 ARRAY token; - 攻击者然后调用了一个闭源合约(Array Collater, [0xa800cda5](https://zhuanlan.zhihu.com/0xa800cda5f3416a6fb64ef93d84d6298a685d190d))的`joinPool`函数连续五次：总共存入676,410.58 DAI + 679,080.46 USDC + 901.82 WETH + 20 WBTC + 20 renBTC, 获得了726.38 个由Array Collater 铸出来的 aBPT token - 攻击者再调用了Array Finance的`sell`函数：毁坏（Burn）了刚刚铸出来的430个[ARRAY token](https://www.zhihu.com/search?q=ARRAY+token\&search_source=Entity\&hybrid_search_source=Entity\&hybrid_search_extra=%7B%22sourceType%22%3A%22article%22%2C%22sourceId%22%3A390916234%7D), 获得了 77.17 个 aBPT token; - 攻击者最后调用了Array Collater 的`exitPool`函数：将上两笔获得的大概804.55 aBPT token 毁坏(Burn), 获得了总共 748,271.55 DAI + 751,225.08 USDC + 997.62 WETH + 22.63 WBTC + 22.74 renBTC.

根据图二，我们基本可以确定攻击者的获利来源：第5步(*Invoke the `sell` function*) 获得的77.17个aBPT 要比第3步(*Invoke the `buy` function*)中存入的 49.9142 WETH 更加值钱。我们下面通过分析代码来确认这种情况为什么会出现。

### 代码漏洞

下图是Array Finance的`sell`函数，可以看出，在这个调用中Array Finance计算攻击者拥有的ARRAY token的余额，并调用内部函数`_sell`计算能够获得的aBPT token数量

<br>

![](https://pic4.zhimg.com/80/v2-140711f2f0e9098aaf864c0cc004f9bf_720w.jpg)

<br>

下图是`_sell`函数的实现，通过调用`calculateLPtokensGivenArrayTokens`函数计算给定的ARRAY token的数量能换取多少aBPT token。随后Burn掉ARRAY token，返回aBPT token。

<br>

![](https://pic3.zhimg.com/80/v2-8dff6f840e1177ddc815a8b46f72e996_720w.jpg)

<br>

下图是`calculateLPtokensGivenArrayTokens`函数的实现。

<br>

![](https://pic4.zhimg.com/80/v2-80877db809bea0b7e227b744107cf577_720w.jpg)

<br>

注意到有四个参数会影响amountLPToken的计算，通过阅读`saleTargetAmount`的函数源码，我们总结出的计算公式如下：

```
arraySmartPool.totalSupply() * (1 - (1 - amount / ARRAY.totalSupply()) ^ (1000000 / reseveRatio))
```

其中，arraySmartPool是Array Collater的地址 ([0xa800cda5](https://zhuanlan.zhihu.com/0xa800cda5f3416a6fb64ef93d84d6298a685d190d))，arraySmartPool.totalSupply() (就相当于aBPT token的totalSupply)的值会随着攻击者不断将FlashLoan借入的资金填充进Array Collater里而不断增加。

通过深入分析整个交易，我们发现在下面的内部交易中arraySmartPool.totalSupply() (相当于aBPT token的totalsupply)的值由于攻击者借用FlashLoan向池子中注入资金而不断增加：

```
InternalTxnIndex: 64 arraySmartPool.totalSupply():  110162296218708026400
InternalTxnIndex: 107 arraySmartPool.totalSupply():  165243444328062039600
InternalTxnIndex: 150 arraySmartPool.totalSupply():  247865166492093059400
InternalTxnIndex: 193 arraySmartPool.totalSupply():  371797749738139589100
InternalTxnIndex: 236 arraySmartPool.totalSupply():  557696624607209383650
InternalTxnIndex: 280 arraySmartPool.totalSupply():  836544936910814075475
```

其中后5次数值变大正好对应了攻击者5次调用joinPool函数向池子中注入流动性。

从arraySmartPool的代码中也不难验证这一点。下面是arraySmartPool的`joinPool`函数（其中省略了部分注释和检查）：

<br>

![](https://pic2.zhimg.com/80/v2-bf2caffe37a2e1246670ad135e9c7e25_720w.jpg)

<br>

该函数首先调用SmartPoolManager.`joinPool`函数计算出需要从`msg.sender`那里拿到的token数量actualAmountsIn，然后对每个token和数量调用`_pullUnderlying`函数把token取到arraySmartPool中，最后调用`_mintPoolShare`和`_pushPoolShare`函数，mint aBPT token并将这些aBPT Token转给`msg.sender`。

注意到arraySmartPool本身是继承了PCToken类，其本身是一个ERC-20 Token的实现，而`_mintPoolShare`是直接调用`_mint`函数，后者的实现如下：

<br>

![](https://pic2.zhimg.com/80/v2-e03537edbbc4f389f6697a6516f53cad_720w.jpg)

<br>

可以看出，`_mint`函数会操纵Token内部的varTotalSupply变量（增加指定的数量），而totalSupply()则会直接返回这个值。因此，每次joinPool都会使这个参数增加。

### 获利分析

攻击交易及对应的获利

![](https://pic2.zhimg.com/80/v2-7aaca292e0ddb7e346c6d27977849e99_720w.jpg)

### 总结

在本次[Array Finance攻击](https://www.zhihu.com/search?q=Array+Finance%E6%94%BB%E5%87%BB\&search_source=Entity\&hybrid_search_source=Entity\&hybrid_search_extra=%7B%22sourceType%22%3A%22article%22%2C%22sourceId%22%3A390916234%7D)事件中，攻击者利用 Array Finance 的计价机制依赖于 aBPT 的totalSupply这一点攻击了 Array Finance. 其攻击原理正是我们早些时候在论文[DeFiRanger: Detecting Price Manipulation Attacks on DeFi Applications](https://link.zhihu.com/?target=https%3A//arxiv.org/abs/2104.15068)中揭露的price dependency 问题。
