29760 - [SC - Insight] Enforcing Multiple Rewards During Arbitration B...
Enforcing Multiple Rewards During Arbitration Bug Allows Malicious Whitehats to Grief Vaults Causing Large Unnecessary Gas Fees, Revert Payment to Benevolent Whitehats, and Leave Vault in Arbitration
Submitted on Apr 2nd 2024 at 11:57:11 UTC by @seinsidler for Boost | Immunefi Arbitration
Report ID: #29760
Report type: Smart Contract
Report severity: Insight
Target: https://github.com/immunefi-team/vaults/blob/main/src/Arbitration.sol
Impacts:
Causes Vault to Stay in Arbitration
Griefing (e.g. no profit motive for an attacker, but damage to the users or the protocol)
Description
Immunefi Arbitration Boost Bug Report
Background
Currently, the VaultDelegate::sendReward
function utilizes a call from the whitehat address. VaultDelegate::sendReward
allows the whitehat to create a smart contract to define a receive
function to execute additional code when VaultDelegate::sendReward
is executed. While the current system limits the gas fees each time VaultDelegate::sendReward
is called, the Arbitration::enforceMultipleRewards
allows for an array of whitehats to be paid at a time, utilizing a for
loop of an array of addresses. And if a call exceeds the vault delegate's gas limit, the transaction reverts. This means that the larger the array of addresses passed to this function, the larger the gas fee will be. However, the combination of how VaultDelegate::sendReward
and Arbitration::enforceMultipleRewards
are set up, a whitehat could create a smart contract with a receive
function that reverts Arbitration::enforceMultipleRewards
.
Appropriate gas fees are applied when the transaction is called (up until the malicious whitehat's receive
function), but the other whitehats in the array passed to Arbitration::enforceMultipleRewards
will not be paid out, and the vault stays in arbitration. The size of the gas fees is dependent on the size of the array passed to Arbitration::enforceMultipleRewards
and how far into the array the malicious whitehat's smart contract address is in the array passed.
Scenario
The following scenario describes how the griefing attack would go and the consequences of it:
Malicious whitehat creates and deploys a smart contract that implements the
receive
function that calls a function that would exceed theUNTRUSTED_TARGET_GAS_CAP
.Malcious whitehat submits the address of the deployed smart contract as their address for a bug bounty to a vault.
Vault goes into arbitration.
Arbiter sends multiple rewards with ``` Arbitration::enforceMultipleRewards `` and includes the malicious whitehat's address of their smart contract in the array.
The transaction reverts. Gas is still paid on the transaction, and the gas fee depends on the index of the malicious whitehat's smart contract within the array passed.
No whitehat in the array, including the addresses indexed before the malicious smart contract address, receives payment.
The vault stays in arbitration, limiting the vault's capabilities.
Recommendations
I would recommend not using Arbitration::enforceMultipleReward
at all. Especially in a live mainnet environment, debugging which address is causing the revert can be hard. If multiple whitehats colluded to do this attack, it could take several failed calls of Arbitration::enforceMultipleReward
before removing all malicious addresses in an array. Sending a single reward during the arbitration at a time still allows for a whitehat to make this attack, but the attack is way less effective because the function call does not have the potential of having a hefty gas fee associated, and the arbiter or vault can quickly identify the address causing the revert. If Arbitration::enforceMultipleReward
is still a function necessary for arbitration, it might make sense to have checks on whitehat addresses to prevent them from being other smart contracts. This wouldn't allow any code execution to cause transactions to revert.
Proof of concept
PoC
The following is a PoC of the attack described above. This test was based on testArbSendsMultipleRewardsAndCloses
in Arbitration.t.sol
with slight changes to execute the attack. The first code snippet is the contract of the malicious whitehat that has a receive
function that gets called through the execution of Arbitration::enforceMultipleRewards
(more specifically VaultDelegate::sendReward
within this Arbitration::enforceMultipleRewards
). A malicious whitehat could supply the address of this smart contract when deployed to do this griefing attack. The second code snippet is the test function that demonstrates the attack.
Attack Smart contract (Named Receiver.sol
):
Test demonstrating the attack:
Last updated