# 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 问题。


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://xiangganzi.gitbook.io/flashloan/shan-dian-dai-gong-ji-ce-lve/array-finance-gong-ji-ce-le.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
