HardhatDeFi
15,000 USDC
View results
Submission Details
Severity: medium
Invalid

Potential for Front-running in Yield Claiming

Summary

The yield claiming functionality might be susceptible to front-running attacks.

Vulnerability Details

In functions like _claimYield on line 332 in AaveDIVAWrapperCore.sol, the contract interacts with external protocols to claim yield for users, but there is no mechanism in place to prevent a malicious actor from front-running the transaction. For example, an attacker could observe a yield claim transaction and submit their transaction with a higher gas price to execute before the legitimate claim, possibly exploiting the claim mechanism.

Impact

An attacker could potentially steal yield or manipulate the protocol's state by exploiting the lack of protection against front-running, resulting in unfair distribution of yield or loss of funds.

Proof of Concept for Potential for Front-running in Yield Claiming

Overview:

The vulnerability arises due to the ability of an attacker to front-run legitimate yield claims by monitoring the mempool and submitting their own transaction with a higher gas fee before the original user's transaction is confirmed. This could allow an attacker to manipulate the system by claiming rewards in a way that benefits them unfairly, potentially impacting other users' expected yield.

Actors:

  • Attacker: A malicious user monitoring pending transactions and front-running reward claims.

  • Victim: A legitimate user attempting to claim their earned yield.

  • Protocol: The smart contract managing the yield distribution.

Working Test Case (PoC):

  1. ** Initial Setup**

    • The protocol has a claimYield() function that allows users to claim their accumulated rewards.

    • The function doesn't implement access control or measures to prevent transaction ordering manipulation (e.g., commit-reveal schemes or delays).

  2. ** Attack Strategy**

    • The attacker monitors the mempool for claimYield() transactions.

    • Before the victim's transaction is confirmed, the attacker submits their own yield claim with a higher gas price, ensuring their transaction is processed first.

    • If there’s a reward cap per block, the attacker's transaction might fully consume the available yield, starving the victim of their rightful rewards.

PoC Implementation (Hardhat Test Case in TypeScript)

import { expect } from "chai";
import { ethers } from "hardhat";
describe("Front-running in Yield Claiming", function () {
let YieldContract: any;
let yieldContract: any;
let owner: any, attacker: any, victim: any;
before(async function () {
[owner, attacker, victim] = await ethers.getSigners();
// Deploying a simple yield contract
const Yield = await ethers.getContractFactory("YieldFarm");
yieldContract = await Yield.deploy();
await yieldContract.deployed();
// Simulate staking by victim
await yieldContract.connect(victim).stake({ value: ethers.utils.parseEther("10") });
// Fast forward time to generate yield
await ethers.provider.send("evm_increaseTime", [3600 * 24 * 7]); // 1 week
await ethers.provider.send("evm_mine", []);
});
it("Attacker front-runs the victim's yield claim", async function () {
// Victim attempts to claim yield
const victimTx = await yieldContract.connect(victim).claimYield();
// Attacker submits a transaction with higher gas fees
const attackerTx = await yieldContract.connect(attacker).claimYield({
gasPrice: victimTx.gasPrice.mul(2) // Higher gas price to prioritize the attacker's transaction
});
// Wait for both transactions to be mined
await attackerTx.wait();
await victimTx.wait();
// Check balances
const attackerBalance = await ethers.provider.getBalance(attacker.address);
const victimBalance = await ethers.provider.getBalance(victim.address);
console.log("Attacker Balance after front-run:", ethers.utils.formatEther(attackerBalance));
console.log("Victim Balance after front-run:", ethers.utils.formatEther(victimBalance));
// Validate that the attacker claimed before the victim
expect(attackerBalance).to.be.gt(victimBalance);
});
});

Exploit Scenario

1. Initial State:

  • The victim has yield ready to claim.

  • The attacker is monitoring the mempool.

2. Exploit Steps:

  1. The victim submits a claimYield() transaction.

  2. The attacker detects it in the mempool.

  3. The attacker submits their own claimYield() transaction with a higher gas price.

  4. The attacker’s transaction gets prioritized and executed first.

  5. The victim's claim executes after the attacker, potentially receiving less or no yield if rewards are capped per block.

3. Outcome:

  • The attacker unfairly claims rewards before the victim.

  • If the protocol has a per-block yield cap, the victim might receive reduced or zero rewards.

4 Implications:

  • Users may experience unfair yield distribution.

  • The attacker can repeatedly exploit this mechanism.

  • It erodes user trust in the fairness of the protocol.

Tools Used

Manual code review

Recommendations

Commit-Reveal Scheme:

  • Require users to commit their intention to claim rewards (by submitting a hash of their claim) in one transaction.

  • Users then reveal their claim in a later transaction, preventing front-running.

Randomized Delays:

  • Introduce randomized processing delays or batch processing, making it harder to predict and front-run transactions.

Prioritize Older Claims:

  • Give priority to users based on their stake duration or first-seen transaction rather than gas price.

Use MEV Protection Services (e.g., Flashbots):

  • Allow users to submit private transactions that bypass the public mempool, preventing front-running.

Updates

Lead Judging Commences

bube Lead Judge 10 months ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.

Give us feedback!