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
fomo 공동창업자, 솔라나 생태계 토큰 발행 플랫폼 출시 부인…"현재 시장에 토큰이 이미 너무 많아"
PANews 속보