背景介绍
2026 年 7 月 19 日,我们监控到 BNB Smart Chain 上的一起攻击事件, 被攻击的项目为 RWT 。
攻击交易为:
本次攻击共造成约 118,000 USD 的损失。
攻击者地址:
攻击合约地址:
被攻击的 RWT/USDT Pair 地址:
攻击及事件分析
首先,攻击者利用flashloan从Lista DAO: Moolah中贷款1,000,000 USDT。
随后,攻击者调用 RWT 项目交易合约0x8812bB5fB89D69D35Ac84D2C37B55769395b9f90的buy函数,使用93,612.061013797236332471 USDT买入RWT。
其中, 14,041.809152069585449870 USDT 被作为手续费转出,实际进入 RWT/USDT Pair 的数量为:
- 14,041.809152069585449870
= 79,570.251861727650882601 USDT
攻击者最终获得:
接着,攻击者继续向RWT/USDT Pair转入238,910.179775312595883090 USDT,并从 Pair 中转出约36,718,733 RWT。
通过上述操作,Pair 中的USDT数量增加,RWT数量减少,导致 RWT 在 PancakeSwap V2 中的价格被拉高。
随后,攻击者通过 RWT 项目交易合约的 sell 函数卖出:
并从 Pair 中获得:
正常情况下,攻击者完成买入和卖出后,需要承担交易手续费和 AMM 滑点,很难直接获利。
但我们继续查看交易日志可以发现,在攻击者卖出 RWT 后,RWT 合约又从 RWT/USDT Pair 中额外销毁了:
我们将攻击者卖出的 RWT 数量和从 Pair 中销毁的 RWT 数量进行比较:
= 71,968,716.701262285946767816
可以看到,从 Pair 中销毁的 RWT 数量正好是攻击者卖出数量的2倍。
这说明 RWT 项目交易合约在执行sell后,还会根据用户卖出的 RWT 数量,从 PancakeSwap Pair 中额外销毁 RWT。
我们看一下 RWT 代币合约的 burn 函数:
_burn(_From, _amount);
可以看到,burn函数允许具有权限的地址指定任意_From地址,并从该地址中销毁 RWT。
这意味着具有权限的地址可以直接执行:
从而销毁 PancakeSwap Pair 中的 RWT。
我们再看一下 RWT 合约中的 onlyOwner 权限判断:
modifier onlyOwner() {
require(
hasRole(_msgSender()),
"Ownable: caller is not the owner"
);
_;
这里的 onlyOwner 并不是只允许 owner() 地址调用,而是检查
因此,任何被项目方设置为 role = true 的地址,都可以调用 RWT 的 burn 、mint 和 setRole 等高权限函数。
RWT 项目方将交易合约0x8812bB5fB89D69D35Ac84D2C37B55769395b9f90设置为了有权限的地址,而该交易合约的buy和sell函数又可以被普通用户调用。
因此,攻击者虽然不能直接调用 RWT 的burn函数,但可以通过交易合约的sell函数,间接触发交易合约使用其权限销毁 Pair 中的 RWT。
其核心调用关系如下:
└── 调用公开的 sell 函数
└── RWT 特权交易合约
└── RWT.burn(pair, amount)
RWT 交易合约 0x8812...9f90 没有公开完整源码。通过对合约运行时字节码、函数选择器、交易日志和事件参数进行分析,可以还原出其关键逻辑大致如下:
// 从用户处收取并卖出 RWT
_sellRWT(amount, minOut);
// 从 PancakeSwap Pair 中销毁 RWT
uint256 burnAmount = amount * 2;
RWT.burn(pancakePair, burnAmount);
// 将销毁后的余额写入 Pair 储备
IPancakePair(pancakePair).sync();
当交易合约从 Pair 中销毁 RWT 后,Pair 的实际 RWT 余额减少,但 USDT 余额没有按相同比例减少。
随后合约调用 sync() ,PancakeSwap V2 Pair 会将当前余额重新写入储备:
reserveUSDT= USDT.balanceOf(pair);
PancakeSwap V2 中的 RWT 价格可以简单表示为:
因此,当 RWT 储备被大量销毁,而 USDT 储备基本不变时,RWT 的池内价格会被快速拉高。
↓
USDT Reserve 基本不变
↓
RWT Price 上升
攻击者利用这一逻辑,多次调用交易合约进行卖出。
每次卖出后,交易合约都会从 Pair 中销毁约为卖出数量2倍的 RWT,然后调用sync()更新储备。
攻击交易中的部分数据如下:
可以看到,每轮从 Pair 中销毁的 RWT 数量都是攻击者卖出数量的约2倍。
随着 Pair 中的 RWT 储备越来越少,RWT 的价格越来越高。攻击者后续只需要卖出少量 RWT,就可以继续从 Pair 中兑换出 USDT。
攻击过程如下:
↓
卖出部分 RWT
↓
交易合约从 Pair 中销毁 RWT
↓
调用 sync() 更新储备
↓
RWT 价格被拉高
↓
再次卖出 RWT 获取 USDT
攻击发生后约6分钟,RWT 项目方连续发送两笔setRole交易,将交易合约0x8812...9f90的权限设置为false。
相关交易为:
交易参数均为:
0x8812bb5fb89d69d35ac84d2c37b55769395b9f90,
false
);
这一操作可以进一步证明,攻击过程中调用burn的权限来自 RWT 项目的交易合约,而不是攻击者直接获取了 RWT Owner 权限。
最后,攻击者向 Moolah 归还 1,000,000 USDT 的 flashloan ,并将剩余的:
转入攻击者地址。
攻击者最终获利约 118,000 USDT 。
漏洞代码分析
本次攻击主要涉及两个合约层面的安全问题。
第一个问题是 RWT 的 burn 函数可以销毁任意地址中的 RWT:
免责声明:本文仅用于信息交流,不构成投资建议。数字资产具有高风险,请自行判断。
