前言
今天分享了一个关于 MEV BOT
因为 open uniswapV2Call
而被反撸的案例,这个风险之前就已经看到有人分享了,但是由于一直没有实际的案例,所以没办法总结成分享,这次正好借着blockSec
分享的案例来详细分享一下关于 open uniswapV2Call
的风险。
什么是 uniswapV2Call
在 Uniswap V2
版本的 pool
合约中,存在一个功能称为 flash swap
,即允许用户在 swap
的过程中产生外部调用,然后实现一些用户想自定义的功能,比方说你想在 uniswapV2
兑换的同时在 sushiswap
也进行一次兑换,那么这个时候就可以利用上这个 flash swap
的功能,在 swap
的中途穿插进 sushiswap
的调用。这里面说到的外部调用,其实就是通过 uniswapV2Call
来实现的,具体的代码如下:
不难发现,#172 行在 swap
函数中开了一个口子,当调用 swap
函数的时候指定的 data
参数长度不为 0 的时候,就会通过 uniswapV2Call
函数来对指定的 to
地址产生外部调用,同时我们可以注意到,uniswapV2Call
函数带上的参数分别为 msg.sender
, amount0Out
, amount1Out
和 data
。这些参数都是为了方便外部合约在接受函数调用的时候可以做一些检查。好了,那么到这里,我们就已经明白这个函数究竟是做什么的了。那么下面我们就容易分析它的风险了。
open uniswapV2Call 的风险
open uniswapV2Call
,其实可以统一定义为那些在接受 uniswapV2Call
调用的外部合约不检查 uniswapV2Call
函数里的 sender
的情况,这个 sender
又是什么呢?其实就是 uniswapV2Call
函数的第一个参数,在通过 uniswapV2Call
调用的时候,它总是被指定成当前调用 swap
函数的地址,也就是 msg.sender
,而在其他情况下,如未鉴权的 uniswapV2Call
函数,则这个 sender
可以是任意值。
那这个函数具体的风险是什么呢?从定义来看,open uniswapV2Call
其实就是因未检查 sender
函数而产生的可被人意调用的风险,针对这种风险通常有两种攻击场景 – 外部合约在定义 uniswapV2Call
函数的时候,完全没有任何的鉴权,允许任意调用 – 外部合约在定义 uniswapV2Call
函数的时候,只对来源地址进行了鉴权,要求必须是来自 pool
合约的调用
上面这两种攻击场景里,第一种就完全不用说了,明显的有问题,那第二种,已经鉴权了,还会有问题吗?针对这个问题,我们这次就以一个 MEV BOT
被反撸的案例来说说第二种情况的攻击方法
如上图所示,整个反撸其实分成3个阶段,分别是 – 闪电贷 – 使用闪电贷资金从 SLP
池中兑换将 WETH
兑换成 PENDLE
– 调用 sushiSwap
的 swap
函数,把 to
制定成要攻击的 MEV BOT
合约,然后通过 sushiSwap
池来调用受害 MEV BOT
的 uniswapV2Call
函数。
那么,基于以上几个流程,攻击者是如何获利的呢?答案是这个 MEV BOT
实际上在做一种 PENDLE
代币到 WETH
代币的操作,这个操作位于 MEV BOT
的 uniswapV2Call
函数内,而触发条件为合约内拥有一定量的 YT-SLP-29DEC2022
代币,为了满足这个条件,我们可以看到攻击者尝试给 MEV BOT
发送了少量的 YT-SLP-29DEC2022
代币才开始调用对应的 uniswapV2Call
操作。
结合这个 MEV BOT
的具体行为和攻击者的第二步,不难发现其实攻击者是为了通过闪电贷现在 SLP
池中先创造一个大滑点,然后调用 MEV BOT
来继续兑换,此时,由于 MEV BOT
的接盘,导致对攻击者形成了利差,此时攻击者使用第二步获得的 PENDLE
代币进行反向兑换即可完成对 MEV BOT
的反向套利。
整个流程就是如此,我们接下来看看 MEV BOT
合约的 uniswapV2Call
权限检查,通过反编译,不难发现 MEV BOT
合约本身只是检查了调用 uniswapV2Call
的来源地址是不是 pair
合约本身,但这是不够的,结合上文,由于 uniswapV2Call
调用是根据 to
地址发起的,所以其实任意人都可以通过调用pair
中的 swap
函数同时将 to
指定成 MEV BOT
的合约来触发对 MEV BOT
合约的 uniswapV2Call
调用,这种情况下,调用的源地址,一定会是 pair
合约本身,但是整个流程,却不一定是你可控的。所以正确的方法是对 uniswapV2Call
的 sender
参数进行检查,判断调用是不是来自自己指定的或信任的地址。才能保证万无一失。本次的问题,合约的开发者很明显的意识到了权限的问题,但是可惜的是想的不全面,导致此次被反撸 8 ETH。
其他资料
-
BlockSec 提供交易:https://ethtx.info/mainnet/0x6c30c0b8afe0596516c32ad6aa17566bfe29677ca4fe35a4a395210d28329d1f
-
Flash swap介绍: https://docs.uniswap.org/protocol/V2/guides/smart-contract-integration/using-flash-swaps
原文始发于微信公众号(蛋蛋的区块链笔记):意识到位,可惜操作没跟上 — 详解 uniswapV2Call 风险