当前位置: 首页 > news >正文

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攻击交易为例进行分析

image-20240112153942369

  1. 对攻击交易进行调用序列分析,直接调用攻击合约中的函数;先查看了Balancer: Vault账户中USDC的余额
  2. 随后攻击者就调用UniswapV3中USDC-USDT对应的闪电贷函数,借出了7 e12wei的USDT;随后查看pair对池子中的USDC和USDT的余额,乐观转账会将对应借贷转给用户。
  3. 闪电贷会回调攻击者的uniswapV3FlashCallback函数,回调中攻击者调用Balancer: Vault的flashloan函数,这里可以看一下这里的函数源码
    function flashLoan(IFlashLoanRecipient recipient,IERC20[] memory tokens,uint256[] memory amounts,bytes memory userData) external override nonReentrant whenNotPaused 

看了一下源码,其功能无特别之处,就是一个闪电贷函数,不过这个函数可以一次借贷多个代币,用tokensamounts表示对应的数组,先后乐观转账,后回调攻击者,在进行还款

  1. 在Balancer: Vault的flashloan函数中,会查看对应的余额,进行相应的乐观转账,随后会再次回调到攻击者的receiveFlashLoan函数,此时用户已经通过借贷获得了大量的USDT、USDC以及ETH

image-20240112162057959

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

    image-20240112171138441

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

image-20240112172018837

image-20240112172948516

  1. 攻击再次调用exchange()函数,将所有的crvUSD兑换为对应的UZD,最后攻击者拥有4873316数量的UZD,并且将自身的ETH换成对应的SDT,并且将全部的SDT转到MIMCurveStakeDao中(为什么要进行这样一个存款,可能跟攻击行为有关)image-20240112174205177

  2. 随后调用SushiSwap: Router的swapExactTokensForTokens函数,进行代币的交换,攻击者首先将自身的WETH兑换为对应的SDT,随后将步骤2中通过闪电贷获得的USDT全部兑换为WETH

  3. 攻击者调用UZD合约的cacheAssetPrice()函数,仔细看一下函数源码,获得UZD缓存的资产价格,源码如下:

    function cacheAssetPrice() public virtual {_blockCached = block.number;uint256 currentAssetPrice = assetPrice();if (_assetPriceCached < currentAssetPrice) {_assetPriceCached = currentAssetPrice;emit CachedAssetPrice(_blockCached, _assetPriceCached);}}
  1. 可以看出对应的_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()函数

image-20240112201732602

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

image-20240112203605220

  1. 随后攻击者调用SushiSwap: Router的swapExactTokensForTokens函数,将SDT转化为WETH,将WETH换成USDT
  2. 随后调用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余额,这时攻击者进行相应的套利即可

image-20240112203656910

  1. 通过Curve.fi Factory Pool的exchange函数,先将错误余额数量的UZD,一部分兑换为crvFRAX,另一部分兑换为crvUSD。

    移除Curve Finance: Swap中的流动性,攻击者获得对应的FRAX和USDC。

    调用exchange函数,将对应的FRAX和crvUSD兑换城USDC

    并且最后将大部分的USDC全部兑换成USDT,现在攻击者资产为USDT和USDC。

image-20240112204626131

  1. 调用WETH-USDCpair对的闪电贷,获得大量的WETH,攻击者偿还相应数量的USDC,并偿还第2步中Balancer: Vault闪电贷借贷的WETH和USDC,最后偿还第一步中uniswapV3借贷的USDT
  2. 最后偿还完闪电贷后,攻击者获得资产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上发生的攻击&#xff0c;存在两个攻击交易&#xff0c;具体信息如下&#xff1a; 攻击合约地址&#xff1a;Contract Address 攻击合约 攻击者地址&#xff1a;Zunami Protocol Exploiter 攻击…...

虾皮电商 电商平台:虾皮(Shopee)东南亚领先的电子商务平台

在当今数字化时代&#xff0c;电子商务平台的兴起改变了人们的购物方式。虾皮&#xff08;Shopee&#xff09;作为东南亚地区领先的电子商务平台&#xff0c;为消费者提供了便捷、多样化的购物体验。由新加坡的Sea Group&#xff08;前称Garena&#xff09;于2015年创立&#x…...

【降龙算法】基于QT插件机制实现一个机器视觉算法小框架

机器视觉行业有各种各样的拖拉拽框架&#xff0c;也叫做低代码平台&#xff0c;例如国内海康的VisionMaster&#xff1a; 一个机器视觉框架需要包含各种算法模块&#xff0c;日志窗口&#xff0c;图像显示窗口等等&#xff0c;【降龙算法】就是做了一个入门级的机器视觉算法框…...

智能路由器 端口映射 (UPnP) Padavan内网端口映射配置方法

新版本Padavan 4.4内核的端口映射配置和老版本的不太一样,因为新版本默认是启用的 UPnP端口映射, 同时默认使用的是 IGD UPnP自动端口映射, UPnP名词解释: UPnP通用即插即用&#xff0c;是一组协议的统称&#xff0c;是一种基于TCP/IP、UDP和HTTP的分布式、开放体系&#xff…...

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&#xff0c; f o l d fold fold indicate its inverse operator diagonal degree tensor D \mathcal{D} D 作者未提供代码...

Java http 响应式请求和非响应式请求有什么区别

在Java中&#xff0c;HTTP的响应式请求和非响应式请求有以下区别&#xff1a; HTTP协议本身并不直接支持响应式请求&#xff0c;因为HTTP是基于请求-响应模型的。然而&#xff0c;可以通过使用其他技术和协议来实现响应式请求。 响应方式&#xff1a;响应式请求是指使用响应式编…...

CHS_06.2.1.6_2+线程的实现方式和多线程模型

CHS_06.2.1.6_2线程的实现方式和多线程模型 知识总览线程的实现方式用户级线程&#xff08;User-Level Thread, ULT&#xff09;内核级线程 多线程模型一对一模型多对一多对多模型 知识回顾 在上个小节中 我们学习了线程相关的一些基本概念 基础的知识 那这个小节中 我们回来看…...

k8s集群配置NodeLocal DNSCache

一、简介 当集群规模较大时&#xff0c;运行的服务非常多&#xff0c;服务之间的频繁进行大量域名解析&#xff0c;CoreDNS将会承受更大的压力&#xff0c;可能会导致如下影响&#xff1a; 延迟增加&#xff1a;有限的coredns服务在解析大量的域名时&#xff0c;会导致解析结果…...

Superpoint Transformer for 3D Scene Instance Segmentation

Abstract 现有的大多数方法通过扩展用于3D物体检测或3D语义分割的模型来实现3D实例分割。然而,这些非直接的方法存在两个缺点:1) 不精确的边界框或不令人满意的语义预测限制了整体3D实例分割框架的性能。2) 现有方法需要一个耗时的中间聚合步骤。为了解决这些问题,本文提出…...

adb调试软件下载 及 常用调试命令

一、软件下载 Windows版本&#xff1a;下载 Mac版本&#xff1a;下载 Linux版本&#xff1a;下载 二、常见调试命令 进入ADB调试 在文件路径栏输入cmd&#xff0c;回车&#xff0c;即可进入adb调试。注意&#xff1a;以下3条不要登录设备 shell &#xff08;一&#xff09;显…...

变电站综合自动化监控系统在某物流园35kV变电站中应用

摘 要&#xff1a;Acrel-1000变电站综合自动化系统&#xff0c;是我司根据电力系统自动化及无人值守的要求&#xff0c;总结国内外的研究和生产的先进经验&#xff0c;专门研制出的新一代电力监控系统。本系统具有保护、遥测、遥信、遥脉、遥调、遥控功能&#xff0c;可实现无人…...

技术的本质,是解决需求

分享下今日朋友圈精华内容。 很多人在初学时&#xff0c;走了很多弯路&#xff0c;一味追求热门、高性能、高复杂的芯片&#xff0c;或者系统&#xff0c;学了一堆东西&#xff0c;最后连个简单的功能&#xff0c;都实现不了。 大概是忘了&#xff0c;学技术的本质&#xff0c;…...

【刷题】leetcode 1 . 两数之和

两数之和 两数之和1 思路一 &#xff08;简单突破&#xff09;2 思路二 &#xff08;进行优化&#xff09;3 思路三 &#xff08;哈希表 我还不会&#xff09; 谢谢阅读Thanks♪(&#xff65;ω&#xff65;)&#xff89;下一篇文章见&#xff01;&#xff01;&#xff01; 两数…...

Sip - Ubuntu 配置 miniSIPServer 服务器(测试用)

客户提供的账号过期了&#xff0c;简单搭建 SIP 服务器&#xff0c;以便测试使用。个人认为这个配置起来最为简单&#xff0c;且测试功能足够。 官网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和文心一言都是基于深度学习技术的自然语言处理模型&#xff0c;它们各自具有优势和局限性&#xff0c;需要根据具体需求进行选择。以下是两者的比较&#xff1a; 算力&#xff1a;ChatGPT由OpenAI开发&#xff0c;具有强大的文本生成能力和语言理解能力&#xff0c;其训…...

第07章_面向对象编程(进阶)拓展练习(关键字:this,继承性和方法重写,关键字:super,多态性,Object类)

文章目录 第07章_面向对象编程&#xff08;进阶&#xff09;拓展练习01-关键字&#xff1a;this1、Circle类2、MyDate类3、Card类 02-继承性和方法重写4、Person、Student、Teacher类5、DepositCard、CreditCard类6、Employee、Programmer、Designer、Architect类7、判断输出结…...

小米路由器有线中继模式设置固定IP

第一步 小米路由器切换为有线中继模式后&#xff0c;进电脑版web管理界面&#xff0c;点击中继设置&#xff0c;把web页面地址中apsetting修改为setting&#xff08;如下&#xff09;后按回车键加载新页面。 修改前&#xff1a; http://192.168.1.168/cgi-bin/luci/;stokxxxx…...

用python实现给出关键字查找并标注pdf文件中关键字

要在Python中标注PDF文件中的关键字&#xff0c;可以使用Python的PDFMiner库和Python的matplotlib库。 首先&#xff0c;需要安装这两个库。可以使用pip命令进行安装&#xff1a; shell 复制代码 pip install pdfminer.six matplotlib 接下来&#xff0c;可以使用以下代码实现…...

postman自动化接口测试

背景描述 有一个项目要使用postman进行接口测试&#xff0c;接口所需参数有&#xff1a; appid: 应用标识&#xff1b;sign&#xff1a;请求签名&#xff0c;需要使用HMACSHA1加密算法计算&#xff0c;签名串是&#xff1a;{appid}${url}${stamp}&#xff1b;stamp&#xff1…...

【Python】 -- 趣味代码 - 小恐龙游戏

文章目录 文章目录 00 小恐龙游戏程序设计框架代码结构和功能游戏流程总结01 小恐龙游戏程序设计02 百度网盘地址00 小恐龙游戏程序设计框架 这段代码是一个基于 Pygame 的简易跑酷游戏的完整实现,玩家控制一个角色(龙)躲避障碍物(仙人掌和乌鸦)。以下是代码的详细介绍:…...

测试markdown--肇兴

day1&#xff1a; 1、去程&#xff1a;7:04 --11:32高铁 高铁右转上售票大厅2楼&#xff0c;穿过候车厅下一楼&#xff0c;上大巴车 &#xffe5;10/人 **2、到达&#xff1a;**12点多到达寨子&#xff0c;买门票&#xff0c;美团/抖音&#xff1a;&#xffe5;78人 3、中饭&a…...

屋顶变身“发电站” ,中天合创屋面分布式光伏发电项目顺利并网!

5月28日&#xff0c;中天合创屋面分布式光伏发电项目顺利并网发电&#xff0c;该项目位于内蒙古自治区鄂尔多斯市乌审旗&#xff0c;项目利用中天合创聚乙烯、聚丙烯仓库屋面作为场地建设光伏电站&#xff0c;总装机容量为9.96MWp。 项目投运后&#xff0c;每年可节约标煤3670…...

是否存在路径(FIFOBB算法)

题目描述 一个具有 n 个顶点e条边的无向图&#xff0c;该图顶点的编号依次为0到n-1且不存在顶点与自身相连的边。请使用FIFOBB算法编写程序&#xff0c;确定是否存在从顶点 source到顶点 destination的路径。 输入 第一行两个整数&#xff0c;分别表示n 和 e 的值&#xff08;1…...

C++:多态机制详解

目录 一. 多态的概念 1.静态多态&#xff08;编译时多态&#xff09; 二.动态多态的定义及实现 1.多态的构成条件 2.虚函数 3.虚函数的重写/覆盖 4.虚函数重写的一些其他问题 1&#xff09;.协变 2&#xff09;.析构函数的重写 5.override 和 final关键字 1&#…...

redis和redission的区别

Redis 和 Redisson 是两个密切相关但又本质不同的技术&#xff0c;它们扮演着完全不同的角色&#xff1a; Redis: 内存数据库/数据结构存储 本质&#xff1a; 它是一个开源的、高性能的、基于内存的 键值存储数据库。它也可以将数据持久化到磁盘。 核心功能&#xff1a; 提供丰…...

消息队列系统设计与实践全解析

文章目录 &#x1f680; 消息队列系统设计与实践全解析&#x1f50d; 一、消息队列选型1.1 业务场景匹配矩阵1.2 吞吐量/延迟/可靠性权衡&#x1f4a1; 权衡决策框架 1.3 运维复杂度评估&#x1f527; 运维成本降低策略 &#x1f3d7;️ 二、典型架构设计2.1 分布式事务最终一致…...

【实施指南】Android客户端HTTPS双向认证实施指南

&#x1f510; 一、所需准备材料 证书文件&#xff08;6类核心文件&#xff09; 类型 格式 作用 Android端要求 CA根证书 .crt/.pem 验证服务器/客户端证书合法性 需预置到Android信任库 服务器证书 .crt 服务器身份证明 客户端需持有以验证服务器 客户端证书 .crt 客户端身份…...

HTTPS证书一年多少钱?

HTTPS证书作为保障网站数据传输安全的重要工具&#xff0c;成为众多网站运营者的必备选择。然而&#xff0c;面对市场上种类繁多的HTTPS证书&#xff0c;其一年费用究竟是多少&#xff0c;又受哪些因素影响呢&#xff1f; 首先&#xff0c;HTTPS证书通常在PinTrust这样的专业平…...

NLP常用工具包

✨做一次按NLP项目常见工具的使用拆解 1. tokenizer from torchtext.data.utils import get_tokenizertokenizer get_tokenizer(basic_english) text_sample "Were going on an adventure! The weather is really nice today." tokens tokenizer(text_sample) p…...