2023 年 10 月 7 日,friend.tech 仿盘项目 Stars Arena 遭到攻击。他们在 twitter 中表明合约中的资金已被攻击者抽干(https://twitter.com/starsarenacom/status/1710604987812708531),损失约 290 万美元。
几小时之后,Stars Arena 发推表示,他们已获得资金来弥补漏洞造成的损失,并邀请白帽开发团队快速审查平台的安全性。新的合约将在全面的安全审计后重新开启(https://twitter.com/starsarenacom/status/1710666706262138953)。
该攻击的本质在于项目方合约中存在重入漏洞,导致攻击者可以从该项目的合约中窃取大量 AVAX 代币。下面我们将对攻击路径进行详细的分析。
攻击事件分析
背景知识
项目简介
Stars Arena 是玩法类似于 Friend.Tech 的 SocialFi 项目,部署在 Avalanche 公链上。在 Stars Arena 上,用户通过自己的加密钱包登录后,就可以直接用 AVAX 买入感兴趣的内容创作者账号的份额,这里的份额可以简单理解为内容创作者发行的入场券或股票,持有内容创作者的的份额后就可以加入内容创作者建立的“专属群组”。用户也可以创建自己的份额让其他人购买。
此类 SocialFi 能够大火的其中一个原因就是份额是支持交易的,且份额的价值可能会上涨。换句话说,用户有机会通过买卖份额赚取利润。此外,Stars Arena 项目组得到了 Avax 官方和创始人的大力支持,Avax 的官方账号和 Avax 的创始人 Emin Gün Sirer 均已入驻 StarsArena,项目的 TVL 在攻击发生前接近 300 万美元。
Stars Arena 项目的分润机制
在 Stars Arena 中,单位份额的初始价格被设定为 0.0066AVAX,份额的买卖交易都会收取 10%的手续费,其中,7%将转给内容创作者(Subject Fee),2%转给平台(Protocol Fee),1%会转给推荐人作为佣金(Referral Fee)。但我们在反编译分析项目合约时发现,邀请人地址在买卖过程中也可以不设置,此时 1%的邀请佣金也会被转给平台。
攻击中涉及的关键地址
攻击者地址:
0xa2ebf3fcd757e9be1e58b643b6b5077d11b4ad7a
攻击合约:
0xdd9afc0e3c43951659c8ebe7aef9ee40879863ea
Stars Arena 代理合约:
0xa481b139a1a654ca19d2074f174f17d7534e8cec
Stars Arena 实现合约(漏洞合约):
0x8af92c23a169b58c2e5ac656d8d8a23fc725080f
攻击流程分析
通过对 Stars Arena 合约的反编译和对攻击交易( https://snowtrace.io/tx/0x4f37ffecdad598f53b8d5a2d9df98e3c00fbda4328585eb9947a412b5fe17ac5)进行分析,我们梳理出了本次攻击事件的流程和原理,攻击流程按照时间线可划分为攻击准备、重入和获利三个主要阶段。详细攻击流程分析如下:
攻击准备
- 攻击者部署攻击合约 0xdd9afc
- 攻击者购买份额
攻击者通过攻击合约调用 Stars Arena 漏洞合约中签名为 0xe9ccf3a3
的函数购买份额,通过反编译,我们发现该函数的作用与 Stars Arena 中的 buyShares 类似,都是用于为指定地址购买份额,不同之处在于,0xe9ccf3a3 可以设置推荐人地址。0xe9ccf3a3
函数的三个参数依次代表创作者、购买的份额、推荐人。攻击者实际传入的三个参数依次为攻击合约地址 0xdd9afc、购买的份额、攻击合约地址 0xdd9afc。流程分析如下:
0xe9ccf3a3
函数中,攻击者传入的 varg2 是攻击合约地址,非零,所以进入函数0x326c
设置推荐人(图 1 第 4 行),将攻击合约地址以及调用者地址(攻击合约地址)传入到函数0x326c
中。
- 在函数
0x326c
中(图 2),varg0
对应推荐人地址,varg1
对应调用者(被推荐人地址)。在图 2 中,状态变量owner_a2
用于记录被推荐人和推荐人的对应关系。首先在第 2-3 行通过owner_a2
变量获取到攻击合约地址还没有对应的推荐者,则在第 4 行判断推荐者和被推荐者是否是同一个地址。因为此处推荐者和被推荐者都是攻击合约地址,所以v0
被置为 0。因此第 6 行的判断通过,在第 7 行直接 return,并未执行到第 10 行将推荐人写入状态变量owner_a2
的代码。总而言之,推荐人设置失败。
- 0x326c 函数执行结束,回到 0xe9ccf3a3 函数第 6 行,调用 0x2058 函数执行具体的份额购买逻辑,传入参数依次为购买的份额(1 AVAX)、创作者地址(攻击合约地址)。
- 在图 3 私有函数 0x2058 中,
- 第 2 行,调用
0x1a9b
函数计算份额价值 v0。 - 第 3、4、5 行分别计算购买者需要支付的
protocolFee
(转给平台的费用)、subjectFee
(转给创作者的费用)、referralFee
(转给推荐人的费用)。 - 第 6 行将
protocolFee
(0.00012 AVAX)转给平台地址。 - 第 7 行将
subjectFee
(0.00042 AVAX)转给创作者(攻击合约),触发攻击合约的 fallback 函数,在 fallback 函数中,攻击者重入到 Stars Arena 漏洞合约的0x5632b2e4
函数中(没有重入锁)修改权重系数(后续详述)。 - 第 14 行进行的是找零操作,在扣除购买份额的费用、
subjectFee
、protocolFee
以及referralFee
之后,多余的 AVAX 将被返还给交易的发送者。 - 第 17 行,调用图 4 中的 0x2f7b 函数,依次传入参数 referralFee、调用者地址(攻击合约地址)。在图 4 中,第 2 行从状态变量
owner_a2
中获取调用者的推荐人,如前所述,推荐人设置失败,所以此处v0
是 0,因此进入第 5 行,将referralFee
(0.00006 AVAX)转给了平台。 - 图 3 第 21 行进行记账操作,代表已经将这部分份额发行给了攻击者地址。
- 第 2 行,调用
重入,修改权重系数
在图 3 的 0x2058
函数第 7 行,将 subjectFee
(0.00042 AVAX)转给创作者(攻击合约)的过程中,触发了攻击合约的 fallback 函数,由于 Stars Arena 漏洞合约的 0x5632b2e4
函数没有重入锁保护,所以攻击者在 fallback 函数中,重入到了 0x5632b2e4
函数中修改权重系数,使得在后续卖出份额时能获取大量 AVAX。攻击者调用 0x5632b2e4
函数传入的四个参数都为 91000000000。详细流程分析如下:
- 图 5 第 3 行中,函数
0x5632b2e4
首先检查份额是否已经被发行,但由于攻击者是重入到该函数中来的,图 3 第 21 行用于记账的代码还未被执行,因此攻击者购买的份额仍然被认为是“未发行”状态,导致攻击者能够通过该检查。 - 第 4 行-第 6 行的代码分别检查了传入的权重系数是否合法。
- 第 7 行-第 9 行的代码将三个权重系数设置为了攻击者传入的参数。
卖出份额,获取大额收益
在重入攻击结束后,权重系数已被攻击者修改为 91000000000。此时,攻击者在攻击合约中调用 Stars Arena 漏洞合约的 sellShares
函数,出售份额,获取到 266103.97278 个 AVAX。攻击合约调用 sellShares
函数传入的参数依次为攻击合约地址、份额数量(1 AVAX)。详细分析流程如下:
- 图 6 第 3 行,调用
0x1a9b
函数计算份额价值v1
。该价值与攻击者设置的权重系数具有近似线性关系。因此计算出的价值v1
非常大,超过了 26 万 AVAX。 - 第 4、5、6 行分别计算卖出份额需要支付的
protocolFee
(转给平台的费用)、subjectFee
(转给创作者的费用)、referralFee
(转给推荐人的费用)。 - 第 8-10 行计算了扣除支付手续费后,应转给卖出者的 AVAX 数量。
- 第 11 行通过调用内部函数
0x30ef
转账 246899.6712 AVAX(第 10 行计算得出)给卖出者(即攻击合约)。 - 第 12 行,将
protocolFee
(5486.65936 AVAX)转给平台地址。 - 第 13 行,将
subjectFee
(19203.30776 AVAX)转给创作者(攻击合约)。 - 第 15 行,调用图 4 中的
0x2f7b
函数,依次传入参数referralFee
、调用者地址(攻击合约地址)。在图 4 中,第 2 行从状态变量owner_a2
中获取调用者的推荐人,如前所述,推荐人设置失败,所以此处v0
是 0,因此进入第 5 行,将referralFee
(2743.32968 AVAX)转给了平台。
被盗资金追踪
攻击者钱包地址:
0xa2ebf3fcd757e9be1e58b643b6b5077d11b4ad7a
攻击者洗钱交易:
https://snowtrace.io/tx/0x8f5b2e8869260d6854ce4c93f58dfcbf6e8fb18b96c3e76db1eeb6dce0ef9fb1
攻击者创建合约 0xef606558483954e5ab40429210da3429d3d12978,通过该合约进行混币操作,将盗取的 266000 个 AVAX 平均分给了 266 个地址,每个地址 1000AVAX。目前这 1000AVAX 还停留在这 266 个地址中,暂无其他动作。
总结和建议
- 在合约开发过程中,正确使用重入锁,并遵守“Transfer after Effect”的模式,保证合约先记账,再转账,从而避免遭受重入攻击。
- 项目上线前强烈建议聘请专业的智能合约安全专家进行安全审计。
- 我们建议项目方公开源码,方便用户了解合约安全性。此外,加入 bug bounty 计划也有助于激励安全社区,帮助解决合约风险。事实证明,闭源合约并不能提高项目安全性,反而是件坏事。