背景介绍

2024 年 7 月 12 日,DoughFina 协议遭受了黑客攻击,造成本次攻击的主要原因是 ConnectorDeleverageParaswap 合约没有对输入参数进行检查,且该合约为 DSA 合约的 owner。攻击者可以构造恶意参数窃取 DSA 合约的资金。

攻击交易

https://app.blocksec.com/explorer/tx/eth/0x92cdcc732eebf47200ea56123716e337f6ef7d5ad714a2295794fdc6031ebb2e

相关合约

  • DSA(被攻击地址):0x534a

    在 AAVE V3 上质押了 596 WETH,借出了 938566 USDC
  • 攻击合约:0x11a8
  • ConnectorDeleverageParaswap:0x9f54e8eaa9658316bb8006e03fff1cb191aafbe6

    通过闪电贷协助 DSA 降低在 AAVE V3 上的杠杆。

攻击交易分析

这个章节我们先尝试从 trace 来定位漏洞的位置。

通过对这笔交易进行一个大概的观察,如下图所示,攻击者在发起攻击之前,先利用闪电贷的资金帮 0x534a 归还了他的借款 938566 USDC,其他部分都是常规的操作,其中需要深入调查的就是 0x11a8 调用 ConnectorDeleverageParaswap.flashloanReq 函数的过程。

ConnectorDeleverageParaswap.flashloanReq 函数

我们看到在 ConnectorDeleverageParaswap.flashloanReq 函数中,会调用 POOL.flashLoan

POOL.flashLoan 执行一个常规的闪电贷流程

  1. ConnectorDeleverageParaswap 合约从闪电贷获取 5 USDC
  2. 调用 ConnectorDeleverageParaswap.executeOperation 函数
  3. 归还 5.025 USDC
  4. handleRepayment

继续跟进到 ConnectorDeleverageParaswap.executeOperation 函数,可以看到它对传入的参数进行解析后直接调用 deloopInOneOrMultipleTransactions 函数

deloopInOneOrMultipleTransactions 函数进行分析

function deloopInOneOrMultipleTransactions(bool opt, address _dsaAddress, address[] memory assets, uint256[] memory amounts, uint256[] memory premiums, address[] memory collateralTokens, uint256[] memory collateralAmounts, bytes[] memory multiTokenSwapData) private {
// Repay all flashloan assets or withdraw all collaterals
// 使用闪电贷获得的资金来偿还用户在 Aave 上的债务。
repayAllDebtAssetsWithFlashLoan(opt, _dsaAddress, assets, amounts); // Extract all collaterals
// 从用户的 DSA 中提取指定数量的抵押品。
extractAllCollaterals(_dsaAddress, collateralTokens, collateralAmounts); // Deloop all collaterals
// 使用 Paraswap 将提取的抵押品换成债务代币
deloopAllCollaterals(multiTokenSwapData); // Repay all flashloan assets or withdraw all collaterals
// 偿还闪电贷,然后将剩余的资金存回 Aave 或转移到金库
repayFlashloansAndTransferToTreasury(opt, _dsaAddress, assets, amounts, premiums);
}

不过在查看 ConnectorDeleverageParaswap.executeOperation 函数的执行 trace 时,发现 Phalcon 把调用的四个函数混在一起了不好分辨。

根据转移的金额确定攻击发生的位置,猜测这笔 WETH 的转账与漏洞的利用有关。

所以通过单步调试的方式来检查发生了什么。

执行到 ConnectorDeleverageParaswap.deloopAllCollaterals 函数中时,对传入的 multiTokenSwapData 参数进行解析,得到对应的参数: flashloanVars.srcToken, flashloanVars.destToken, flashloanVars.srcAmount, flashloanVars.destAmount, flashloanVars.paraSwapContract, flashloanVars.tokenTransferProxy, flashloanVars.paraswapCallData

其中 paraSwapContract(out4)对应的地址为 WETH 的合约地址(0xc02a),而不是进行 swap 的合约地址。

当执行到 paraSwapContract.call 部分的函数时,由于 paraSwapContract 的地址已被替换为 WETH9 的地址,且 paraswapCallData 为攻击者构造的转账 calldata,所以实际执行的是 WETH 的转账操作

function deloopAllCollaterals(bytes[] memory multiTokenSwapData) private {
FlashloanVars memory flashloanVars; for (uint i = 0; i < multiTokenSwapData.length;) {
// Deloop
(flashloanVars.srcToken, flashloanVars.destToken, flashloanVars.srcAmount, flashloanVars.destAmount, flashloanVars.paraSwapContract, flashloanVars.tokenTransferProxy, flashloanVars.paraswapCallData) = _getParaswapData(multiTokenSwapData[i]); // using ParaSwap
IERC20(flashloanVars.srcToken).safeIncreaseAllowance(flashloanVars.tokenTransferProxy, flashloanVars.srcAmount);
(flashloanVars.sent, ) = flashloanVars.paraSwapContract.call(flashloanVars.paraswapCallData);
if (!flashloanVars.sent) revert CustomError("ParaSwap deloop failed"); unchecked { i++; }
}
}

deloopInOneOrMultipleTransactions 函数分析

已经定位到了漏洞发生的位置,接下来就根据 trace 分析 deloopInOneOrMultipleTransactions 函数对四个函数的调用情况

  1. repayAllDebtAssetsWithFlashLoan

    替 0x11a8 账户归还 5 USDC 的借款

  2. extractAllCollaterals

    传入空数组,跳过这函数的逻辑

  3. deloopAllCollaterals

    由于没有进行参数检查,攻击者在这个函数中构造了两个恶意的调用来获利。

    1. 第一个 for 循环:调用 0x534a.executeAction 把 5 USDC 兑换成 596 WETH,并且 approve 给 ConnectorDeleverageParaswap 合约

    2. 第二个 for 循环:把 596 WETH 转移到攻击者控制的 0x11a8 地址

  4. repayFlashloansAndTransferToTreasury

    提供 0.9 USDC 给 0x11a8 的 AAVE 账户

攻击流程分析

本次攻击是围绕降低 0x11a8 账户在 AAVE V3 上的杠杆而展开的,由于这个函数没有对传入的参数进行检查,所以攻击者构造了恶意的 multiTokenSwapData 参数在 deloopAllCollaterals 函数中对 0x534a 的 AVVE 资金进行转移。

攻击者构造恶意的 data 参数,解析出恶意的 multiTokenSwapData 参数

恶意的 multiTokenSwapData 参数传入到 deloopAllCollaterals 函数中

恶意的 multiTokenSwapData 参数解析出恶意的 paraSwapContractparaswapCallData,导致了任意执行。

后记

第一次分析这个类型的攻击事件,感觉攻击事件还是得多分析,多积累积累经验,扩展自己的视野。好好看好好学吧,之前对项目类型的接触和理解上都有很大的局限,还是需要多接触一下目前经典的、热门的项目有利于提高自己的水平,跟上市场的步伐。继续干呗,这事儿只能靠慢慢积累起来的。

【漏洞分析】DoughFina 攻击事件分析:不做任何参数检查的去杠杆合约的更多相关文章

  1. 孟加拉央行SWIFT攻击事件恶意木马分析(转)

    第一章 分析概述 该恶意木马样本为运行于winodws平台上的PE文件(名称:evtdiag.exe).文件大小为64KB,编译时间是2016年2月5日. 经分析,该样本为定制的攻击SWIFT客户端程 ...

  2. 【漏洞复现】Paraluni 安全事件分析及复现

    Paraluni 被黑分析 前言 Paraluni (平行宇宙)是新加坡 Parallel Universe 基金会发布的一个 基于币安智能链的 DeFi 项目,更多相关内容见此处.在 2022 年 ...

  3. 【漏洞分析】KaoyaSwap 安全事件分析

    相关信息 KaoyaSwap 是 BSC 链上的一个自动做市商 AMM.然后,现在他们的官网 https://www.kaoyaswap.com/ 已经打不开了(如果我打开方式没错的话).所以就直接进 ...

  4. Log4shell漏洞研究及其挖矿案例分析

    本文首发于云影实验室,为本人创作,现转载到个人博客,记录一下. 原文链接:https://mp.weixin.qq.com/s/O2xHr2OEHiga-qTnbWTxQg Apache Log4j是 ...

  5. ENode框架Conference案例分析系列之 - 事件溯源如何处理重构问题

    前言 本文可能对大多数不太了解ENode的朋友来说,理解起来比较费劲,这篇文章主要讲思路,而不是一上来就讲结果.我写文章,总是希望能把自己的思考过程尽量能表达出来,能让大家知道每一个设计背后的思考的东 ...

  6. 从源码的角度分析ViewGruop的事件分发

    从源码的角度分析ViewGruop的事件分发. 首先我们来探讨一下,什么是ViewGroup?它和普通的View有什么区别? 顾名思义,ViewGroup就是一组View的集合,它包含很多的子View ...

  7. OneAlert 入门(三)——事件分析

    OneAlert 是国内首个 SaaS 模式的云告警平台,集成国内外主流监控/支撑系统,实现一个平台上集中处理所有 IT 事件,提升 IT 可靠性.有了 OneAlert,你可以更快更合理地为事件划分 ...

  8. 再谈DOMContentLoaded与渲染阻塞—分析html页面事件与资源加载

    浏览器的多线程中,有的线程负责加载资源,有的线程负责执行脚本,有的线程负责渲染界面,有的线程负责轮询.监听用户事件. 这些线程,根据浏览器自身特点以及web标准等等,有的会被浏览器特意的阻塞.两个很明 ...

  9. [Abp 源码分析]九、事件总线

    0.简介 事件总线就是订阅/发布模式的一种实现,本质上事件总线的存在是为了降低耦合而存在的. 从上图可以看到事件由发布者发布到事件总线处理器当中,然后经由事件总线处理器调用订阅者的处理方法,而发布者和 ...

  10. 007_ip统计及攻击ip分析

    线上经常有被扫描的DDoS攻击事件,需要集合日志进行分析,这里有两种方法,分别是通过shell和python的方式. 一.shell '''<1>shell一句命令分析 http://bl ...

随机推荐

  1. java学习之旅(day.14)

    可变字符串 StringBuffer:可变长字符串,运行效率慢,线程安全 StringBuilder:可变长字符串,运行效率快,线程不安全 package com.zhang.lei; //Strin ...

  2. 编译mmdetection3d时,无root权限下为虚拟环境单独创建CUDA版本

    在跑一些深度学习代码的时候,如果需要使用mmdetection3d框架,下载的pytorch的cudatoolkit最好需要和本机的cuda版本是一样的,即输入nvcc -V命令后显示的版本一样. 但 ...

  3. WPF没修改代码出现InitializeComponent报错的解决办法

    问题不在于我们做错了什么,之前还好好的,我们什么都没做,就报错了.这完全不是我们的问题. 我的建议是,直接做绝一点,删除obj和debug文件夹,让他自己重新生成一次

  4. Three加载3D模型贴图

    Three加载3D模型贴图 准备阶段 3D模型 three 库文件 纹理图片 相关资料 官方开发文档: https://threejs.org/docs 官网编辑3D模型:https://threej ...

  5. 从零开始写 Docker(十五)---实现 mydocker run -e 支持环境变量传递

    本文为从零开始写 Docker 系列第十五篇,实现 mydocker run -e, 支持在启动容器时指定环境变量,让容器内运行的程序可以使用外部传递的环境变量. 完整代码见:https://gith ...

  6. 基于Python的性能优化

    一.多线程 在CPU不密集.IO密集的任务下,多线程可以一定程度的提升运行效率. import threading import time import requests def fetch_url( ...

  7. Fastapi获取其他第三方回调

    flask.django获取第三方回调数据,可以用request.data直接获取全部参数.而很多同学不知道fastapi如何获取回调的全部参数,其实可以通过request.body(). 如: @p ...

  8. jquery加载页面时触发事件

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  9. .NET桌面程序混合开发之三:WebView2与JS的深度应用

    在 WebView2 控件中使用 JavaScript 根据需求自由扩展原生应用的能力.本文探讨如何在 WebView2 中使用 JavaScript,并列举如何使用高级 WebView2 特性和功能 ...

  10. 「C++」深度分析C++中i++与++i的区别

    大家好,我是Charzie.在C++编程中,i++和++i是两个常见的自增运算符,用于将变量的值增加1(有时与i+=1效果一样).然而,虽然它们的功能看似相似,但在实际使用中却存在显著的区别.本博客将 ...