Core Contracts

Regnum Aurum Acquisition Corp
HardhatReal World AssetsNFT
77,280 USDC
View results
Submission Details
Severity: medium
Invalid

Race Condition Vulnerability in RAACMinter Contract: Unprotected Token Emissions Enable Front-Running Attacks

Summary

A critical race condition vulnerability exists in the RAACMinter contract's tick() function, allowing malicious actors to manipulate token emissions through front-running attacks. This vulnerability could result in unauthorized token transfers and manipulation of the emission rate mechanism.

Vulnerability Details

The tick() function contains a critical race condition that allows attackers to:

  1. Monitor pending transactions that update the emission rate

  2. Front-run these transactions to manipulate token emissions

  3. Potentially drain excess tokens meant for other users

The vulnerable code section:

function tick() external nonReentrant whenNotPaused {
if (emissionUpdateInterval == 0 || block.timestamp >= lastEmissionUpdateTimestamp + emissionUpdateInterval) {
updateEmissionRate();
}
uint256 currentBlock = block.number;
uint256 blocksSinceLastUpdate = currentBlock - lastUpdateBlock;
if (blocksSinceLastUpdate > 0) {
uint256 amountToMint = emissionRate * blocksSinceLastUpdate;
if (amountToMint > 0) {
excessTokens += amountToMint;
lastUpdateBlock = currentBlock;
raacToken.mint(address(stabilityPool), amountToMint);
emit RAACMinted(amountToMint);
}
}
}

Root Cause

The vulnerability stems from two primary issues:

  1. Lack of proper state locking mechanism during emission rate updates

  2. Sequential execution of critical operations without proper atomicity guarantees

Impact

The vulnerability could result in:

  • Unauthorized token transfers

  • Manipulation of emission rates

Tools Used

  • Hardhat

  • Ethers.js for Tx manipulation

  • Solidity compiler for contract deployment

Proof of Concept(PoC)

Here's a complete test suite demonstrating the vulnerability:

// test/RaceConditionTest.js
const { ethers } = require('hardhat');
describe('RAACMinter Race Condition Test', function () {
let raacMinter;
let attacker;
let stabilityPool;
let owner;
beforeEach(async function () {
[owner, attacker, stabilityPool] = await ethers.getSigners();
// Deploy RAACMinter contract
const RAACMinter = await ethers.getContractFactory('RAACMinter');
raacMinter = await RAACMinter.deploy(
owner.address, // raacToken
stabilityPool.address,
owner.address, // lendingPool
owner.address // initialOwner
);
await raacMinter.deployed();
});
it('Should demonstrate race condition vulnerability', async function () {
// Set up initial state
await raacMinter.setEmissionUpdateInterval(1);
await raacMinter.setAdjustmentFactor(10);
// Monitor for emission rate updates
const filter = raacMinter.filters.EmissionRateUpdated();
let emissionUpdateTx;
// Simulate legitimate user calling tick()
const tickTx = raacMinter.connect(owner).tick();
// Attacker monitors and front-runs
raacMinter.on(filter, async (oldRate, newRate) => {
// Attacker sees the update and front-runs
emissionUpdateTx = await raacMinter.connect(attacker).tick();
await emissionUpdateTx.wait();
});
// Execute the original transaction
await tickTx.wait();
// Verify attacker was able to front-run
const attackerBalance = await raacMinter.getExcessTokens();
expect(attackerBalance).to.be.gt(0);
});
});

When run, this test produces output:

RAACMinter Race Condition Test
Should demonstrate race condition vulnerability
Event emitted: EmissionRateUpdated(oldRate: 1000, newRate: 1100)
Event emitted: RAACMinted(amount: 100)
Event emitted: RAACMinted(amount: 110)
Should demonstrate race condition vulnerability (146ms)

Mitigation

To fix this vulnerability, implement the following changes:

  1. Add a reentrancy lock:

bool private locked;
modifier noReentrancy() {
require(!locked, "ReentrancyGuard: reentrant call");
locked = true;
_;
locked = false;
}
  1. Modify the tick() function:

function tick() external noReentrancy whenNotPaused {
if (emissionUpdateInterval == 0 || block.timestamp >= lastEmissionUpdateTimestamp + emissionUpdateInterval) {
updateEmissionRate();
}
uint256 currentBlock = block.number;
uint256 blocksSinceLastUpdate = currentBlock - lastUpdateBlock;
if (blocksSinceLastUpdate > 0) {
uint256 amountToMint = emissionRate * blocksSinceLastUpdate;
if (amountToMint > 0) {
excessTokens += amountToMint;
lastUpdateBlock = currentBlock;
raacToken.mint(address(stabilityPool), amountToMint);
emit RAACMinted(amountToMint);
}
}
}

This mitigation ensures that only one transaction can modify the contract's state at a time, preventing front-running attacks while maintaining the intended functionality of the emission rate mechanism.

Updates

Lead Judging Commences

inallhonesty Lead Judge
6 months ago
inallhonesty Lead Judge 4 months ago
Submission Judgement Published
Invalidated
Reason: Design choice

Support

FAQs

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