Defi安全--Zunami Protocol攻击事件分析
其它相关内容可见个人主页
1 Zunami攻击事件相关信息
2023.8.13发生在Ethereum上发生的攻击,存在两个攻击交易,具体信息如下:
-
攻击合约地址:Contract Address 攻击合约
-
攻击者地址:Zunami Protocol Exploiter
-
攻击交易hash1:Ethereum Transaction Hash (Txhash) Details | Etherscan
-
攻击交易hash2:Ethereum Transaction Hash (Txhash) Details | Etherscan
-
phalcon分析调用序列:0x0788ba222970c7c68a | Phalcon Explorer (blocksec.com)
2 攻击流程详解
项目介绍
Zunami是稳定币投资聚合器,用户给定用ETH/USDC/DAI等稳定币投资Zunami协议;
然后Zunami协议会使用用户质押的代币到Curve中高收益的池子进行质押;
那么为了保证更进一步的收益,Zunami还会把Curve的流动性再次质押到StakeDAO和Convex平台中,吃两波流动性奖励。
然后把收到的流动性奖励代币(CRV)经过用户的质押比例返回给用户。
zETH是Zunami协议实现的变基代币(rebase token),变基代币的逻辑是因为他的代币数量计算是锚定了Zunami所有的资产来计算的,所以可以通过闪电贷对Zunami质押的池子买入卖出就可以影响zETH的数量计算
攻击流程
两次攻击交易是单独的,但是基于的漏洞及原理是一致的
以0x0788ba222970c7c68a738b0e08fb197e669e61f9b226ceec4cab9b85abe8cceb攻击交易为例进行分析

- 对攻击交易进行调用序列分析,直接调用攻击合约中的函数;先查看了Balancer: Vault账户中USDC的余额
- 随后攻击者就调用UniswapV3中USDC-USDT对应的闪电贷函数,借出了7 e12wei的USDT;随后查看pair对池子中的USDC和USDT的余额,乐观转账会将对应借贷转给用户。
- 闪电贷会回调攻击者的
uniswapV3FlashCallback函数,回调中攻击者调用Balancer: Vault的flashloan函数,这里可以看一下这里的函数源码
function flashLoan(IFlashLoanRecipient recipient,IERC20[] memory tokens,uint256[] memory amounts,bytes memory userData) external override nonReentrant whenNotPaused
看了一下源码,其功能无特别之处,就是一个闪电贷函数,不过这个函数可以一次借贷多个代币,用tokens和amounts表示对应的数组,先后乐观转账,后回调攻击者,在进行还款
- 在Balancer: Vault的flashloan函数中,会查看对应的余额,进行相应的乐观转账,随后会再次回调到攻击者的
receiveFlashLoan函数,此时用户已经通过借贷获得了大量的USDT、USDC以及ETH

-
随后攻击者给curve finance,sushiswap以及uniswap上很多factory和router合约地址进行相应的代币授权,并调用Curve Finance: Swap用USDC给池子中添加流动性,获得crvFRAX

-
随后调用Curve.fi Factory Pool中的一些pair对的exchange()函数,DEX智能合约的代币交换功能,可以把vyper代码直接放到GPT中解析,可以理解为就算进行代币的交换,攻击者将对应的crvFRAX兑换为Zunami UZD,将USDC兑换为crvUSD。此时用户拥有UZD和crvUSD


-
攻击再次调用exchange()函数,将所有的crvUSD兑换为对应的UZD,最后攻击者拥有4873316数量的UZD,并且将自身的ETH换成对应的SDT,并且将全部的SDT转到
MIMCurveStakeDao中(为什么要进行这样一个存款,可能跟攻击行为有关)
-
随后调用SushiSwap: Router的
swapExactTokensForTokens函数,进行代币的交换,攻击者首先将自身的WETH兑换为对应的SDT,随后将步骤2中通过闪电贷获得的USDT全部兑换为WETH -
攻击者调用UZD合约的cacheAssetPrice()函数,仔细看一下函数源码,获得UZD缓存的资产价格,源码如下:
function cacheAssetPrice() public virtual {_blockCached = block.number;uint256 currentAssetPrice = assetPrice();if (_assetPriceCached < currentAssetPrice) {_assetPriceCached = currentAssetPrice;emit CachedAssetPrice(_blockCached, _assetPriceCached);}}
- 可以看出对应的
_assetPriceCached的价格是由assetPrice()决定的,进一步阅读函数源码
function assetPrice() public view override returns (uint256) {return priceOracle.lpPrice();}
进一步阅读etherscan上源码,可得priceOracle地址为0x2ffCC661011beC72e1A9524E12060983E74D14ce,查看该合约的lpPrice()函数。
function lpPrice() external view returns (uint256) {return (totalHoldings() * 1e18) / totalSupply();}
价格取决于totalHoldings()函数,totalSupply()为ERC标准函数
function totalHoldings() public view returns (uint256) {uint256 length = _poolInfo.length;uint256 totalHold = 0;for (uint256 pid = 0; pid < length; pid++) {totalHold += _poolInfo[pid].strategy.totalHoldings();}return totalHold;}
这个会取决于每个_poolInfo[pid].strategy的Holdings()函数,这里我们去看MIMCurveStakeDao对应的函数,源码如下所示:
function totalHoldings() public view virtual returns (uint256) {uint256 crvLpHoldings = (vault.liquidityGauge().balanceOf(address(this)) * getCurvePoolPrice()) /CURVE_PRICE_DENOMINATOR;uint256 sdtEarned = vault.liquidityGauge().claimable_reward(address(this), address(_config.sdt));uint256 amountIn = sdtEarned + _config.sdt.balanceOf(address(this));uint256 sdtEarningsInFeeToken = priceTokenByExchange(amountIn, _config.sdtToFeeTokenPath);uint256 crvEarned = vault.liquidityGauge().claimable_reward(address(this), address(_config.crv));amountIn = crvEarned + _config.crv.balanceOf(address(this));uint256 crvEarningsInFeeToken = priceTokenByExchange(amountIn, _config.crvToFeeTokenPath);uint256 tokensHoldings = 0;for (uint256 i = 0; i < 3; i++) {tokensHoldings += _config.tokens[i].balanceOf(address(this)) * decimalsMultipliers[i];}returntokensHoldings +crvLpHoldings +(sdtEarningsInFeeToken + crvEarningsInFeeToken) *decimalsMultipliers[feeTokenId];}function priceTokenByExchange(uint256 amountIn, address[] memory exchangePath)internalviewreturns (uint256){if (amountIn == 0) return 0;uint256[] memory amounts = _config.router.getAmountsOut(amountIn, exchangePath);return amounts[amounts.length - 1];}
重点关注sdtEarningsInFeeToken,因为攻击者在此之前,给该合约存入了大量的SDT,仔细看一下priceTokenByExchange()函数

进一步可以去SushiSwap: Router中查看getAmountsOut()函数,发现其返回值与amountIn正相关,amountIn的值一定程度上取决于该合约当前的SDT余额,而攻击者在此之前给该地址存入了大量的SDT,最终导致sdtEarningsInFeeToken数量过高,CachedAssetPrice价格过高

- 随后攻击者调用SushiSwap: Router的
swapExactTokensForTokens函数,将SDT转化为WETH,将WETH换成USDT - 随后调用UZD合约中的balanceOf函数,发现其依赖于被操纵的cacheAssetPrice价格,具体如下:
function balanceOf(address account) public view virtual override returns (uint256) {if (!containRigidAddress(account)) return super.balanceOf(account);return _balancesRigid[account];}function balanceOf(address account) public view virtual override returns (uint256) {// don't cache pricereturn _convertFromNominalCached(_balances[account], Math.Rounding.Down);}function _convertFromNominalWithCaching(uint256 nominal, Math.Rounding rounding)internalvirtualreturns (uint256 value){if (nominal == type(uint256).max) return type(uint256).max;_cacheAssetPriceByBlock();return nominal.mulDiv(assetPriceCached(), DEFAULT_DECIMALS_FACTOR, rounding);}
所以其会错误计算攻击者的UZD余额,这时攻击者进行相应的套利即可

-
通过Curve.fi Factory Pool的exchange函数,先将错误余额数量的UZD,一部分兑换为crvFRAX,另一部分兑换为crvUSD。
移除Curve Finance: Swap中的流动性,攻击者获得对应的FRAX和USDC。
调用exchange函数,将对应的FRAX和crvUSD兑换城USDC
并且最后将大部分的USDC全部兑换成USDT,现在攻击者资产为USDT和USDC。

- 调用WETH-USDCpair对的闪电贷,获得大量的WETH,攻击者偿还相应数量的USDC,并偿还第2步中Balancer: Vault闪电贷借贷的WETH和USDC,最后偿还第一步中uniswapV3借贷的USDT
- 最后偿还完闪电贷后,攻击者获得资产USDT和WETH,将其全部提取完成攻击。
再简单看一下另一个攻击交易0x2aec4fdb2a09ad4269a410f2c770737626fb62c54e0fa8ac25e8582d4b690cca
- 也是先调用攻击合约,后进行闪电贷,借出WETH,然后通过curve finance将eth兑换成zETH
- 将ETH兑换成CRV,存入sEthFraxEthCurveConvex合约中,与上述相同,攻击者账户的zETH余额和sEthFraxEthCurveConvex合约中的CRV余额相关,攻击者通过多次在wETH/CRV在池子中兑换CRV,操纵了CRV的价格和漏洞合约的CRV余额,最终导致CachedAssetPrice变大
相关文章:
Defi安全--Zunami Protocol攻击事件分析
其它相关内容可见个人主页 1 Zunami攻击事件相关信息 2023.8.13发生在Ethereum上发生的攻击,存在两个攻击交易,具体信息如下: 攻击合约地址:Contract Address 攻击合约 攻击者地址:Zunami Protocol Exploiter 攻击…...
虾皮电商 电商平台:虾皮(Shopee)东南亚领先的电子商务平台
在当今数字化时代,电子商务平台的兴起改变了人们的购物方式。虾皮(Shopee)作为东南亚地区领先的电子商务平台,为消费者提供了便捷、多样化的购物体验。由新加坡的Sea Group(前称Garena)于2015年创立&#x…...
【降龙算法】基于QT插件机制实现一个机器视觉算法小框架
机器视觉行业有各种各样的拖拉拽框架,也叫做低代码平台,例如国内海康的VisionMaster: 一个机器视觉框架需要包含各种算法模块,日志窗口,图像显示窗口等等,【降龙算法】就是做了一个入门级的机器视觉算法框…...
智能路由器 端口映射 (UPnP) Padavan内网端口映射配置方法
新版本Padavan 4.4内核的端口映射配置和老版本的不太一样,因为新版本默认是启用的 UPnP端口映射, 同时默认使用的是 IGD UPnP自动端口映射, UPnP名词解释: UPnP通用即插即用,是一组协议的统称,是一种基于TCP/IP、UDP和HTTP的分布式、开放体系ÿ…...
MR-GCN
∘ Φ \circ_Φ ∘Φ denotes a convolution Let b l o c k d i a g blockdiag blockdiag(A) be a n1n3-by-n2n3 block diagonal matrix, f o l d fold fold indicate its inverse operator diagonal degree tensor D \mathcal{D} D 作者未提供代码...
Java http 响应式请求和非响应式请求有什么区别
在Java中,HTTP的响应式请求和非响应式请求有以下区别: HTTP协议本身并不直接支持响应式请求,因为HTTP是基于请求-响应模型的。然而,可以通过使用其他技术和协议来实现响应式请求。 响应方式:响应式请求是指使用响应式编…...
CHS_06.2.1.6_2+线程的实现方式和多线程模型
CHS_06.2.1.6_2线程的实现方式和多线程模型 知识总览线程的实现方式用户级线程(User-Level Thread, ULT)内核级线程 多线程模型一对一模型多对一多对多模型 知识回顾 在上个小节中 我们学习了线程相关的一些基本概念 基础的知识 那这个小节中 我们回来看…...
k8s集群配置NodeLocal DNSCache
一、简介 当集群规模较大时,运行的服务非常多,服务之间的频繁进行大量域名解析,CoreDNS将会承受更大的压力,可能会导致如下影响: 延迟增加:有限的coredns服务在解析大量的域名时,会导致解析结果…...
Superpoint Transformer for 3D Scene Instance Segmentation
Abstract 现有的大多数方法通过扩展用于3D物体检测或3D语义分割的模型来实现3D实例分割。然而,这些非直接的方法存在两个缺点:1) 不精确的边界框或不令人满意的语义预测限制了整体3D实例分割框架的性能。2) 现有方法需要一个耗时的中间聚合步骤。为了解决这些问题,本文提出…...
adb调试软件下载 及 常用调试命令
一、软件下载 Windows版本:下载 Mac版本:下载 Linux版本:下载 二、常见调试命令 进入ADB调试 在文件路径栏输入cmd,回车,即可进入adb调试。注意:以下3条不要登录设备 shell (一)显…...
变电站综合自动化监控系统在某物流园35kV变电站中应用
摘 要:Acrel-1000变电站综合自动化系统,是我司根据电力系统自动化及无人值守的要求,总结国内外的研究和生产的先进经验,专门研制出的新一代电力监控系统。本系统具有保护、遥测、遥信、遥脉、遥调、遥控功能,可实现无人…...
技术的本质,是解决需求
分享下今日朋友圈精华内容。 很多人在初学时,走了很多弯路,一味追求热门、高性能、高复杂的芯片,或者系统,学了一堆东西,最后连个简单的功能,都实现不了。 大概是忘了,学技术的本质,…...
【刷题】leetcode 1 . 两数之和
两数之和 两数之和1 思路一 (简单突破)2 思路二 (进行优化)3 思路三 (哈希表 我还不会) 谢谢阅读Thanks♪(・ω・)ノ下一篇文章见!!! 两数…...
Sip - Ubuntu 配置 miniSIPServer 服务器(测试用)
客户提供的账号过期了,简单搭建 SIP 服务器,以便测试使用。个人认为这个配置起来最为简单,且测试功能足够。 官网miniSIPServer - 基于 Windows 以及 Linux 平台的 VoIP (SIP) 服务器软件. miniSIPServer 可能是最容易使用的 VoIP(SIP) 服务器…...
SpringCloud openFeign 之 获取被调用服务名
SpringCloud openFeign 之 获取被调用服务名 一. 概述 低版本 feign 只能获取到被调用方法的信息。 只有高版本 feign 才支持获取到被调用服务的信息。 二. 代码实现 package com.zxguan.springcloud2.template.user;import com.zxguan.springcloud2.template.user.config…...
ChatGPT和文心一言哪个更好用?
ChatGPT和文心一言都是基于深度学习技术的自然语言处理模型,它们各自具有优势和局限性,需要根据具体需求进行选择。以下是两者的比较: 算力:ChatGPT由OpenAI开发,具有强大的文本生成能力和语言理解能力,其训…...
第07章_面向对象编程(进阶)拓展练习(关键字:this,继承性和方法重写,关键字:super,多态性,Object类)
文章目录 第07章_面向对象编程(进阶)拓展练习01-关键字:this1、Circle类2、MyDate类3、Card类 02-继承性和方法重写4、Person、Student、Teacher类5、DepositCard、CreditCard类6、Employee、Programmer、Designer、Architect类7、判断输出结…...
小米路由器有线中继模式设置固定IP
第一步 小米路由器切换为有线中继模式后,进电脑版web管理界面,点击中继设置,把web页面地址中apsetting修改为setting(如下)后按回车键加载新页面。 修改前: http://192.168.1.168/cgi-bin/luci/;stokxxxx…...
用python实现给出关键字查找并标注pdf文件中关键字
要在Python中标注PDF文件中的关键字,可以使用Python的PDFMiner库和Python的matplotlib库。 首先,需要安装这两个库。可以使用pip命令进行安装: shell 复制代码 pip install pdfminer.six matplotlib 接下来,可以使用以下代码实现…...
postman自动化接口测试
背景描述 有一个项目要使用postman进行接口测试,接口所需参数有: appid: 应用标识;sign:请求签名,需要使用HMACSHA1加密算法计算,签名串是:{appid}${url}${stamp};stamp࿱…...
基于距离变化能量开销动态调整的WSN低功耗拓扑控制开销算法matlab仿真
目录 1.程序功能描述 2.测试软件版本以及运行结果展示 3.核心程序 4.算法仿真参数 5.算法理论概述 6.参考文献 7.完整程序 1.程序功能描述 通过动态调整节点通信的能量开销,平衡网络负载,延长WSN生命周期。具体通过建立基于距离的能量消耗模型&am…...
Mybatis逆向工程,动态创建实体类、条件扩展类、Mapper接口、Mapper.xml映射文件
今天呢,博主的学习进度也是步入了Java Mybatis 框架,目前正在逐步杨帆旗航。 那么接下来就给大家出一期有关 Mybatis 逆向工程的教学,希望能对大家有所帮助,也特别欢迎大家指点不足之处,小生很乐意接受正确的建议&…...
DIY|Mac 搭建 ESP-IDF 开发环境及编译小智 AI
前一阵子在百度 AI 开发者大会上,看到基于小智 AI DIY 玩具的演示,感觉有点意思,想着自己也来试试。 如果只是想烧录现成的固件,乐鑫官方除了提供了 Windows 版本的 Flash 下载工具 之外,还提供了基于网页版的 ESP LA…...
高防服务器能够抵御哪些网络攻击呢?
高防服务器作为一种有着高度防御能力的服务器,可以帮助网站应对分布式拒绝服务攻击,有效识别和清理一些恶意的网络流量,为用户提供安全且稳定的网络环境,那么,高防服务器一般都可以抵御哪些网络攻击呢?下面…...
视频行为标注工具BehaviLabel(源码+使用介绍+Windows.Exe版本)
前言: 最近在做行为检测相关的模型,用的是时空图卷积网络(STGCN),但原有kinetic-400数据集数据质量较低,需要进行细粒度的标注,同时粗略搜了下已有开源工具基本都集中于图像分割这块,…...
论文笔记——相干体技术在裂缝预测中的应用研究
目录 相关地震知识补充地震数据的认识地震几何属性 相干体算法定义基本原理第一代相干体技术:基于互相关的相干体技术(Correlation)第二代相干体技术:基于相似的相干体技术(Semblance)基于多道相似的相干体…...
VM虚拟机网络配置(ubuntu24桥接模式):配置静态IP
编辑-虚拟网络编辑器-更改设置 选择桥接模式,然后找到相应的网卡(可以查看自己本机的网络连接) windows连接的网络点击查看属性 编辑虚拟机设置更改网络配置,选择刚才配置的桥接模式 静态ip设置: 我用的ubuntu24桌…...
【分享】推荐一些办公小工具
1、PDF 在线转换 https://smallpdf.com/cn/pdf-tools 推荐理由:大部分的转换软件需要收费,要么功能不齐全,而开会员又用不了几次浪费钱,借用别人的又不安全。 这个网站它不需要登录或下载安装。而且提供的免费功能就能满足日常…...
解决:Android studio 编译后报错\app\src\main\cpp\CMakeLists.txt‘ to exist
现象: android studio报错: [CXX1409] D:\GitLab\xxxxx\app.cxx\Debug\3f3w4y1i\arm64-v8a\android_gradle_build.json : expected buildFiles file ‘D:\GitLab\xxxxx\app\src\main\cpp\CMakeLists.txt’ to exist 解决: 不要动CMakeLists.…...
Ubuntu系统复制(U盘-电脑硬盘)
所需环境 电脑自带硬盘:1块 (1T) U盘1:Ubuntu系统引导盘(用于“U盘2”复制到“电脑自带硬盘”) U盘2:Ubuntu系统盘(1T,用于被复制) !!!建议“电脑…...
