Defi安全--Orion Protocol攻击事件分析

其它相关文章可见个人主页

1. Orion Protocol攻击事件相关信息

2023年2月2日,在ETH和BSC上的Orion Protocol项目被攻击,这里以ETH上攻击为例:

  • 攻击合约地址:Attacker Contract Address | Etherscan
  • 攻击者地址:Orion Protocol Exploiter 2 | Address
  • 攻击交易:Ethereum Transaction Hash (Txhash) Details | Etherscan
  • Phalcon调用序列分析:0xa6f63fcb6bec881886 | Phalcon Explorer (blocksec.com)

2. Orion Protocol攻击事件分析

攻击流程详解

Eth上的攻击交易Ethereum Transaction Hash (Txhash) Details | Etherscan

从中我们可以看出,input data为单纯的函数签名,没有参数,只是调用了一个攻击函数

image-20240116111931103

查看对应的phalcon调用序列:

image-20240116112006459

  1. 先进行了一系列基础操作,对Orion Protocol项目合约进行一系列的代币授权approve()操作,如USDT和USDC等。

image-20240116112824996

  1. 随后我们可以看到攻击者调用了Orion Protocol的depositAsset函数,看一下该函数的源码:
    function depositAsset(address assetAddress, uint112 amount) external {uint256 actualAmount = IERC20(assetAddress).balanceOf(address(this));IERC20(assetAddress).safeTransferFrom(msg.sender,address(this),uint256(amount));actualAmount = IERC20(assetAddress).balanceOf(address(this)) - actualAmount;generalDeposit(assetAddress, uint112(actualAmount));}
  1. 攻击者向orion Protocol合约转入对应数量的USDC,将该合约转账前后的代币余额,作为用户存款的数量,并调用generateDeposit函数,这一步USDC的存款是为后续的攻击做准备。
  2. 攻击者调用Uniswap V2: USDT的闪电贷函数,借出200多万个USDT

image-20240116113406567

  1. 调用uniswapv2的闪电贷函数,借贷对应的USDT,乐观转账,先将对应的USDT转账给了攻击者,后回调攻击者的uniswapV2Call函数
  2. 回调函数中,因为攻击者先前存入了USDC,现在攻击者调用了orion Protocol项目ExchangeWithAtomic合约中的一个函数swapThroughOrionPool,orion Protocol提供的代币交换函数,代币兑换路径为[USDC, ATK, USDT],其中ATK为攻击者提前创建的恶意代币,将USDC兑换成USDT
  3. 随后调用LibPool的doSwapThroughOrionPool的函数,再调用PoolFunctionality 合约中的doSwapThroughOrionPool函数
    function swapThroughOrionPool(uint112     amount_spend,uint112     amount_receive,address[]   calldata path,bool        is_exact_spend) public payable nonReentrant {bool isCheckPosition = LibPool.doSwapThroughOrionPool(IPoolFunctionality.SwapData({amount_spend: amount_spend,amount_receive: amount_receive,is_exact_spend: is_exact_spend,supportingFee: false,path: path,orionpool_router: _orionpoolRouter,isInContractTrade: false,isSentETHEnough: false,isFromWallet: false,asset_spend: address(0)}),assetBalances, liabilities);
  1. 进一步调用PoolFunctionality 合约中的 doSwapThroughOrionPool 函数,仔细看一下函数源码,该函数进一步调用了_doSwapTokens()函数

  2. 上述代码中_doSwapTokens()函数时进行相应的输入,输出代币数量的计算,跟进该函数的实现

    function _doSwapTokens(InternalSwapData memory swapData) internal returns (uint256 amountIn, uint256 amountOut) {bool isLastWETH = swapData.path[swapData.path.length - 1] == WETH;address toAuto = isLastWETH || swapData.curFactoryType == FactoryType.CURVE ? address(this) : swapData.to;uint256[] memory amounts;if (!swapData.supportingFee) {if (swapData.isExactIn) {amounts = OrionMultiPoolLibrary.getAmountsOut(swapData.curFactory,swapData.curFactoryType,swapData.amountIn,swapData.path);require(amounts[amounts.length - 1] >= swapData.amountOut, "PoolFunctionality: IOA");} else {amounts = OrionMultiPoolLibrary.getAmountsIn(swapData.curFactory,swapData.curFactoryType,swapData.amountOut,swapData.path);require(amounts[0] <= swapData.amountIn, "PoolFunctionality: EIA");}} else {amounts = new uint256[](1);amounts[0] = swapData.amountIn;}amountIn = amounts[0];{uint256 curBalance;address initialTransferSource = swapData.curFactoryType == FactoryType.CURVE ? address(this): OrionMultiPoolLibrary.pairFor(swapData.curFactory, swapData.path[0], swapData.path[1]);if (swapData.supportingFee) curBalance = IERC20(swapData.path[0]).balanceOf(initialTransferSource);IPoolSwapCallback(msg.sender).safeAutoTransferFrom(swapData.asset_spend,swapData.user,initialTransferSource,amountIn);if (swapData.supportingFee) amounts[0] = IERC20(swapData.path[0]).balanceOf(initialTransferSource) - curBalance;}{uint256 curBalance = IERC20(swapData.path[swapData.path.length - 1]).balanceOf(toAuto);//计算转账前的余额if (swapData.curFactoryType == FactoryType.CURVE) {_swapCurve(swapData.curFactory, amounts, swapData.path, swapData.supportingFee);} else if (swapData.curFactoryType == FactoryType.UNISWAPLIKE) {//这里的swap函数完成相应的代币兑换_swap(swapData.curFactory, amounts, swapData.path, toAuto, swapData.supportingFee);}//将账户余额与转账前余额相减,得到新增的金额amountOut = IERC20(swapData.path[swapData.path.length - 1]).balanceOf(toAuto) - curBalance;}require(swapData.amountIn == 0 || swapData.amountOut == 0 ||amountIn * 1e18 / swapData.amountIn <= amountOut * 1e18 / swapData.amountOut,"PoolFunctionality: OOS");if (isLastWETH) {SafeTransferHelper.safeAutoTransferTo(WETH,address(0),swapData.to,amountOut);} else if (swapData.curFactoryType == FactoryType.CURVE) {IERC20(swapData.path[swapData.path.length - 1]).safeTransfer(swapData.to, amountOut);}emit OrionPoolSwap(tx.origin,convertFromWETH(swapData.path[0]),convertFromWETH(swapData.path[swapData.path.length - 1]),swapData.amountIn,amountIn,swapData.amountOut,amountOut,swapData.curFactory);}
  1. 这里进行相应的代币兑换,之前的兑换path为[USDC, ATK, USDT],这里通过PoolFunctionality合约中的_swap()完成相应的兑换,跟进 _swap()函数的源码
    function _swap(address curFactory,uint256[] memory amounts,address[] memory path,address _to,bool supportingFee) internal {for (uint256 i; i < path.length - 1; ++i) {(address input, address output) = (path[i], path[i + 1]);IOrionPoolV2Pair pair = IOrionPoolV2Pair(OrionMultiPoolLibrary.pairFor(curFactory, input, output));(address token0, ) = OrionMultiPoolLibrary.sortTokens(input, output);uint256 amountOut;if (supportingFee) {(uint reserve0, uint reserve1,) = pair.getReserves();(uint reserveInput, uint reserveOutput) = input == token0 ? (reserve0, reserve1) : (reserve1, reserve0);uint256 amountIn = IERC20(input).balanceOf(address(pair)).sub(reserveInput);amountOut = OrionMultiPoolLibrary.getAmountOutUv2(amountIn, reserveInput, reserveOutput);} else {amountOut = amounts[i + 1];}(uint256 amount0Out, uint256 amount1Out) = input == token0 ? (uint256(0), amountOut) : (amountOut, uint256(0));address to = i < path.length - 2 ? OrionMultiPoolLibrary.pairFor(curFactory, output, path[i + 2]) : _to;pair.swap(amount0Out, amount1Out, to, new bytes(0));}}

image-20240116142931330

  1. path序列中的[USDC, ATK, USDT],每两个代币对之间存在一个pair合约,即USDC转到ATK,ATK转到对应的USDT,实现对应的代币兑换,攻击者创建的pair对合约,这里通过相应的计算金融模型,得到对应的转账金额,调用pair合约中的swap函数,实现相应的代币转移。

    image-20240116150016939

    image-20240116151407776

  2. 由于pair对中的swap函数,进行相应的转账,需要调用ATK代币的转账函数,ATK是攻击者部署的恶意代币,攻击者可控,攻击者这里调用自身的deposit()函数,调用ExchangeWithAtomic合约的depositAsset函数,并将闪电贷得到的200多万USDT全部转进Orion Protocol的depositAsset()函数中

  3. 这时攻击者在ExchangeWithAtomic 合约中USDT的存款被记账为了200多万,原来ExchangeWithAtomic 合约的余额为200多万,两者数值相近(攻击者设计的)

  4. 而通过swapThroughOrionPool函数中攻击者USDC兑换出多少的USDT最终是通过ExchangeWithAtomic 合约兑换前后的USDT余额计算的,相当于存入的200万USDT被认为是USDC兑换出来的,最后通过creditUserAssets 函数来更新ExchangeWithAtomic 维护的adress-balance的账本,攻击者被认为是存入了200+200万

image-20240116151533445

  1. 攻击者进行相应的闪电贷还款,归还借出的200多万,获利200多万

image-20240116151634701

  1. 调用闪电贷,借出WETH,归还USDT,实现对应的套利离场

攻击事件发生的主要原因

  • doswapThroughOrionPool 函数,兑换路径攻击者可控,代币类型攻击者可控(恶意代币)
  • 兑换后更新账本的记账方式不正确,利用前后余额计算(×)
  • 合约兑换功能的函数没有做重入保护

3. 分析Orion Protocol攻击事件所需信息

  1. 最关键的一点,重入的发生回调不在这个攻击合约之中,在攻击者创建的恶意代币合约之中(可能是这个案例的特殊情况)
  2. 普适的一点:触发恶意合约回调的功能,是在经过5次外部函数调用后,才最终调用到攻击者的恶意代币合约中的函数,在我们的工具中是无法获得这样的调用过程,全路径覆盖不太现实。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/413014.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

2018年认证杯SPSSPRO杯数学建模A题(第一阶段)海豚与沙丁鱼全过程文档及程序

2018年认证杯SPSSPRO杯数学建模 探究海豚猎捕时沙丁鱼群的躲避运动模型 A题 海豚与沙丁鱼 原题再现&#xff1a; 沙丁鱼以聚成大群的方式来对抗海豚的捕食。由于水下光线很暗&#xff0c;所以在距离较远时&#xff0c;海豚只能使用回声定位方法来判断鱼群的整体位置&#xf…

Qt编程之仿gnome-terminal终端样式 +颜色文字显示

Qt仿linux 终端样式 颜色文字 1.说再多废话不如直接show code2.实现效果 本文采用QTextBrowser作为文本显示窗口&#xff0c;进行文本的显示。本文实例实现的效果并没有终端的输入效果&#xff0c;这里只是提供一些仿终端样式思路。 1.说再多废话不如直接show code 1.ui文件…

FFmpeg之AVFormat

文章目录 一、概述二、解封装流程三、重要结构体3.1、AVFormatContext3.2、AVInputFormat3.3、AVOutputFormat3.4、AVStream 四、重要函数分析4.1、avformat_alloc_context4.2、avformat_open_input4.2.1、init_input4.2.2、av_probe_input_format2 4.3、avformat_find_stream_…

Unity3D和three.js的比较

一、Unity3D和three.js简介 Unity3D是一款跨平台的游戏引擎,可以用于开发2D和3D游戏。它提供了一个可视化的开发环境,包含了强大的编辑器和工具,使开发者可以方便地创建游戏场景、添加物体、设置物理效果、编写脚本等。Unity3D支持多种平台,包括PC、移动设备、主机等,可以…

Windows10系统下的Java环境变量配置

Windows10系统下的Java环境变量配置 1.准备java的jdk2.安装JDK--全部选择下一步3.如果安装的jdk环境没有自动配置,需要以下操作进行手动配置4.点击环境变量5.在系统变量的情况下进行新建6.这个路径是默认安装的是jdk默认安装配置的环境变量:7.java在安装的时候相当于把上面命令…

自建服务器如何备案?

随着互联网的普及和发展&#xff0c;越来越多的人开始考虑自建服务器。然而&#xff0c;在中国大陆地区&#xff0c;自建服务器需要进行备案。本文将介绍自建服务器备案的流程、所需材料以及注意事项。 一、备案流程 确定备案地区 根据《中华人民共和国计算机信息网络国际联网…

rabbitmq-java基础详解

一、rabbitmq是什么&#xff1f; 1、MQ定义 MQ&#xff08;Message Queue&#xff09;消息队列 主要解决&#xff1a;异步处理、应用解耦、流量削峰等问题&#xff0c;是分布式系统的重要组件&#xff0c;从而实现高性能&#xff0c;高可用&#xff0c;可伸缩和最终一致性的架…

【MATLAB随笔】GUI编程(未完结)

文章目录 一、创建GUI图窗1.1 使用figure 函数1.11 窗口标识1.12 窗口外观1.13 位置和大小1.14 绘图1.15 交互性 1.2 使用uifigure函数 二、 添加GUI控件2.1 uicontrol&#xff08;适用于figure&#xff09;2.11 控件类型2.12 文本和样式2.13 字体待续 一、创建GUI图窗 跟很多…

过滤器、拦截器、AOP、ControllerAdvcie执行顺序对比

过滤器Filter 简介 来自J2EE中的Servlet技术实现原理&#xff1a;基于servlet的函数回调实现只可以获取到请求中的request和response&#xff0c;无法获取到响应方法的信息可以拦截所有请求支持使用xml配置和注解配置应用场景&#xff1a;权限认证、敏感词检测、访问日志记录…

数组练习 Leetcode 566.重塑矩阵

在 MATLAB 中&#xff0c;有一个非常有用的函数 reshape &#xff0c;它可以将一个 m x n 矩阵重塑为另一个大小不同&#xff08;r x c&#xff09;的新矩阵&#xff0c;但保留其原始数据。 给你一个由二维数组 mat 表示的 m x n 矩阵&#xff0c;以及两个正整数 r 和 c &#…

大模型关键技术:上下文学习、思维链、RLHF、参数微调、并行训练、旋转位置编码、模型加速、大模型注意力机制优化、永久记忆、LangChain、知识图谱、多模态

大模型关键技术 大模型综述上下文学习思维链 CoT奖励建模参数微调并行训练模型加速永久记忆&#xff1a;大模型遗忘LangChain知识图谱多模态大模型系统优化AI 绘图幻觉问题从 GPT1 - GPT4 拆解GPTs 对比主流大模型技术点旋转位置编码层归一化激活函数注意力机制优化 大模型综述…

关于python环境变量相关的配置汇总(venv虚拟环境/conda环境/pip相关)

关于python环境变量相关的配置汇总(venv虚拟环境/conda环境/pip相关) 本文作者&#xff1a; slience_me 文章目录 关于python环境变量相关的配置汇总(venv虚拟环境/conda环境/pip相关)1. python环境配置相关1.1 系统环境1.2 Anaconda环境相关1.2.1 安装1.2.2 查看python环境 1.…