RWT 项目遭闪电贷攻击,损失 11.8 万美元:漏洞复盘与深度分析

현재 언어 번역이 없어 원문을 표시합니다.
7 月 19 日 BSC 链 RWT 项目因合约权限设计缺陷遭攻击,攻击者借闪电贷操纵池内资产价格。项目交易合约持有销毁池内代币权限,卖出后双倍销毁流动池资产抬高报价,攻击者反复操作套利。事后项目紧急收回合约权限,攻击者还清借贷后获利约 11.8 万等值资产。

背景介绍

2026 年 7 月 19 日,我们监控到 BNB Smart Chain 上的一起攻击事件,被攻击的项目为 RWT

攻击交易为:

https://bscscan.com/tx/0x22300140e7c44899c2602382a6e7a4a34a70f47f9736721744bc6434c07171dc

本次攻击共造成约 118,000 USD 的损失。

攻击者地址:

https://bscscan.com/address/0x84dd3a5d4de44c8ad0ce032beab8bc3f01d1dcf7

攻击合约地址:

https://bscscan.com/address/0x7ed953ff42509568f620aa340a33a9373447f4ce

被攻击的 RWT/USDT Pair 地址:

https://bscscan.com/address/0xc1c2ef25372f12ce18d35044446064b720c4aa27

攻击及事件分析

首先,攻击者利用flashloan从Lista DAO: Moolah中贷款1,000,000 USDT。

随后,攻击者调用 RWT 项目交易合约0x8812bB5fB89D69D35Ac84D2C37B55769395b9f90的buy函数,使用93,612.061013797236332471 USDT买入RWT。

其中, 14,041.809152069585449870 USDT 被作为手续费转出,实际进入 RWT/USDT Pair 的数量为:

93,612.061013797236332471
 

- 14,041.809152069585449870

= 79,570.251861727650882601 USDT

攻击者最终获得:

  •  
36,718,733.010848105074881558RWT

接着,攻击者继续向RWT/USDT Pair转入238,910.179775312595883090 USDT,并从 Pair 中转出约36,718,733 RWT。

通过上述操作,Pair 中的USDT数量增加,RWT数量减少,导致 RWT 在 PancakeSwap V2 中的价格被拉高。

随后,攻击者通过 RWT 项目交易合约的 sell 函数卖出:

35,984,358.350631142973383908 RWT

并从 Pair 中获得:

235,902.720974496353639994USDT

正常情况下,攻击者完成买入和卖出后,需要承担交易手续费和 AMM 滑点,很难直接获利。

但我们继续查看交易日志可以发现,在攻击者卖出 RWT 后,RWT 合约又从 RWT/USDT Pair 中额外销毁了:

71,968,716.701262285946767816 RWT

我们将攻击者卖出的 RWT 数量和从 Pair 中销毁的 RWT 数量进行比较:

35,984,358.350631142973383908 × 2
 

= 71,968,716.701262285946767816

可以看到,从 Pair 中销毁的 RWT 数量正好是攻击者卖出数量的2倍。

这说明 RWT 项目交易合约在执行sell后,还会根据用户卖出的 RWT 数量,从 PancakeSwap Pair 中额外销毁 RWT。

我们看一下 RWT 代币合约的 burn 函数:

function burn(address _From, uint256 _amount) external onlyOwner {

_burn(_From, _amount);

}

可以看到,burn函数允许具有权限的地址指定任意_From地址,并从该地址中销毁 RWT。

这意味着具有权限的地址可以直接执行:

RWT.burn(pancakeSwapPair, amount);

从而销毁 PancakeSwap Pair 中的 RWT。

我们再看一下 RWT 合约中的 onlyOwner 权限判断:

mapping(address => bool) internal role;
 

modifier onlyOwner() {

 

require(

 

hasRole(_msgSender()),

 

"Ownable: caller is not the owner"

 

);

 

_;

 

}

这里的 onlyOwner 并不是只允许 owner() 地址调用,而是检查

role[msg.sender] ==true

因此,任何被项目方设置为 role = true 的地址,都可以调用 RWT 的 burn 、mint 和 setRole 等高权限函数。

RWT 项目方将交易合约0x8812bB5fB89D69D35Ac84D2C37B55769395b9f90设置为了有权限的地址,而该交易合约的buy和sell函数又可以被普通用户调用。

因此,攻击者虽然不能直接调用 RWT 的burn函数,但可以通过交易合约的sell函数,间接触发交易合约使用其权限销毁 Pair 中的 RWT。

其核心调用关系如下:

攻击者
 

 

└── 调用公开的 sell 函数

 

 

└── RWT 特权交易合约

 

 

└── RWT.burn(pair, amount)

RWT 交易合约 0x8812...9f90 没有公开完整源码。通过对合约运行时字节码、函数选择器、交易日志和事件参数进行分析,可以还原出其关键逻辑大致如下:

functionsell(uint256 amount, uint256 minOut) external { 
 

// 从用户处收取并卖出 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 会将当前余额重新写入储备:

reserveRWT= RWT.balanceOf(pair);
 

reserveUSDT= USDT.balanceOf(pair);

PancakeSwap V2 中的 RWT 价格可以简单表示为:

RWTPrice= USDT Reserve / RWT Reserve

因此,当 RWT 储备被大量销毁,而 USDT 储备基本不变时,RWT 的池内价格会被快速拉高。

RWT Reserve 减少

USDT Reserve 基本不变

RWT Price 上升

攻击者利用这一逻辑,多次调用交易合约进行卖出。

每次卖出后,交易合约都会从 Pair 中销毁约为卖出数量2倍的 RWT,然后调用sync()更新储备。

攻击交易中的部分数据如下:

可以看到,每轮从 Pair 中销毁的 RWT 数量都是攻击者卖出数量的约2倍。

随着 Pair 中的 RWT 储备越来越少,RWT 的价格越来越高。攻击者后续只需要卖出少量 RWT,就可以继续从 Pair 中兑换出 USDT。

攻击过程如下:

使用 USDT 买入 RWT
 

 

卖出部分 RWT

 

 

交易合约从 Pair 中销毁 RWT

 

 

调用 sync() 更新储备

 

 

RWT 价格被拉高

 

 

再次卖出 RWT 获取 USDT

攻击发生后约6分钟,RWT 项目方连续发送两笔setRole交易,将交易合约0x8812...9f90的权限设置为false。

相关交易为:

https://bscscan.com/tx/0xa7220206910794024452293b80cbbb77389f847c12c53b81528e628b4c3d9d73

https://bscscan.com/tx/0x18f2b31770c152242d85cfbe711f28d5ae985a0f4950a17a7e9558712a9d082d

交易参数均为:

setRole(

0x8812bb5fb89d69d35ac84d2c37b55769395b9f90,

false

);

这一操作可以进一步证明,攻击过程中调用burn的权限来自 RWT 项目的交易合约,而不是攻击者直接获取了 RWT Owner 权限。

最后,攻击者向 Moolah 归还 1,000,000 USDT 的 flashloan ,并将剩余的:

118,069.281571404198479027 USDT

转入攻击者地址。

攻击者最终获利约 118,000 USDT 。

漏洞代码分析

本次攻击主要涉及两个合约层面的安全问题。

第一个问题是 RWT 的 burn 函数可以销毁任意地址中的 RWT:

function burn(address _From, uint256 _amount) external onlyOwner {

_burn(_From, _amount);

}

这里没有限制_From不能为 PancakeSwap Pair,因此有权限的合约可以直接修改 Pair 中的 RWT 余额。

第二个问题是项目方将该权限授予了公开交易合约,同时交易合约在sell流程中根据用户输入计算 Pair 销毁量。

因此,攻击者可以控制:

卖出 RWT 的数量

从 Pair 中销毁 RWT 的数量

Pair 更新后的 RWT 储备

RWT 的池内价格

虽然 RWT 的burn函数存在onlyOwner修饰器,但高权限交易合约的公开sell函数没有对调用者进行限制,使攻击者可以间接使用该合约的burn权限。

所以,本次漏洞不是简单的“ burn 函数缺少权限控制”,而是:

项目方将高权限的任意地址销毁能力授予公开业务合约,业务合约又允许外部用户通过可控参数销毁 AMM Pair 中的资产,并调用 sync() 操纵储备。

总结

本次漏洞的成因是 RWT 项目方设计了一个可以从 PancakeSwap Pair 中直接销毁 RWT 的交易机制,并将 RWT 的 burn 权限授予了公开交易合约。

攻击者先利用 flashloan 借入 1,000,000 USDT ,买入大量 RWT 并改变 RWT/USDT Pair 的储备比例。随后,攻击者调用交易合约的 sell 函数卖出 RWT。

交易合约在卖出完成后,从 Pair 中额外销毁约为卖出数量 2 倍的 RWT,并调用 sync() 更新 Pair 储备,导致 RWT 的池内价格不断升高。

攻击者重复执行该操作,逐步减少 Pair 中的 RWT 储备并从池中提取 USDT。最后归还 flashloan 后,获利约 118,000 USDT 。

建议项目方不要允许任何合约从 AMM Pair 地址中执行 burn 或强制转账操作。代币销毁应该只针对协议自身实际持有的代币,不能直接修改 LP 地址中的资产。

同时,项目方应将 mint 、 burn 、黑名单管理、交易开关和角色管理等权限进行拆分,避免单个外部业务合约同时拥有多项管理员权限。

在设计代币销毁、自动加池、自动回购和价格计算逻辑时,也需要重点检查burn(pair)、transferFrom(pair)、skim()和sync()等操作的组合风险。合约上线前应通过多个审计团队进行交叉审计,并在主网分叉环境中进行闪电贷和经济模型攻击测试。

공유하기:

작성자: 零时科技

이 글은 PANews 입주 칼럼니스트의 관점으로, PANews의 입장을 대표하지 않으며 법적 책임을 지지 않습니다.

글 및 관점은 투자 조언을 구성하지 않습니다

이미지 출처: 零时科技. 권리 침해가 있을 경우 저자에게 삭제를 요청해 주세요.

PANews 공식 계정을 팔로우하고 함께 상승장과 하락장을 헤쳐나가세요
관련 특집
PANews APP
Circle 新增铸造 2.5 亿枚 USDC 稳定币
PANews 속보