Stars Arena 攻击事件分析

Posted by AntChain Open Labs on 2023-10-09

2023 年 10 月 7 日,friend.tech 仿盘项目 Stars Arena 遭到攻击。他们在 twitter 中表明合约中的资金已被攻击者抽干(https://twitter.com/starsarenacom/status/1710604987812708531),损失约 290 万美元。

image

几小时之后,Stars Arena 发推表示,他们已获得资金来弥补漏洞造成的损失,并邀请白帽开发团队快速审查平台的安全性。新的合约将在全面的安全审计后重新开启(https://twitter.com/starsarenacom/status/1710666706262138953)。

image

该攻击的本质在于项目方合约中存在重入漏洞,导致攻击者可以从该项目的合约中窃取大量 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

攻击流程分析

image

Attack Process

https://explorer.phalcon.xyz/tx/avax/0x4f37ffecdad598f53b8d5a2d9df98e3c00fbda4328585eb9947a412b5fe17ac5

通过对 Stars Arena 合约的反编译和对攻击交易( https://snowtrace.io/tx/0x4f37ffecdad598f53b8d5a2d9df98e3c00fbda4328585eb9947a412b5fe17ac5)进行分析,我们梳理出了本次攻击事件的流程和原理,攻击流程按照时间线可划分为攻击准备、重入和获利三个主要阶段。详细攻击流程分析如下:

攻击准备

  1. 攻击者部署攻击合约 0xdd9afc
  2. 攻击者购买份额

攻击者通过攻击合约调用 Stars Arena 漏洞合约中签名为 0xe9ccf3a3 的函数购买份额,通过反编译,我们发现该函数的作用与 Stars Arena 中的 buyShares 类似,都是用于为指定地址购买份额,不同之处在于,0xe9ccf3a3 可以设置推荐人地址。0xe9ccf3a3 函数的三个参数依次代表创作者、购买的份额、推荐人。攻击者实际传入的三个参数依次为攻击合约地址 0xdd9afc、购买的份额、攻击合约地址 0xdd9afc。流程分析如下:

  • 0xe9ccf3a3 函数中,攻击者传入的 varg2 是攻击合约地址,非零,所以进入函数 0x326c 设置推荐人(图 1 第 4 行),将攻击合约地址以及调用者地址(攻击合约地址)传入到函数 0x326c 中。

image

图 1 函数 0xe9ccf3a3

  • 在函数 0x326c 中(图 2),varg0 对应推荐人地址,varg1 对应调用者(被推荐人地址)。在图 2 中,状态变量 owner_a2 用于记录被推荐人和推荐人的对应关系。首先在第 2-3 行通过 owner_a2 变量获取到攻击合约地址还没有对应的推荐者,则在第 4 行判断推荐者和被推荐者是否是同一个地址。因为此处推荐者和被推荐者都是攻击合约地址,所以 v0 被置为 0。因此第 6 行的判断通过,在第 7 行直接 return,并未执行到第 10 行将推荐人写入状态变量 owner_a2 的代码。总而言之,推荐人设置失败。

image

图 2 函数 0x326c

  • 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 行进行的是找零操作,在扣除购买份额的费用、subjectFeeprotocolFee 以及 referralFee 之后,多余的 AVAX 将被返还给交易的发送者。
    • 第 17 行,调用图 4 中的 0x2f7b 函数,依次传入参数 referralFee、调用者地址(攻击合约地址)。在图 4 中,第 2 行从状态变量 owner_a2 中获取调用者的推荐人,如前所述,推荐人设置失败,所以此处 v0 是 0,因此进入第 5 行,将 referralFee(0.00006 AVAX)转给了平台。
    • 图 3 第 21 行进行记账操作,代表已经将这部分份额发行给了攻击者地址。

image

图 3 函数 0x2058

image

图 4 函数 0x2f7b

重入,修改权重系数

在图 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 行的代码将三个权重系数设置为了攻击者传入的参数。

image

图 5 函数 0x5632b2e4

卖出份额,获取大额收益

在重入攻击结束后,权重系数已被攻击者修改为 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)转给了平台。

image

图 6 函数 sellShares

被盗资金追踪

攻击者钱包地址:

0xa2ebf3fcd757e9be1e58b643b6b5077d11b4ad7a

攻击者洗钱交易:

https://snowtrace.io/tx/0x8f5b2e8869260d6854ce4c93f58dfcbf6e8fb18b96c3e76db1eeb6dce0ef9fb1

攻击者创建合约 0xef606558483954e5ab40429210da3429d3d12978,通过该合约进行混币操作,将盗取的 266000 个 AVAX 平均分给了 266 个地址,每个地址 1000AVAX。目前这 1000AVAX 还停留在这 266 个地址中,暂无其他动作。

image

总结和建议

  1. 在合约开发过程中,正确使用重入锁,并遵守“Transfer after Effect”的模式,保证合约先记账,再转账,从而避免遭受重入攻击。
  2. 项目上线前强烈建议聘请专业的智能合约安全专家进行安全审计。
  3. 我们建议项目方公开源码,方便用户了解合约安全性。此外,加入 bug bounty 计划也有助于激励安全社区,帮助解决合约风险。事实证明,闭源合约并不能提高项目安全性,反而是件坏事。