Biswap交易所攻击事件分析

admin 2023年7月10日15:15:47评论31 views字数 5910阅读19分42秒阅读模式
01
事件背景

2023年7月1日,Biswap社区发布预警,疑似发现可疑活动,为了减少损失,社区建议用户自查,看是否有三个地址的授权:
Biswap交易所攻击事件分析
其中0xa7a98876C1dC2BFFC4B2c8CCDeBB847Ff808662b为攻击者地址。
该地址首先创建了攻击合约0x76A40Bd16b6d2b9BfBfe112199Bae836c16DFC84,接着多次调用攻击合约。
Biswap交易所攻击事件分析

02
交易分析

以第一次交易0xe013cf0061db30884da943c4cc72cb0555901fe844cdae41a1c2cd4fcd444b42为例,对攻击流程进行分析。
攻击合约:0x76a40bd16b6d2b9bfbfe112199bae836c16dfc84。
调用攻击合约的参数:
Biswap交易所攻击事件分析
输入的两个地址为受害者地址和受害者持有的LP的token合约。
首先攻击合约调用LP token的balanceOf,获取受害者外部地址0x2978d920a1655abaa315bad5baf48a2d89792618在(BSW-WBNB)pair合约0x46492b26639df0cda9b2769429845cb991591e0a中的LP余额:
Biswap交易所攻击事件分析
调用allowance获取受害者EOA对Migrate合约0x839b0afd0a0528ea184448e890cbaaffd99c1dbf的LP授权数量:
Biswap交易所攻击事件分析
攻击合约调用V3Migrator合约0x839b0afd0a0528ea184448e890cbaaffd99c1dbf的migrate函数:
/// @notice This function burn V2 liquidity, and mint V3 liquidity with received tokens
/// @param params see IV3Migrator.MigrateParams
/// @return refund0 amount of token0 that burned from V2 but not used to mint V3 liquidity
/// @return refund1 amount of token1 that burned from V2 but not used to mint V3 liquidity
function migrate(MigrateParams calldata params) external override returns(uint refund0, uint refund1){

// burn v2 liquidity to this address
IBiswapPair(params.pair).transferFrom(params.recipient, params.pair, params.liquidityToMigrate);
(uint256 amount0V2, uint256 amount1V2) = IBiswapPair(params.pair).burn(address(this));

// calculate the amounts to migrate to v3
uint128 amount0V2ToMigrate = uint128(amount0V2);
uint128 amount1V2ToMigrate = uint128(amount1V2);

// approve the position manager up to the maximum token amounts
safeApprove(params.token0, liquidityManager, amount0V2ToMigrate);
safeApprove(params.token1, liquidityManager, amount1V2ToMigrate);

// mint v3 position
(, , uint256 amount0V3, uint256 amount1V3) = ILiquidityManager(liquidityManager).mint(
ILiquidityManager.MintParam({
miner: params.recipient,
tokenX: params.token0,
tokenY: params.token1,
fee: params.fee,
pl: params.tickLower,
pr: params.tickUpper,
xLim: amount0V2ToMigrate,
yLim: amount1V2ToMigrate,
amountXMin: params.amount0Min,
amountYMin: params.amount1Min,
deadline: params.deadline
})
);

// if necessary, clear allowance and refund dust
if (amount0V3 < amount0V2) {
if (amount0V3 < amount0V2ToMigrate) {
safeApprove(params.token0, liquidityManager, 0);
}

refund0 = amount0V2 - amount0V3;
if (params.refundAsETH && params.token0 == WETH9) {
IWETH9(WETH9).withdraw(refund0);
safeTransferETH(params.recipient, refund0);
} else {
safeTransfer(params.token0, params.recipient, refund0);
}
}
if (amount1V3 < amount1V2) {
if (amount1V3 < amount1V2ToMigrate) {
safeApprove(params.token1, liquidityManager, 0);
}

refund1 = amount1V2 - amount1V3;
if (params.refundAsETH && params.token1 == WETH9) {
IWETH9(WETH9).withdraw(refund1);
safeTransferETH(params.recipient, refund1);
} else {
safeTransfer(params.token1, params.recipient, refund1);
}
}

emit Migrate(
params,
amount0V2,
amount1V2,
amount0V3,
amount1V3
);
}

可以发现该函数可以被外部任意调用,且params参数也基本没有验证。

migrate函数,在函数中调用transferFrom,其中的pair为真实的pair合约,recipient为受害者的外部地址,数量为之前获取的授权数量。这里将受害者地址的所有lp发送migrate合约。之后再由migrate合约burn掉所有lp,将两种真实代币由pair合约发送本migrate合约:

Biswap交易所攻击事件分析

// burn v2 liquidity to this address
IBiswapPair(params.pair).transferFrom(params.recipient, params.pair, params.liquidityToMigrate);

(uint256 amount0V2, uint256 amount1V2) = IBiswapPair(params.pair).burn(address(this));

pair:0x46492b26639df0cda9b2769429845cb991591e0a (BiswapPair)(BSW)(WBNB)

recipient:0x2978d920a1655abaa315bad5baf48a2d89792618(Victim EOA)

liquidityToMigrate:2167939368021752115474

之后的逻辑是migrate合约把从V2版本pair合约发送来的两种token授权给liquidityManager,但是token0和token1被黑客指定为假的地址,所以两种代币实际上是被锁在了本合约。

// approve the position manager up to the maximum token amounts
safeApprove(params.token0, liquidityManager, amount0V2ToMigrate);
safeApprove(params.token1, liquidityManager, amount1V2ToMigrate);

mint V3版本的LP,mint到受害者地址,但两种代币都是假的,所以受害者获取到的LP也是假的。

// mint v3 position
(, , uint256 amount0V3, uint256 amount1V3) = ILiquidityManager(liquidityManager).mint(
ILiquidityManager.MintParam({
miner: params.recipient,
tokenX: params.token0,
tokenY: params.token1,
fee: params.fee,
pl: params.tickLower,
pr: params.tickUpper,
xLim: amount0V2ToMigrate,
yLim: amount1V2ToMigrate,
amountXMin: params.amount0Min,
amountYMin: params.amount1Min,
deadline: params.deadline
})
);

tokenX:0x6919b2988d68128ed62644d7043c1799dd0f0d78(fake token 1)

tokenY:0xe26ade3a97f068603af72690746bae81b971d4f9(fake token 2)

第二次调用migrate:

这里的pair合约地址是攻击合约。

IBiswapPair(params.pair).transferFrom(params.recipient, params.pair, params.liquidityToMigrate);

(uint256 amount0V2, uint256 amount1V2) = IBiswapPair(params.pair).burn(address(this));

pair:0x76a40bd16b6d2b9bfbfe112199bae836c16dfc84(攻击合约)

Recipient:0xa7a98876c1dc2bffc4b2c8ccdebb847ff808662b(攻击者地址)

这里用攻击合约的假burn函数返回假的两种代币数量:

Biswap交易所攻击事件分析

这次向攻击者地址mint V3版本的LP且两种代币为真实代币。

// mint v3 position
(, , uint256 amount0V3, uint256 amount1V3) = ILiquidityManager(liquidityManager).mint(
ILiquidityManager.MintParam({
miner: params.recipient,
tokenX: params.token0,
tokenY: params.token1,
fee: params.fee,
pl: params.tickLower,
pr: params.tickUpper,
xLim: amount0V2ToMigrate,
yLim: amount1V2ToMigrate,
amountXMin: params.amount0Min,
amountYMin: params.amount1Min,
deadline: params.deadline
})
);

tokenX:0x965f527d9159dce6288a2219db51fc6eef120dd1(BSW)

tokenY:0xbb4cdb9cbd36b01bd1cbaebf2de08d9173bc095c(WBNB)

这次mint结果是向V3的pool合约发送了全部的BSW代币,攻击者获取了相应的V3 LP:

Biswap交易所攻击事件分析

Biswap交易所攻击事件分析

之后将合约中剩余的BSW和WBNB发送给攻击者地址:

Biswap交易所攻击事件分析
之后攻击者可以调用decLiquidity函数,使用LP取回之前铸造进去的剩余代币。

03
课程介绍
  1. 黑客收集将LP token授权给V3Migrator合约的地址。
  2. 黑客创建攻击合约,该攻击合约要能够调用V3Migrator合约的migrate函数,同时也要实现transferFrom和burn接口。
  3. 攻击合约第一次调用migrate函数,输入真实的pair合约和两个假的token合约地址。
  4. 受害者的lp被转到V3Migrator合约并被销毁,销毁的LP变成交易对中的两种代币并发回给V3Migrator合约。
  5. mint V3版本的LP给受害者地址,但输入的两个token是假的。
  6. 攻击合约第二次调用migrate函数,这时pair合约地址设为攻击合约自己,两个token地址设置成真实的token地址。
  7. 向黑客地址mint V3版本的LP,两种token的地址为真实地址,这时黑客获取的LP是真实的。
  8. V3Migrator合约将剩余的真实token发送给黑客地址。
  9. 黑客调用decLiquidity函数销毁LP,获取流动性。
  10. 重复上述过程,遍历获取所有攻击者的流动性。


04
如何避免

转移LP是现在DeFi项目的常见功能,biSwap的migrate函数在被调用时没有做任何权限限制,按照项目方的设想,一旦用户将LPtoken授权给V3Migrator合约,就相当于同意进行转移操作,但在实际的实现上,最好对调用者进行限定。

其次是对输入参数没有做任何检查,transferFrom和burn函数的目标地址可以被任意指定,一方面这两个函数会进行一些余额判断,在条件不满足时会revert,如果使用假的地址,则会绕过本应该在逻辑中的判断。另一方面,由于可以任意指定地址,所以可以在函数中再对目标合约进行重入。这两种类型的漏洞都会对交易的原子性产生破坏。

两种token的真伪也没有验证,假的token可以将本应该转移到V3版本池子的代币锁在本合约中,mint给受害者的LP也是假的。正确做法是先验证pair合约是否合法,并通过这个pair合约获取两种代币地址,之后mint新版本的LP时就使用这个地址。


       

原文始发于微信公众号(山石网科安全技术研究院):Biswap交易所攻击事件分析

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2023年7月10日15:15:47
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   Biswap交易所攻击事件分析https://cn-sec.com/archives/1864941.html

发表评论

匿名网友 填写信息