http://www.7klian.com

慢雾:Opyn 合约被黑具体阐明

// 2. Calculate Collateral to pay // 2.1 Payout enough collateral to get (strikePrice * oTokens) amount of collateral uint256 amtCollateralToPay = calculateCollateralToPay( oTokensToExercise, Number(1, 0) );

4、exercise函数挪用 transferCollateral 函数将 USDC 转给函数挪用者(由于 for 轮回挪用两次_exercise函数,transferCollateral 函数也将执行两次)

// 3. Update collateral + oToken balances vault.collateral = vault.collateral.sub(totalCollateralToPay); vault.oTokensIssued = vault.oTokensIssued.sub(oTokensToExercise);
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" );
}

完整的进攻流程如下

require(hasVault(vaultToExerciseFrom), "Vault does not exist");

https://etherscan.io/tx/0xa858463f30a08c6f3410ed456e59277fbe62ff14225754d2bb0b4f6a75fdc8ad

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" ); }

// 4.3 Pay out collateral transferCollateral(msg.sender, amtCollateralToPay);

2、进攻合约挪用exercise函数,传入已建设 vault 的地点

4、此时由于此前进攻者建设的 oToken 为 0xa21fe800 (2720000000),及 vault.oTokensIssued 为 2720000000 小于 5440000000,所以将走exercise函数中的 else 逻辑,此时 oTokensToExercise 为 0xa21fe800 (2720000000),则以上代码第 60 行 msg.value == amtUnderlyingToPay 是必定创立的

0xa858463f30a08c6f3410ed456e59277fbe62ff14225754d2bb0b4f6a75fdc8ad

// 1. Check sufficient underlying // 1.1 update underlying balances uint256 amtUnderlyingToPay = underlyingRequiredToExercise( oTokensToExercise ); vault.underlying = vault.underlying.add(amtUnderlyingToPay);

3、在代码第 14、16、21 行对传入的 oTokensToExercise 值举办了查抄,在上图 OKO 欣赏器中我们可以看到进攻者传入了 0x1443fd000,这显然是可以通过查抄的

3、进攻者挪用exercise传入 oTokensToExercise 为 0x1443fd000 (5440000000),msg.value 为 272ETH,vaultsToExerciseFrom 别离为以上两个地点

进攻生意业务(其一)

2020 年 8 月 5 日,Opyn 合约遭遇黑客进攻。慢雾安详团队在收到情报后对本次进攻事件举办了全面的阐明,下面为各人就这次进攻事件展开详细的技能阐明。

emit Exercise( amtUnderlyingToPay, amtCollateralToPay, msg.sender, vaultToExerciseFrom );

进攻细节

可以看到exercise函数答允传入多个 vaultsToExerciseFrom,,然后通过 for 轮回挪用_exercise函数对各个 vaultsToExerciseFrom 举办处理惩罚,此刻我们切入_exercise函数举办详细的阐明

// 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);

2、进攻者传入的 vaultToExerciseFrom 别离为:

3、通过exercise函数中 for 轮回逻辑执行挪用两次_exercise函数

5、进攻合约挪用 removeUnderlying 函数将此前传入的 ETH 转出

4、接下来在代码第 28 行计较需要耗损的 ETH 数量

郑重声明:本文版权归原作者所有,转载文章仅为传播更多信息之目的,如作者信息标记有误,请第一时间联系我们修改或删除,多谢。