LOADING...
LOADING...
LOADING...
当前位置: 玩币族首页 > 新闻观点 > [BlockSec DeFi攻击分析系列之三] 偷天换日:深度剖析Akropolis攻击事件

[BlockSec DeFi攻击分析系列之三] 偷天换日:深度剖析Akropolis攻击事件

2021-08-03 BlockSec 来源:区块链网络

去中心化金融 (DeFi) 作为区块链生态当红项目形态,其安全尤为重要。从去年至今,发生了几十起安全事件。

BlockSec 作为长期关注 DeFi 安全的研究团队 (https://blocksecteam.com),独立发现了多起 DeFi 安全事件,研究成果发布在顶级安全会议中(包括 USENIX Security, CCS 和 Blackhat)。在接下来的一段时间里,我们将系统性分析 DeFi 安全事件,剖析安全事件背后的根本原因

往期回顾:
(1) [BlockSec DeFi 攻击分析系列之一] 我为自己代言:ChainSwap 攻击事件分析
(2) [BlockSec DeFi 攻击分析系列之二] 倾囊相送:Sushiswap 手续费被盗

0xffffffff. 前言

北京时间 2020 年 11 月 12 日夜晚。DeFi 聚合器 Akropolis 遭受攻击,黑客通过 Flashloan (闪电贷)+ 重入的攻击方式,盗取了存储在 Akropolis 中价格超过 2,000,000 美元的数字资产。本文将以一笔攻击交易为例,深度解读此次攻击的原理。

时间:Nov-12-2020 12:04:02 PM +UTC #11242695


阅读建议:

如果您刚刚接触 DeFi (Ethereum),可以从头看器,但是文章比较长,看不下去记得点个关注再走。如果您对 Akropolis 等 DeFi 聚合器项目比较了解,可以直接从「0x2 攻击分析」开始。

0x0. 背景介绍


2020 年可谓是去中心化金融(下文简称 DeFi)的元年。随着 DeFi 生态的不断发展,越来越多的 DeFi 项目上线以太坊。普通用户可以选择自己喜欢的 DeFi 项目进行投资以获得收益。但是随着 DeFi 市场的急剧扩张,普通用户面临的一个问题就是在众多 DeFi 项目中如何选择投资的目标,他们需要有人来帮助他们来进行投资。在这种情况下,一系列资产托管平台、投资组合平台应需而生。Akropolis 就是众多资产托管平台中的一个:

普通用户将个人的数字资产投入到 Akropolis 中,由 Akropolis 确定投资方向,优化投资策略。Akropolis 获得投资收益之后,将收益按比例分配给投资 Akropolis 的普通用户。以现实中的金融活动举例子,Akropolis 就像是基金,其所投资的 DeFi 项目就是股票,基民购买基金,基金持有股票,获得收益后将收益分配给基民。

0x1. Akropolis 机制

为了便于理解,我们首先简要地介绍一下和此次攻击相关的两个实体合约:Protocol contract和Akropolis contract

Protocol:
Protocol可以简单地理解为:用户将手中的 token 投资到Protocol中,获得Protocol的份额(股份),等到将来Protocol中的资产升值了,那么用户便可以从Protocol获取到一定的收益

Akropolis:
之前我们讲过,由于 ETH 上的 DeFi 项目太多了,如果用户要投资多个项目,要么不知如何抉择,要么操作难度比较大,所以 Akropolis 扮演了一个投资聚合器的角色:Akropolis 绑定了多个 DeFi 投资项目,用户可以很方便地通过 Akropolis 使用手里的 token 进行投资:用户只需要简单的调用 Akropolis 的函数,指定想要投资的 protocol,那么 Akropolis 就会去帮你在不同的 protocol 中完成投资。

总体的交互逻辑如图所示:

Akropolis 提供了两个外部接口:function deposit(address_protocol, address[] memory_tokens, uint256[] memory_dnAmounts)和function withdraw(address_protocol, address token, uint256 dnAmount, uint256 maxNAmount)

Akropolis.depositfucntion:
deposit函数的功能是:

用户调用deposit。Akropolis 先计算Protocol中有多少的 token:nBalanceBefore。Akropolis 将用户的 token 转入到Protocol中。Akropolis 再计算 protocol 中 token 的数量:nBalanceAfter。得到用户投资的数量:nDeposit = nBalanceAfter - nBalanceBefore。通过用户投资的数量计算得到用户投资份额。


Akropolis.depositToProtocol函数:

Akropolis.distributeYieldInternal函数和Akropolis.updateProtocolBalance函数:

Protocol.normalizedBalance函数:

注意,Protocol.normalizedBalance中的_registeredTokens = [DAI, USDC, USDT, TUSD]。deposit 函数中使用到的distributeYieldInternal函数和updateprotocolBalance函数的功能都是查询当前Protocol函数中所有代币(实际上是 DAI,USDC,USDT,TUSD)的余额,注意这里面normalizedBalance的意思是对余额归一化处理(因为存在不同种类 token 的 decimal 不一致的情况)。

用户可以调用deposit函数,通过指定Protocol和 token 以及 token 的 amount,将一定数量的代币存入到和 Akropolis 中,Akropolis 根据用户存入的代币数量给用户返还 Akropolis 的 LP Token。随后使用用户投资的 token 投入到Protocol中。

withdrawfucntion:

用户通过调用withdraw函数,取出之前通过deposit函数存入的 token,获取利息。

0x2. 攻击原理

我们注意到,在Akropolis.deposit函数中,要先计算beforeBalance,然后调用 token 的transferFrom函数,将 token 转到Protocol中,最后计算afterbalance,正常逻辑下这种计算方式是正确的,但是如果攻击者提供的 token 地址是一个恶意地址(即伪造的 token 合约,Akropolis 并没有对 token 地址进行验证),那么在这种情况下,实际有效的 token 并没有转到Protocol账户中,并且Akropolis.deposit没有防止重入的机制。这样就使得攻击者可以利用 1. token 地址未经校验,2.deposit函数没有放重入机制来对 Akropolis 进行攻击。攻击流程图如下:

**** 1:


攻击者首先调用Akropolis.deposit函数,传递的 token 地址参数是一个恶意合约地址。函数内部首先计算balanceBefore1,然后由于攻击者传递进来的 token 合约地址是攻击者伪造的 fake token,所以 Akropolis 紧接着调用了fakeToken.transferFrom函数。

**** 2:


在fakeToken.transferFrom函数中,该合约再次调用Akropolis.deposit,传递的 token 地址就是真实的 DAI 的地址,所以在第二次调用deposit的逻辑是一个正常逻辑:攻击者存入一笔 DAI,然后 Akropolis 根据balanceAfter2-balanceBefore2为攻击者 mint 出对应的 LP token。注意:这里面balanceBefore2 = balanceBefore1。

**** 3:


在 step 2 (第二次 deposit)结束后,交易控制流返回到第一次的 deposit 之中,注意:这时候 Akropolis 计算了balanceAfter1并且balanceAfter1 = balanceAfter2(其实balanceAfter2比balanceAfter1小 1,原因我们后续会说。在这里这个差值可以忽略不计当成相等),然后根据 step 1 中的balanceBefore1计算出balance之差,再一次给攻击者 mint 出 LP tokens。

虽然在整个交易中攻击者只给Protocol传进去一笔约 $25k 的 DAI,但是由于重入的机制,导致的第二次deposit之后balanceBefore1没有及时更新,使得 Akropolis 在函数控制流返回到第一次deposit函数中的时候以为攻击者又给Protocol转移了 $25k 的 DAI。从而给用户再一次 mint 出对应的 LP tokens。

Others

总体而言,攻击者通过重入+构造恶意 token 合约的方式,在仅仅花费 $25k DAI 的情况下使得 Akropolis 以为攻击者存入了 $50k DAI,最后取出这 50k DAI,完成攻击,获利 25k DAI。

Step 3 中我们提到了其实balanceAfter2比balanceAfter1小 1,这个原因是在 deposit 的后续操作中会将用户传递进来的 token 注入到Curve的池子之中,而Curve的合约中会检查Curve之中 Token 的余额变化,如果说池子的余额没有变化,那么Curve就会执行revert。所在 fake token 的transferFrom函数中一定要往protocol之中转一笔钱,不过数量不限。


可以看出在 fake token 的transferFrom函数中攻击者向Protocol转了 1 个 DAI。

0x3. 总结

由于 Akropolis 并没有对用户传递进来的 token 参数进行校验,加上deposit函数没有防重入,导致攻击者利用伪造的 token contract 重入deposit函数,从而达到偷天换日,鱼目混珠,使用少量的 DAI 换出大量的 DAI 的效果。

BlockSec 团队以核心安全技术驱动,长期关注 DeFi 安全、数字货币反洗钱和基于隐私计算的数字资产存管,为 DApp 项目方提供合约安全和数字资产安全服务。团队发表 20 多篇顶级安全学术论文 (CCS, USENIX Security, S&P),合伙人获得 AMiner 全球最具影响力的安全和隐私学者称号 (2011-2020 排名全球第六). 研究成果获得中央电视台、新华社和海外媒体的报道。独立发现数十个 DeFi 安全漏洞和威胁,获得 2019 年美国美国国立卫生研究院隐私计算比赛 (SGX 赛道) 全球第一名。团队以技术驱动,秉持开放共赢理念,与社区伙伴携手共建安全 DeFi 生态。



https://www.blocksecteam.com/
[email protected]

—-

编译者/作者:BlockSec

玩币族申明:玩币族作为开放的资讯翻译/分享平台,所提供的所有资讯仅代表作者个人观点,与玩币族平台立场无关,且不构成任何投资理财建议。文章版权归原作者所有。

LOADING...
LOADING...