背景介绍
2024 年 7 月 12 日,DoughFina 协议遭受了黑客攻击,造成本次攻击的主要原因是 ConnectorDeleverageParaswap
合约没有对输入参数进行检查,且该合约为 DSA
合约的 owner
。攻击者可以构造恶意参数窃取 DSA
合约的资金。
相关合约
- DSA(被攻击地址):0x534a
在 AAVE V3 上质押了 596 WETH,借出了 938566 USDC - 攻击合约:0x11a8
- ConnectorDeleverageParaswap:0x9f54e8eaa9658316bb8006e03fff1cb191aafbe6
通过闪电贷协助 DSA 降低在 AAVE V3 上的杠杆。
攻击交易分析
这个章节我们先尝试从 trace 来定位漏洞的位置。
通过对这笔交易进行一个大概的观察,如下图所示,攻击者在发起攻击之前,先利用闪电贷的资金帮 0x534a 归还了他的借款 938566 USDC ,其他部分都是常规的操作,其中需要深入调查的就是 0x11a8
调用 ConnectorDeleverageParaswap.flashloanReq
函数的过程。
![](https://img2024.cnblogs.com/blog/1483609/202407/1483609-20240715211640568-537349740.png)
ConnectorDeleverageParaswap.flashloanReq
函数
![](https://img2024.cnblogs.com/blog/1483609/202407/1483609-20240715212103391-1270478236.png)
我们看到在 ConnectorDeleverageParaswap.flashloanReq
函数中,会调用 POOL.flashLoan
POOL.flashLoan
执行一个常规的闪电贷流程
- ConnectorDeleverageParaswap 合约从闪电贷获取 5 USDC
- 调用 ConnectorDeleverageParaswap.executeOperation 函数
- 归还 5.025 USDC
- handleRepayment
![](https://img2024.cnblogs.com/blog/1483609/202407/1483609-20240715212208401-1141477079.png)
继续跟进到 ConnectorDeleverageParaswap.executeOperation
函数,可以看到它对传入的参数进行解析后直接调用 deloopInOneOrMultipleTransactions
函数
![](https://img2024.cnblogs.com/blog/1483609/202407/1483609-20240715212312194-706381635.png)
对 deloopInOneOrMultipleTransactions
函数进行分析
solidity
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 把调用的四个函数混在一起了不好分辨。
![](https://img2024.cnblogs.com/blog/1483609/202407/1483609-20240715212831721-991417625.png)
根据转移的金额确定攻击发生的位置,猜测这笔 WETH
的转账与漏洞的利用有关。
![](https://img2024.cnblogs.com/blog/1483609/202407/1483609-20240715212904555-1394096309.png)
所以通过单步调试的方式来检查发生了什么。
执行到 ConnectorDeleverageParaswap.deloopAllCollaterals
函数中时,对传入的 multiTokenSwapData
参数进行解析,得到对应的参数: flashloanVars.srcToken, flashloanVars.destToken, flashloanVars.srcAmount, flashloanVars.destAmount, flashloanVars.paraSwapContract, flashloanVars.tokenTransferProxy, flashloanVars.paraswapCallData
其中 paraSwapContract
(out4)对应的地址为 WETH 的合约地址(0xc02a),而不是进行 swap 的合约地址。
![](https://img2024.cnblogs.com/blog/1483609/202407/1483609-20240715212954062-180635814.png)
当执行到 paraSwapContract.call
部分的函数时,由于 paraSwapContract
的地址已被替换为 WETH9
的地址,且 paraswapCallData
为攻击者构造的转账 calldata,所以实际执行的是 WETH
的转账操作
solidity
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 函数对四个函数的调用情况
-
repayAllDebtAssetsWithFlashLoan
替 0x11a8 账户归还 5 USDC 的借款
-
extractAllCollaterals
传入空数组,跳过这函数的逻辑
-
deloopAllCollaterals
由于没有进行参数检查,攻击者在这个函数中构造了两个恶意的调用来获利。
-
第一个 for 循环:调用 0x534a.executeAction 把 5 USDC 兑换成 596 WETH,并且 approve 给 ConnectorDeleverageParaswap 合约
-
第二个 for 循环:把 596 WETH 转移到攻击者控制的 0x11a8 地址
-
-
repayFlashloansAndTransferToTreasury
提供 0.9 USDC 给 0x11a8 的 AAVE 账户
攻击流程分析
本次攻击是围绕降低 0x11a8
账户在 AAVE V3 上的杠杆而展开的,由于这个函数没有对传入的参数进行检查,所以攻击者构造了恶意的 multiTokenSwapData
参数在 deloopAllCollaterals
函数中对 0x534a
的 AVVE 资金进行转移。
攻击者构造恶意的 data
参数,解析出恶意的 multiTokenSwapData
参数
恶意的 multiTokenSwapData
参数传入到 deloopAllCollaterals
函数中
恶意的 multiTokenSwapData
参数解析出恶意的 paraSwapContract
和 paraswapCallData
,导致了任意执行。
后记
第一次分析这个类型的攻击事件,感觉攻击事件还是得多分析,多积累积累经验,扩展自己的视野。好好看好好学吧,之前对项目类型的接触和理解上都有很大的局限,还是需要多接触一下目前经典的、热门的项目有利于提高自己的水平,跟上市场的步伐。继续干呗,这事儿只能靠慢慢积累起来的。