LOADING...
LOADING...
LOADING...
当前位置: 玩币族首页 > 新闻观点 > [BlockSec DeFi 攻击分析系列之二] 倾囊相送:Sushiswap 手续费被盗

[BlockSec DeFi 攻击分析系列之二] 倾囊相送:Sushiswap 手续费被盗

2021-08-03 BlockSec 来源:区块链网络

去中心化金融 (DeFi) 作为区块链生态当红项目形态,其安全尤为重要。从去年至今,发生了几十起安全事件

BlockSec 作为长期关注 DeFi 安全的研究团队 (https://blocksecteam.com),独立发现了多起 DeFi 安全事件,研究成果发布在顶级安全会议中(包括 USENIX Security, CCS 和 Blackhat)。在接下来的一段时间里,我们将系统性分析 DeFi 安全事件,剖析安全事件背后的根本原因

往期回顾

(1) [BlockSec DeFi攻击分析系列之一] 我为自己代言:ChainSwap攻击事件分析

今天带来第二期:今天给大家带来第二期,本文简述了一个老实的日本寿司店收银员,因为过于相信他人,被骗走攒了很久的血汗钱的真实故事

时间:Nov-28-2020 05:41:10 AM +UTC#11345179


阅读建议:

如果您刚刚接触 DeFi,可以从头开始看起,但是文章较长,看不下去记得点个关注再走如果您对 Sushiswap、AMM、DEX 等比较了解,可以直接从「0x1 攻击分析」开始

0x0. 背景介绍

Uniswap 走过最长的路就是 Sushiswap 的套路(不是

Sushiswap 由 Uniswap 分叉而来 [注 1],自诩为社区版 Uniswap[注 2]

【注 1】\"Taking Uniswap's elegant core design, we've added community-oriented features that we believe help improve the design of the protocol, as well as provide further benefits to the actors involved.\"

【注 2】\"It's not just a fork. LPs are take care of. It's not just a 0.3% fee going to them anymore.\"

为什么叫社区版呢?因为 Sushiswap 在 Uniswap 的基础上添加了 SUSHI 这一平台代币

【注】SUSHI 是 Sushiswap 的平台代币,可以和 USDC、ETH 一样在二级市场交易,同时也具有治理的功能(拥有 SUSHI 可以参与 Sushiswap 一些决策的投票)

原本 Uniswap 的交易手续费是 0.3% ,也就是在 Uniswap 上每次 swap 代币,都需要预先扣除卖出代币的 0.3% 流入交易池,使得流动性提供者所能换取的底层代币(Underlying token)更多

而 Sushiswap 将此交易费划分为两部分:其中 0.25% 和原来一样,作为交易的手续费流入交易池,另外的 0.05% 用来回购 SUSHI (这里注意,一会要考的)

① 在一切开始之前,我们先简单回顾一下 AMM 的原理:

AMM 是实现去中心化交易所 (DEX) 的一种技术

作为交易所,最核心的功能当然就是交换(swap)代币,而代币本质上就是存储在区块链上的数字,不同的代币是不同的变元。只要寻找到一条合适的曲线,可以表示出这种此消彼长的关系(比如 Uniswap 的恒定乘积),就可以通过程序来实现代币的 \" 自动交换 \"

从合约层面来看,我们可以通过 Uniswap 的 Factory 合约创建出不同交易对合约(Pari, 交易池)。一个交易池对应着一对 token,人们可以找到合适的交易池来交换代币(swap),交易池中的底层代币是流动性提供者(Liquidity provider)存入的。作为凭证,交易池会给流动性提供者发送 \" 交易池代币 \"(LP token),LP token 同时也代表占有池中资产的份额

这里要注意的是,每个交易对合约都会继承一个 ERC20 代币合约,所以交易对合约本身就是 LP token,或者我们可以从 OOP 的角度来看,交易池只是一种扩展版的 ERC20,这种 ERC20 代币我们统称为 LP token,它不仅具有普通的 mint, send, burn 功能,还有 : LP.addLiquidity(token0, token1), LP.swap(token0) → token1 这些额外的接口

【注】Sushiswap 的 LP token 一般被称作 SLP

补充:交易池深度与滑点之间的关系?

交易池中两种 token 的价格如何确定?有一种简单的方法,就是看比值。比如一个池中 ETH:USDT = 1:2000,那我们就可以简单的认为一个 ETH 能换出 2000 个 USDT,池中 ETH 更少,更值钱。有个比较专业的词叫做 spot price,2000 USDT/ETH 就是 ETH 在该池中的 spot price

但是真的如此吗?对于 AMM 类型的 DEX,比如 Uniswap 中有一个 DAI/USDT 的交易池,池中的储备量为:100DAI + 100USDT,DAI 在池中的 spot price 是 1 USDT/DAI ,但是,这时我们用 100 DAI 只能从池中换出 50 个 USDT,实际价格(effective price)为 0.5 USDT/DAI。我们把这部分的差就叫做滑点(slippage)

通常会做归一化处理即:slippage = (effective price - spot price) / spot price,上面例子中 slippage = ( 1 - 0.5) / 1 = 0.5 = 50%

那有什么办法可以使滑点小一些吗?毕竟没有人愿意吃亏。我们再看一个例子:还是 USDT/DAI 池,池中的储备量为:1000DAI + 1000USDT,可以发现:这时用 100DAI 能够换出的 USDT 数量为:90.90 个,明显可以看出这次 effective price 就更接近 spot price,而此时的滑点为:(1 - 0.909) / 1 = 0.091 = 9.1%。

原因就在于池子的储备量大了,这便是交易池的深度因此我们可以得出结论:底层代币的储备量越大(深度越大),价格差异越小(滑点越低)

看到这里,聪明的同学可能就要问了 :

②为什么 Sushiswap 给的手续费少了,还有人愿意给 Sushiswap 提供流动性呢(流动性挖矿介绍)?

向 Sushiswap 提供流动性虽然获得的手续费减少了 0.5% ,但是 Sushiswap 额外提供了一个功能:流动性挖矿 (Yield Farming)

这个词听起来时髦的很,其实本质上就是 \" 存币生币 \"(种币得币 [注 1]),和现实中把钱存银行吃利息类似

Sushiswap 允许流动性提供者将手里的 LP token 存到 Sushiswap (SushiChef 的 deposit 方法),而 Sushiswap 每个区块会铸出一定数量的 SUSHI (最初的 100,000 个区块,每个区块释放 100 x 10 = 1,000 枚 SUSHI,往后每个块释放 100 个)这些将 LP token 存到 Sushiswap 的人会获得相应数量的 SUSHI (利息)

【注 1】:流动性挖矿的英文名 Yield Farming,直译过来就是收益耕种,还是很生动展示了 \" 币农 \" 这一形象

【思考题】同样是 3%,一个是全给你,另一个先扣你点,我统一收上来再分配,为啥后面这个就更香?

但是,问题又来了:利息得是真的钱啊,如果有人和你说你把钱存我这里,我每个月会给你发一张白纸,你会存吗?应该不会吧。所以现在 Sushiswap 要做的就是把 SUSHI 的价格 \" 炒高 \":

③SUSHI 代币价值的来源?

还记得之前我们提到的回购 SUSHI 吗?SUSHI 之所以具有价值,就在于这个回购 SUSHI。我们知道一个东西,没人买的话,就算喊到天价也一分钱不值。问题是谁来买 SUSHI?Sushiswap 说「没人买,我来买啊」

那 Sushiswap 用什么买,怎么买?

④ Sushiswap 如何回购 SUSHI (convert 函数的原理)?

还记得 Sushiswap 将 0.3% 的手续费拆成 0.25%+0.05% 吧?收的这 0.05% 就用来买 SUSHI

交易的手续费是预先扣除的,最终反应在 LP token 能从池中换出的底层代币数量(交易者手续费在池中的囤积越多,用一个 LP token 能换出的底层代币就越多)


而每当有人调用 mint (添加流动性)或者 burn (撤出流动性)时,相应的 LP token (0.05%)就会发送给 feeTo,对于 Uniswap 这个 feeTo 尚未设置,而Sushiswap 将这个 feeTo 设置为了 SushiMaker 合约


SushiMaker 这个合约既负责管理从各个池子上收来的 LP token,同时也负责将这些 LP token 换成 SUSHI,以实现回购 SUSHI

而实现将这些 LP token 转换为 SUSHI 的方法,就在于合约中的一个 \" 按钮 \",convert(token0, token1) 函数,任何人都可以按这个按钮

按下后,它会先通过 token0 和 token1 获取到相应的 LP token (也就是交易对的地址),然后将自己拥有的 LP token 烧掉换成底层的两种 token

最后将这两种 token 分别换成 SUSHI:先是分别将两种 token 换成 wETH (通过:token0/wETH,token1/wETH 交易对)[注 4],再将所有的 wETH 换成 SUSHI (通过:wETH/SUSHI 交易对)[注 5](最终换得的 SUSHI 转给 SushiBar 合约,这部分超纲了,和本次攻击无关,就不展开了)

【注 4】因为会先都换成 wETH,所以对于 token 是 wETH 本身,就可以跳过这一步

【注 5】因为最终的目的是换成 SUSHI,所以对于 token 是 SUSHI 本身,就可以跳过这一步


0x1. 攻击分析

本次事件中,攻击者盯上的便是 Sushiswap管理手续费的合约 SushiMaker,下面我们来看看这小子到底干了什么吧:

1.1 基本原理:

上面已经解释了 convert(token0, token1) 函数的原理,并且提到,这个按钮任何人(仅限 EOA)都可以调用。看起来 convert 函数和调用它的人半点关系都没有(只是替 Sushiswap 将 SushiMaker 中存的手续费换成 SUSHI),Sushiswap 的管理员可以定期去调用,闲着没事的好心人也可以帮忙调用

但我们要硬扯上关系的话,其实还是可以沾上点的!

回忆 convert 函数执行过程,有一步是 SushiMaker 将 token0、token1 分别到 token0/wETH、token1/wETH 中去换 wETH,如果这两个交易池是健康的(比如 : USDT、USDC…),确实沾不到什么关系

但这两个交易池有没有可能是恶意的呢(比如:被攻击者操控或劫持),那就是另一个故事了

比方说,token0/wETH 池子是攻击者创建的,滑点奇高。其中的价格非常离谱,token0:wETH 本来是 1:1,但是池中是 300:1,或者是深度极低。我们知道 convert 是要用 token0 来买 wETH,这一步中 SushiMaker 就会血亏,用远低于市场的价格卖出了 token0,但是合约自己是不知道的(没有做相应的检查)

接下来,攻击者只要在这个池中在做一笔反向的交易(用 wETH 买 token),就可以把 SushiMaker 亏的钱弄到自己口袋里了

1.2 资产分析:

既然要攻击 SushiMaker 合约,首先要分析能偷到哪些钱?

SushiMaker 中存放着从各个池中收来的 SLP,而这些SLP 可以直接到相应的交易池去提取底层代币:

1.3 代码漏洞

convert 函数:

// 获取相应 SLP token 合约的地址(即:token0/token1 交易对合约地址)

IUniswapV2Pair pair = IUniswapV2Pair(factory.getPair(token0, token1));

// 将 SushiMaker 中存有的全部 SLP,转给交易池

pair.transfer(address(pair), pair.balanceOf(address(this)));

// Burn 掉这些 SLP 换成两种 token

pair.burn(address(this));

// 分别将两种 token 找到相应的和 wETH 的交易对,全部转换为 wETH

uint256 wethAmount =_toWETH(token0) +_toWETH(token1);

// 将转换的到的全部 wETH 找 wETH/SUSHI 交易对换成 SUSHI

_toSUSHI(wethAmount);

}

_toWETH 函数:

uint amount = IERC20(token).balanceOf(address(this));

所以这个 balanceOf(address(this)) 真的只有在第一阶段 burn 来的 底层代币 吗?

Nononono~

想一想,如果这里的 token 并不是底层代币,而是一个 SLP,会发生什么?

IERC20(SLP).balanceOf(address(this))是 SushiMaker 拥有的所有 SLP,既包含刚刚 burn 出来的,也包含原本 SushiMaker 就有的(从相应的交易池中收取,积攒的手续费)

如果底层代币是 SLP,那第一阶段 burn 的是什么?答案是SLP 的 SLP!


还记得吗?攻击者的目标是将 SushiMaker 诱骗到其创建的恶意交易池中交易,这个 SLP1/WETH 池不恰好是攻击者创建的,当攻击者调用 convert(SLP1, wETH) 时,SushiMaker 由于上面这一漏洞(我们暂且称为资产隔离问题)会将其全部的 SLP1 都到 SLP1/WETH 池中换取 WETH —— 正中下怀

【注】SLP/WETH 池一般都是不存在的,或是深度很浅的。攻击者其实也可以通过建立 SLP1/SLP2 来实现攻击,但是这样的话,还需要单独为 SLP1、SLP2 建立 SLP1/WETH,SLP2/WETH,不如直接一步到位

1.4 Real World

管你看没看懂,继续看就完了

下面我们来看看攻击者在真实世界中到底做了什么吧?(要记得,攻击者的目的是将 SushiMaker 骗到他劫持的恶意交易对中)

攻击者地址:0x1925e832C22522E0d9947eE4677120b2f28E4cD4 (https://etherscan.io/address/0x1925e832c22522e0d9947ee4677120b2f28e4cd4)

一组攻击包括很多笔交易,这里以对MKR/WETH池的攻击为例:

Swap Exact ETH for Tokens: 0xa8c4edd85727dApprove:0xf1fdd4cf4d8aaAdd Liquidity ETH: 0x7340edca1a17fApprove: 0x41a33f0c91b7cAdd Liquidity ETH: 0x896f412f15a7aTransfer: 0x0e8a76bf7295dConvert: 0x4947b4f075f8eSwap Exact ETH For Tokens: 0x5f37bb3b97341Remote Liquidity ETH: 0xdff10159275e0Swap Exact Tokens For ETH: 0xb8889bbdeb478


攻击者的一个完整的攻击周期:

序章:攻击准备

Step 1:通过 swapExactETHForTokens 用一些 ETH 换取 MKR ,作为攻击的启动资金

Step 2:将刚刚换到的 MKR 和 ETH 一起,向 Sushi 的 MKR/wETH 交易池中添加流动性,获得该池的 SLP(MKR/wETH)(后简称为 SLP)

Step 3:将刚刚换到的 SLP 和 ETH 一起,向 Sushi 的 SLP/wETH 交易池中添加流动性(如果不存在该池,自动创建一个),获得新池的 SLP(SLP/wETH) (后简称 SLP')

Step 4:将新生成的 SLP' 转给 SushiMaker

【解释】这步有什么意义呢?因为攻击者后续会调用 convert(SLP, wETH),将 SushiMaker.sol 合约中存放的 SLP' 燃烧掉,换出 SLP, wETH。如果 SushiMaker.sol 中没有 SLP' 就没法进行下去了

但是,实际复现过程中,我发现有没有这步其实都影响不大。因为上一步 addLiquidity(SLP, wETH) 是,一方面会 mint 出 SLP' 给攻击者(0.25% 的手续费),另一方面还会 mint 出一些 SLP' 给 SushiMaker.sol (0.05% 的手续费),所以 SushiMaker.sol 中其实还是有一点 SLP' 的

→ 现在攻击者手里有 SLP',其实他有两个不同的决策:

1.将 SLP' 转给 SushiMaker,这样 SushiMaker 可以换出更多的 SLP 和 wETH,下一步回到这个交易池将自己所有的 SLP (主要是从 MKR/wETH 池中收的手续费,一小部分是 convert 第一阶段中用 SLP' 换出来的)换成 wETH

2.不转 SLP' 给 SushiMaker,因为之前创建 SLP/wETH 这个池的时候会 mint 一点点 SLP' 给 SushiMaker,所以还是可以通过调用 convert 将 SushiMaker 存的 SLP 都换成 wETH

第一种方式中,使 SushiMaker 拥有的 SLP' 更多,会换出更多的 SLP 和 wETH,使得池中的滑点更大,SushiMaker 能亏的 SLP 就更多,但是如果 SushiMaker 中本身存的 SLP 就不多,SLP/wETH 深度也不大时,其实结果差不太多,都是几乎将 SushiMaker 搬空。总而言之,还是第一种方式更优,可以看出攻击者其实是有精心考虑过的


高潮:攻击 SushiMaker

Step 5:调用 convert(SLP, wETH),这一步中,SushiMaker 会先将 SLP' burn 掉,换成 SLP 和 wETH,再将自己所有的 SLP 都转成 wETH (包括之前从 MKR/wETH 池中收的 SLP 手续费),问题在于SLP/wETH 这个池是由攻击者创建的(已被劫持),池中的深度非常小,滑点极高,SushiMaker 通过swap 将 SLP 换成 wETH 这步会血亏(大量的 SLP 只换出了一点点 wETH )


尾声:获利

Step 6:上一步中,SushiMaker 在 SLP/wETH 这个池中血亏了大量的 SLP,池中 wETH 巨值钱,此时攻击者只需要调用 swap 用一点点 wETH 就可以几乎将整个池子搬空(换出全部的 SLP,SLP 是真的钱,可以到 SLP(MKR/WETH) 池中提现的)

0x2 附录

2.1 本次事件相关的攻击交易:

[

{
\"YFI/WETH\": {
\"0x18be4ac177c5ada3522e3cafd6c40d750d4288a3a7b22e140521124074fed2ae\": \"Swap Exact ETH For Tokens\",
\"0xc8653ef0c4658acf3d44059babea07c62578769cac10176e61310f7af04e1597\": \"Approve\",
\"0x18ed3e5efeec3e475501786541a4f7602340e5aa6bc6cbf24b9c9aa1062449ac\": \"Add Liquidity ETH\",
\"0x42d553b614d0a**ea2cfe95bf2a99d337c07f1b53ac4d7987966fc39817975e6\": \"Approve\",
\"0x041463e755e91548b9d6dd1e61519b08f101ff96a44df969b33ee57996619a88\": \"Add Liquidity ETH\",
\"0x8a2d7926e50123459b7814c0e19b5ac59f7ce221cb5fa82f79d2c0c37ef7ef1f\": \"Transfer\",
\"0xaa001ac10841c784954b0028192f0ea232bf84e390b964af98f1c49074ec4beb\": \"Convert\",
\"0x08bbbeaf7cbd2649738812cf720042eb7ebe1411be2f7cf3ede41411dcbf8bc0\": \"Swap Exact ETH For Tokens\",
\"0x3952f24ef1e5dce37463edcf0f3902316131fb823d79946b96f2a2397a9cd25d\": \"Remove Liquidity ETH\",
\"0x47c200fefd8c007d972b65b97dadf429c3e6e91359b7b49aaa66ee0fc4a555f4\": \"Swap Exact Tokens For ETH\"
},
\"profit\": \"0.2558139 ether\"
},
{
\"USDT/WETH\": {
\"0x123ec6c44fa3baa70c66f583f8ee6bc6b6d2b4d39a1119ab8490d2c1120d4**7\": \"Convert\",
\"0x08bbbeaf7cbd2649738812cf720042eb7ebe1411be2f7cf3ede41411dcbf8bc0\": \"Swap Exact ETH For Tokens\",
\"0x42973ea279d0bda4222e4689709f86bdb12117dce14420f84f3e9cc6fd42ede2\": \"Remote Liquidity ETH\",
\"0x30eab0a184f9a1ed6cee309754bb6536bd443fea567776a72b72f3ab911e0113\": \"Swap Exact Tokens For ETH\"
},
\"profit\": \"0.1835604 ether\"
},
{
\"MKR/WETH\": {
\"0xa8c4edd85727d3d25401f0cbca1136982edb833f77ff0a93178b222063eb57a4\": \"Swap Exact ETH for Tokens\",
\"0xf1fdd4cf4d8aa073ff01876333cfeadeab2b874236**d1daa4cf36ad5e415587\": \"Approve\",
\"0x7340edca1a17fc9e5a6587d6a89c0ed01c5d1f1d20cb19738ab993f6ef7b8b4d\": \"Add Liquidity ETH\",
\"0x41a33f0c91b7cd17edf888356dfb110d7744f593e432b54ec2f21aae19076aa0\": \"Approve\",
\"0x896f412f15a7abb959c3c43b8cd9206720fc37f4eabeae336ba101670003e9db\": \" Add Liquidity ETH\",
\"0x0e8a76bf7295d9316704fde7f953eced612daa9ca7a70322248eeb8a7f508656\": \"Transfer\",
\"0x4947b4f075f8e9f89f63**158b3d05a92cd72b7920c44c2150a0d12ba009a7a0\": \"Convert\",
\"0x5f37bb3b9734175b605f6e02700c4b710e5e01622f30d822bcca227d91363d77\": \"Swap Exact ETH For Tokens\",
\"0xdff10159275e0**dad2c7c79585d1f3c4fd60b73d843715e0cff1a2292730e99\": \"Remote Liquidity ETH\",
\"0xb8889bbdeb478809c3781ccb1c8a16f9fa90b6844d180818b6e73a16963a5ddc\": \"Swap Exact Tokens For ETH\"
},
\"profit\": \"1.9761631 ether\"
},
{
\"KP3R/WETH\": {
\"0xe5a025658cd28a688442a57a9b441980feeed8f5e78920**efc68966eb4253eb\": \"Swap Exact ETH For Tokens\",
\"0xe77e7e9d636c2b733d226693d95d06005ae2b5aa8532957240310225aa73e051\": \"Approve\",
\"0x5ce8451f9f39f2d32ffbd943d38723a8da6fff59ece8f62caa84c4c2a311ede3\": \"Add Liquidity ETH\",
\"0x171b741bb7cdcafebdbed1c503eb33babc70c2e2e45b2b55330df3bc6d5c9eda\": \"Approve\",
\"0x0ad658ebab282a26d67da0a97083a41c83d0eb1ab6d19a4adb3676b57fc3851c\": \"Add Liquidity ETH\",
\"0x676dd29fade2b93e5ca4b0da9d2b5e4ede86f69d26a6201440e359892360b096\": \"Transfer\",
\"0x3b37a792edbb785b223a2b4f0971834083acd52503e54d00989a365bbc533627\": \"Convert\",
\"0x4df5506fdc7616ff65b4ae3b93360e180c7b6f5e976914c4af04**8ad6559817\": \"Swap Exact ETH For Tokens\",
\"0x07e56eaa7935fb341654ac20223b0d32fab006b3f34e372aecf6bce05c3f903f\": \"Remove Liquidity ETH\",
\"0x8f4d9d676e9a12d07dcd0c8225e207df48f8cb575cdaa6d81c4b7124c3500510\": \"Approve\",
\"0xac3c837c249fb68ceebd896a72e1e4e8cf355caf3f5fcd0bdc7df7c101ea0596\": \"Swap Exact Tokens For ETH\"
},
\"profit\": \"1.8226875 ether\"
},
{
\"YFI/WETH\": {
\"0x1b0eb70bc4d2407745df416acf6e2c312d25ee63c7e8e1291545d66e23fbf704\": \"Pre Attack\",
\"0x8f6caabd4ecfad30793db76f60a7e28832a18e2bcf006c0ee3652063c7386778\": \"Convert\",
\"0xe6d28052b55ab520a211daebbc67ee3aece6ae356044e9352fc82910214a038a\": \"Swap\"
},
\"profit\": \"-0.031382 ether\"
},
{
\"AXS/WETH\": {
\"0x375f5befeb1af976ebb09cf5539b3a3b9746b87153cfa96ba862deb8828970f1\": \"Pre Attack\",
\"0xed8508a18c347c3829024f7acb94056a78cc63152e2246ebd42b59cd86d0c8f9\": \"Convert\",
\"0xd08eb264e39cd5dca8edb3684dfd36afb905aea9c47a43110b6c5c45bdb2f7d1\": \"Swap\"
},
\"profit\": \"1.349482 ether\"
},
{
\"CRD/WETH\": {
\"0xc6c86de784359a3813b31b4be5806a23f82fdd8d5cd9bd3a7379d33a413eb920\": \"Pre Attack\",
\"0x7d1b36019fb287867419dffb281b5ad73b37d8dc21abca47f7b618875842ef0a\": \"Convert\",
\"0x8c14595b205cd04d1e5062a3efe47d3e4dae946eabe744e2d8350d6ba951495a\": \"Swap\"
},
\"profit\": \"1.325357 ether\"
},
{
\"USDC/WETH\": \"Failed\"
},
{
\"COVER/WETH\": \"Failed\"
},
{
\"YAX/WETH\": {
\"0x063bc5df97c0ec87d6c309b54fbc4e3ed13b22f5a115fb1170015e4e9d23cd3d\": \"Pre Attack\",
\"0x9537551d0db7ed6ae372c050c73cae1272f4d7e7847ea908d6e4d440809a871f\": \"Convert\",
\"0x9f6bdf1c9065f5f5e5c71f32058093b7ac06e140c3a33db2f21cc8a5ff8a5d21\": \"Swap Exact ETH for Tokens\",
\"0x9281c35277ea6594924600d70b73077a4307973c540edb5b4ed534c7e25f2423\": \"Approve\",
\"0xe2f682d8b4801061d504025f11b3032648a3b94019396f7b12749b162f8404cf\": \"Remote Liquidity ETH\",
\"0xf6ea6c9eb92ca9aed70e8b4b03a6e5565a3a13df71a0aa527fa556077de553bc\": \"Approve\",
\"0xbf19c9a6a4d879467519299100b4f56c4e86369382ba87d90bebf6c1b1893c14\": \"Swap Exact Tokens For ETH\"
},
\"profit\": \"1.153749 ether\"
},
{
\"SEEN/WETH\": {
\"0xc6b39015ab0a32762fb23108e9705cb30d171374c7f9d4b9cf30e**0f43d4884\": \"Pre Attack\",
\"0x3c14c48007147f78ae3c3ac5d56f74413ebc468c6e09f7eb1098c19b9b546185\": \"Convert\",
\"0x8681b43b4814bb54f49650b19d42133c933bcdbc65dfcd36a6cadf7ef916391c\": \"Swap\"
},
\"profit\": \"1.0703319 ether\"
},
{
\"AAVE/WETH\": {
\"0x97c06394d2b9f34606946b438b4fb26ab41f379f8612831c2ec4ed6daac6b4e3\": \"Pre Attack\",
\"0x7eb4eada4f0700e8a2e8b94d4e4a1879e66ccb36285968500e370420210dde6d\": \"Convert\",
\"0x40ae49cdb3e9fa61c3309bf35d08ec99494d4a6aa2815791aed1ca654a04d211\": \"Swap\"
},
\"profit\": \"0.987368 ether\"
},
{
\"REVV/WETH\": {
\"0x59b5e394caf35f5bbe603b42044707cbb20712d765c260da4434fde0fd3c0e15\": \"Pre Attack\",
\"0x45660abd04d88f0dae0524b40182d11cb57710316e58ac14413909a9aaf44869\": \"Convert\",
\"0xcd8749d61d8e96da48de8bd**1280f2ce8d93aa5e1e44c9144d322d8b01b1c81\": \"Swap\"
},
\"profit\": \"0.888306 ether\"
},
{
\"SNX/WETH\": {
\"0x7eea5790e31176d45b944c0f2624c30a755650b1a353e052191e36d23db2abbc\": \"Pre Attack\",
\"0x1518424118b519af43657d5de42311f5b5174aab941ff1b7bfe14b0ef5d0874a\": \"Convert\",
\"0x04cd83c93cf8f37fbafb7d05d5f34cb23bcef8871b2744efad517e6aab38395c\": \"Swap\"
},
\"profit\": \"0.908345 ether\"
},
{
\"OUSD/WETH\": {
\"0xc4c196e2383e472edf7dee0c93830f20b3314490d9145dfeac936249688a4cd8\": \"Swap Exact ETH For Tokens\",
\"0x975e65569e52644fe7b029ce28fef2c3f07dcb818ea9c9d4b7235ab7c4e11c67\": \"Approve\",
\"0x8c569e7eaa6d7dfc57d5115e628a2b8fc2628837d4e73d6ca85125f639e4f0d0\": \"Add Liquidity ETH\",
\"0x0f2ed674f497069878c585297812f036a6dd50060e0685e275092320a3797db0\": \"Approve\",
\"0x483eb12c36b997e9fcb26210e11**00f13702db4887b0f4cc3e04fdb8d40c04b\": \"Add Liquidity ETH\",
\"0xc18eb762dc3fe84afdc7d2642f380c7f56c5ac18eb9a50103766ba0caa5a6bdf\": \"Transfer\",
\"0x45fd39eef68076c052908c7f8a214cac1cad073f2b5ccf0b919e95670bc801d5\": \"Convert\",
\"0x32beea84a5b4916cd7107adbb8f609071547e78751c360ceee9bff455d3e7660\": \"Swap Exact ETH For Tokens\",
\"0x2eb1802c7771695e6dc0835ed2a0e4d029345f35f1d465b8f15ccae11824da2a\": \"Remote Liquidity ETH\",
\"0x2be981eb82e7577ce43c91718e734cc83e7dd4b384095ea18d62540e53b391bc\": \"Swap Exact Tokens For ETH\",
\"0x584c6f03a2029bc1b262f31016d87156af3c0c773ebad44f6a2b1e63d623ba5d\": \"Swap Exact Tokens For ETH\"
},
\"profit\": \"0.4029899 ether\"
},
{
\"CRV/WETH\": {
\"0xa7b8e476e15ff576878efee81bf200abeec1491941c5507ca846b51cd685d01e\": \"Pre Attack\",
\"0x19ee**733e4edb44e22086814e7493f30d5bb66582e5963b0db9ea8d05a5bd73\": \"Convert\",
\"0xe959e2079d1366893cce39212194fd387a0c4594f423c07b9af0d4fa7c2c062c\": \"Swap\"
},
\"profit\": \"0.698111 ether\"
},
{
\"LINK/WETH\": {
\"0x62b3ae66524c68ddabd255066807cd7b7f7304e4e8b2da72b9a5e4d8352d35b9\": \"Pre Attack\",
\"0xb0f7512a12afabb56b3d69cef4df83add82c81338dc92e059d8ba130ef777a1e\": \"Convert\",
\"0x54570d6f723c3b9f9c6c9e5f7c0357ba50c847b87f17ef5bb517416dcc7f47c3\": \"Swap\"
},
\"profit\": \"0.6134929 ether\"
},
{
\"RSR/WETH\": {
\"0xa69b5290fe5dd5580e0c738baa47000cf51ed77cc06b8b54a244c7509f8002ad\": \"Pre Attack\",
\"0x87a639847c58b6704f28407be39e3ec**b01f005b47753b1a99faf776ea11780\": \"Convert\",
\"0xa458915881d79debafe5f465843931e572321d35bc98ec25db9ecb9f6a3ea6fd\": \"Swap\"
},
\"profit\": \"0.589048 ether\"
},
{
\"CORE/WETH\": {
\"0xa01fd6fcfee90a7a2b0a180e7271b9429e9f23cf502ba7966932e5012c1bc9b7\": \"Pre Attack\",
\"0x05af95d603cba12d9ca14ce307a973c8875cdcb2e70b07b295440cd2940ea953\": \"Convert\",
\"0x9bb8b7ca5b41c55a8efcdcd49fdec16747ccee0bf33df8e8f4d62eb116142b21\": \"Swap\"
},
\"profit\": \"0.236909 ether\"
},
{
\"zLOT/WETH\": {
\"0x0bb5d792a1d1ea598e45997d5dedb4fcf6acafab47afdb45892417cd128cf37f\": \"Pre Attack\",
\"0x2550eb33adbe7cfb68cce685363ad2cab90860e5e8a8ed87de1f0a6321e24cf8\": \"Convert\",
\"0x514bd13f68c8afc1f86ea3e74ca5f51724c2736fa327144b7b6aaf7228bd9124\": \"Swap\"
},
\"profit\": \"0.516633 ether\"
},
{
\"USDC/WETH\": {
\"0x98b24059e21c3bdbf7de835f8f30daacaac7a00ba2e9cfdff7e0540b172828aa\": \"Pre Attack\",
\"0x29fd17d8060f94cd43b45718d0f36df727a27ff4ce67b3ce5a65d6d40ff2d99a\": \"Convert\",
\"0x24b03cbacf310b5efd2500c3aa8499a356a5ea835426a33d929a38ceef6ea83d\": \"Swap\"
},
\"profit\": \"0.688682 ether\"
},
{
\"sUSD/WETH\": {
\"0x8014c37ff678124d5fcf652547264769aa125bba933aadd4a81a4f6115d71ff2\": \"Pre Attack\",
\"0xc55509dbd81f4da525a540cdbd319d1ffacd2aab2dd3f07abd51cfa2528f28fb\": \"Convert\",
\"0xc0867b786399a23804f60bb2e974ee5f214a44d418ba5d7a0e51496f9d5f8ff8\": \"Swap\"
},
\"profit\": \"0.4547969 ether\"
},
{
\"AKRO/WETH\": {
\"0xc38c30660b9c06e9cecc77c9ff0019020eb9c6fa20887dc15b354ed1a50fec26\": \"Pre Attack\",
\"0x03b9eb788861e5442c3739738cfa848c73f20871e110c6587609169136c3e772\": \"Convert\",
\"0x88d3a3e31df87329e87fbfc21b9648907365c1e2d5a7a236eae5655c1ba3ad41\": \"Swap\"
},
\"profit\": \"0.43971 ether\"
},
{
\"DAI/WETH\": {
\"0x6964f68672edb916ae170254ee48c537cbb**ca0584651d21fd575550559354f\": \"Pre Attack\",
\"0x61856228835bfd97dc6b9d7674aaabb577b74fa7d7ff7b7f45454a8d521ff533\": \"Convert\",
\"0x053ebfe8aedd5975137cfa9bf7d7329a2d**13f6d6e3**b252f4386222957eed\": \"Swap\"
},
\"profit\": \"0.694006 ether\"
},
{
\"YFI/WETH\": {
\"0xf28c246bf1ce25da1811c7c0eb6fefb8587fa2cdafc84cec4e709600429e6e3f\": \"Pre Attack\",
\"0xc75a8ca881d4da75774f51006651c9946311d40145ce69d07aee3a85627153d6\": \"Convert\",
\"0x332c7eff23c9022fe6578550a079034cd3356d9f66507d2ec38462169a4b282c\": \"Swap\"
},
\"profit\": \"0.672139 ether\"
},
{
\"UBXT/WETH\": {
\"0x635c79a8dca89a3aa95f545e2243a735906c4ff221cfbc160396c150d58ea036\": \"Pre Attack\",
\"0x3ffcfc9985622ad7cf0fdc2eb582ad7ce8bf9e9295fd7a4de44354fdd71a688a\": \"Convert\",
\"0x7c6af5ca27ceb04aad514ddcaee8afc6dd4eb79d0816e24b007e7db205e93ce3\": \"Swap\"
},
\"profit\": \"1.1470659\"
},
{
\"0xMaki\": \"I see u.\"
},
{
\"WBTC/WETH\": {
\"0xa195c9c23a56ca5fa747677c04a2d5a8c513bdfd141e45c20a8c4e091ca73883\": \"Pre Attack\",
\"0x136b1d2bf6c51a6ed1fc3f1da7a2783e3835fa78c9149c1c6d2b21e9aad8b05b\": \"Convert\",
\"0x43cde98b0be5932b8dbf709eebd0cbb4738599c9a4f0795ddb52d7d31f293cf4\": \"Swap\"
},
\"profit\": \"2.575794 ether\"
},
{
\"USDC/WETH\": {
\"0xfc8196231bfb22ae6fcc9ceb04f9e5e3d**7fc9e023870020048be93958218a5\": \"Pre Attack\",
\"0xe43fe2eb54c2eefba519a7ff9cf27f84e743961268dfdf9477a47cd2ea467**2\": \"Convert\",
\"0x4b0b3b51150b3a270d10f3388ccf12a196a2cada4203e3a2c39af88d5dc04958\": \"Swap\"
},
\"profit\": \"1.157208 ether\"
},
{
\"LINK/WETH\": {
\"0x95eea1cedccffc405d0cb9743360712b69a295a66fe0**9b5ab1b067869f05fa\": \"Pre Attack\",
\"0xf259718ee2f81b543bbfbe2f236cd4235651f8365ec8944741a3c7f3242f06b9\": \"Convert\",
\"0x525929006fc3f089d67b6596f53c5ebe6b82de1f8d3cdeb397f1f55ff4937c47\": \"Swap\"
},
\"profit\": \"0.7395449 ether\"
}

后续攻击者又尝试攻击 Sushiswap 的其他相关仿盘项目,如 LuaSwap:

https://etherscan.io/tx/0xeacfed6fb18563c18b9af5cb0d3a5b13e97cf02026ef9a4cb625cc33580d02a7

但可能获利不多,只成功几笔就不了了之了

2.2 本次事件后续结果:

在 #11351530 块,Sushi 的管理员向攻击者喊话:


可以看到,好像有些延迟,攻击者的攻击依然持续了一段时间:


最后一条 Convert 中:


SushiMaker 地址从 0x6684977bBED67 变成了 0x280ac711bb99d:

Old:0x6684977bBED67e101BB80Fc07fCcfba655c0a**F
function convert(address token0, address token1) public {
// At least we try to make front-running harder to do.
// 限制只能 EOA 来调用该函数
require(msg.sender == tx.origin, \"do not convert from contract\");
// 获取相应 SLP token 合约的地址(即:token0/token1 交易对合约地址)
IUniswapV2Pair pair = IUniswapV2Pair(factory.getPair(token0, token1));
// 将 SushiMaker 中存有的全部 SLP,转给交易池
pair.transfer(address(pair), pair.balanceOf(address(this)));
// Burn 掉这些 SLP 换成两种 token
pair.burn(address(this));
// 分别将两种 token 找到相应的和 wETH 的交易对,全部转换为 wETH
uint256 wethAmount =_toWETH(token0) +_toWETH(token1);
// 将转换的到的全部 wETH 找 wETH/SUSHI 交易对换成 SUSHI
_toSUSHI(wethAmount);
}

function_toETH(address token) {
...
uint amountIn = IERC20(token).balanceOf(address(this));
}

New:0x280ac711bb99dE7C73FB70fb6DE29846D5e4207F
function convert(address token0, address token1) public {
// At least we try to make front-running harder to do.
require(msg.sender == tx.origin, \"do not convert from contract\");

IUniswapV2Pair pair = IUniswapV2Pair(factory.getPair(token0, token1));
pair.transfer(address(pair), pair.balanceOf(address(this)));
(uint amount0, uint amount1) = pair.burn(address(this));
uint256 wethAmount =_toWETH(token0, amount0) +_toWETH(token1, amount1);
_toSUSHI(wethAmount);
}

function_toETH(address token, uint amountIn) {
...
}

可以看到区别在于:代码第 26 行_toWETH 限制了 amount,这样修改以后就不会将 SushiMaker 中存的全部 SLP 都去池中换成 wETH,而只是换取 burn 出的一部分

但是这样问题就解决了吗?其实还没有,时隔两个月,同样的地方,Sushiswap 又中枪了 见:amenda:SushiSwap 攻击事件 (2021 年 1 月 27 号) 分析 (https://zhuanlan.zhihu.com/p/372058217)

0x3. 参考

以小博大,简述 Sushi Swap 攻击事件始末:https://www.chainnews.com/zh-hant/articles/726256718885.htmAs token price rises and reputation mends, Sushiswap foils midnight exploit: https://cointelegraph.com/news/as-token-price-rises-and-reputation-mends-sushiswap-foils-midnight-exploitAn evolution of Uniswap with SUSHI tokenomics:https://sushichef.medium.com/the-sushiswap-project-dd6eb80c6ba2一文看懂 Uniswap 和 Sushiswap:https://zhuanlan.zhihu.com/p/226085593sok decentralized exchanges (dex) with automated market maker (amm) protocols: https://arxiv.org/abs/2103.12732Attacking the DeFi Ecosystem with Flash Loans for Fun and Profit: https://arxiv.org/abs/2003.03810

BlockSec 团队以核心安全技术驱动,长期关注 DeFi 安全、数字货币反洗钱和基于隐私计算的数字资产存管,为 DApp 项目方提供合约安全和数字资产安全服务。团队发表 20 多篇顶级安全学术论文 (CCS, USENIX Security, S&P),合伙人获得 AMiner 全球最具影响力的安全和隐私学者称号 (2011-2020 排名全球第六). 研究成果获得中央电视台、新华社和海外媒体的报道。独立发现数十个 DeFi 安全漏洞和威胁,获得 2019 年美国美国国立卫生研究院隐私计算比赛 (SGX 赛道) 全球第一名。团队以技术驱动,秉持开放共赢理念,与社区伙伴携手共建安全 DeFi 生态。

https://www.blocksecteam.com/ [email protected]

—-

编译者/作者:BlockSec

玩币族申明:玩币族作为开放的资讯翻译/分享平台,所提供的所有资讯仅代表作者个人观点,与玩币族平台立场无关,且不构成任何投资理财建议。文章版权归原作者所有。

LOADING...
LOADING...