0xe7870231992Ab4b1A01814FA0A599115FE94203f
4、exercise函数挪用 transferCollateral 函数将 USDC 转给函数挪用者(由于 for 轮回挪用两次_exercise函数,transferCollateral 函数也将执行两次)
进攻合约地点
function exercise( uint256 oTokensToExercise, address payable[] memory vaultsToExerciseFrom) public payable { for (uint256 i = 0; i < vaultsToExerciseFrom.length; i++) { address payable vaultOwner = vaultsToExerciseFrom[i]; require( hasVault(vaultOwner), "Cannot exercise from a vault that doesn't exist" ); Vault storage vault = vaults[vaultOwner]; if (oTokensToExercise == 0) { return; } else if (vault.oTokensIssued >= oTokensToExercise) { _exercise(oTokensToExercise, vaultOwner); return; } else { oTokensToExercise = oTokensToExercise.sub(vault.oTokensIssued); _exercise(vault.oTokensIssued, vaultOwner); } } require( oTokensToExercise == 0, "Specified vaults have insufficient collateral" ); }
发起如下:
要害点在于 oToken 合约的exercise函数,从上图中可以看出在exercise函数中通过挪用两次 transfer 将 USDC 发送给进攻者合约,接下来我们切入exercise函数举办详细的阐明
2020 年 8 月 5 日,Opyn 合约遭遇黑客进攻。慢雾安详团队在收到情报后对本次进攻事件举办了全面的阐明,下面为各人就这次进攻事件展开详细的技能阐明。
可以看到exercise函数答允传入多个 vaultsToExerciseFrom,然后通过 for 轮回挪用_exercise函数对各个 vaultsToExerciseFrom 举办处理惩罚,此刻我们切入_exercise函数举办详细的阐明
function _exercise(
uint256 oTokensToExercise,
address payable vaultToExerciseFrom) internal {
// 1. before exercise window: revert
require(
isExerciseWindow(),
"Can't exercise outside of the exercise window"
);
require(hasVault(vaultToExerciseFrom), "Vault does not exist");
Vault storage vault = vaults[vaultToExerciseFrom];
require(oTokensToExercise > 0, "Can't exercise 0 oTokens");
// Check correct amount of oTokens passed in)
require(
oTokensToExercise <= vault.oTokensIssued,
"Can't exercise more oTokens than the owner has"
);
// Ensure person calling has enough oTokens
require(
balanceOf(msg.sender) >= oTokensToExercise,
"Not enough oTokens"
);
// 1. Check sufficient underlying
// 1.1 update underlying balances
uint256 amtUnderlyingToPay = underlyingRequiredToExercise(
oTokensToExercise
);
vault.underlying = vault.underlying.add(amtUnderlyingToPay);
// 2. Calculate Collateral to pay
// 2.1 Payout enough collateral to get (strikePrice * oTokens) amount of collateral
uint256 amtCollateralToPay = calculateCollateralToPay(
oTokensToExercise,
Number(1, 0)
);
// 2.2 Take a small fee on every exercise
uint256 amtFee = calculateCollateralToPay(
oTokensToExercise,
transactionFee
);
totalFee = totalFee.add(amtFee);
uint256 totalCollateralToPay = amtCollateralToPay.add(amtFee);
require(
totalCollateralToPay <= vault.collateral,
"Vault underwater, can't exercise"
);
// 3. Update collateral + oToken balances
vault.collateral = vault.collateral.sub(totalCollateralToPay);
vault.oTokensIssued = vault.oTokensIssued.sub(oTokensToExercise);
// 4. Transfer in underlying, burn oTokens + pay out collateral
// 4.1 Transfer in underlying
if (isETH(underlying)) {
require(msg.value == amtUnderlyingToPay, "Incorrect msg.value");
} else {
require(
underlying.transferFrom(
msg.sender,
address(this),
amtUnderlyingToPay
),
"Could not transfer in tokens"
);
}
// 4.2 burn oTokens
_burn(msg.sender, oTokensToExercise);
// 4.3 Pay out collateral
transferCollateral(msg.sender, amtCollateralToPay);
emit Exercise(
amtUnderlyingToPay,
amtCollateralToPay,
msg.sender,
vaultToExerciseFrom
);
}
2、项目方可以在项目初期或未完成多次严谨安详审计之前添加合约暂停成果与可进级模子,制止在产生黑天鹅事件时无法有效的担保剩余资金安详。
1、进攻者利用合约先挪用 Opyn 合约的 createERC20CollateralOption 函数建设 oToken
0x951D51bAeFb72319d9FBE941E1615938d89ABfe2
3、进攻者挪用
3、通过exercise函数中 for 轮回逻辑执行挪用两次_exercise函数
6、最终进攻者拿回了此前投入的 ETH 以及特另外 USDC
2、进攻合约挪用exercise函数,传入已建设 vault 的地点
通过查察内联生意业务可以看到进攻者仅利用 272ETH 最终获得 467ETH
Opyn 合约地点
0xa858463f30a08c6f3410ed456e59277fbe62ff14225754d2bb0b4f6a75fdc8ad
1、在代码第 6 行首先查抄了此刻是否在保险期限内,这自然是必定的
2、在代码第 11 行则对 vaultToExerciseFrom 是否建设了 vault 举办查抄,留意这里只是查抄了是否有建设 vault
3、在代码第 14、16、21 行对传入的 oTokensToExercise 值举办了查抄,在上图 OKO 欣赏器中我们可以看到进攻者传入了 0x1443fd000,这显然是可以通过查抄的
4、接下来在代码第 28 行计较需要耗损的 ETH 数量
5、在代码第 35、41 行计较需要付出的数量与手续费
6、接下来在代码第 59 行对 underlying 是否是 ETH 地点举办判定,而 underlying 在上面代码第 31 行举办了赋值,由于 isETH 为 true, 因此将会进入 if 逻辑而不会走 else 逻辑,在 if 逻辑中 amtUnderlyingToPay 与 msg.value 都是用户可控的
7、随后对 oTokensToExercise 举办了燃烧,并挪用 transferCollateral 函数将 USDC 转给
此次进攻主要是操作了_exercise函数中对 vaultToExerciseFrom 是否建设 vault 的查抄缺陷。此查抄未校验 vaultToExerciseFrom 是否是挪用者本身,,而只是简朴的查抄是否建设了 vault,导致进攻者可以任意传入已建设 vault 的地点来通过查抄。
修复发起
履历证,这两个地点都建设了 vault
5、进攻合约挪用 removeUnderlying 函数将此前传入的 ETH 转出
1、在处理惩罚用户可控的参数时应做好权限判定,限制 vaultToExerciseFrom 需为挪用者本人。
进攻生意业务(其一)
郑重声明:本文版权归原作者所有,转载文章仅为传播更多信息之目的,如作者信息标记有误,请第一时间联系我们修改或删除,多谢。