Powerful Honeysuckle Anteater
High
Attacker can use redeemUSDT to steal all funds from treasury, as we can specify the usda and usdt prices
In the redeemUSDT()
function in CDS, we can currently specify both prices for usda and usdt, which makes it possible and very easy to steal funds from the treasury.
redeemUSDT()
has usdaPrice and usdtPrice params, which allows for heavy manipulations, reference:
@>> function redeemUSDT(uint128 usdaAmount, uint64 usdaPrice, uint64 usdtPrice)
external
payable
nonReentrant
whenNotPaused(IMultiSign.Functions(6))
{
burnedUSDaInRedeem = CDSLib.redeemUSDT(
Interfaces(treasury, globalVariables, usda, usdt, borrowing, CDSInterface(address(this))),
burnedUSDaInRedeem,
usdaAmount,
@>> usdaPrice,
@>> usdtPrice
);
// getting options since,the src don't know the dst state
bytes memory _options = OptionsBuilder.newOptions().addExecutorLzReceiveOption(400000, 0);
// calculting fee
MessagingFee memory fee =
globalVariables.quote(IGlobalVariables.FunctionToDo(1), IBorrowing.AssetName(0), _options, false);
// Calling Omnichain send function
globalVariables.send{value: fee.nativeFee}(
IGlobalVariables.FunctionToDo(1), IBorrowing.AssetName(0), fee, _options, msg.sender
);
}
In the redeemUSDT()
function we call CDSLib.redeemUSDT();
, where to calculate what amount of USDT we should return to the user, we use this formula which includes the prices usdtAmount = ((usdaPrice * usdaAmount) / usdtPrice)
function redeemUSDT(
CDSInterface.Interfaces memory interfaces,
uint256 burnedUSDaInRedeem,
uint128 usdaAmount,
uint64 usdaPrice,
uint64 usdtPrice
) external returns (uint256) {
if (usdaAmount == 0) revert CDSInterface.CDS_NeedsMoreThanZero();
// @audit user just needs at least 1 balance of USDa to pass this check and can steal all the USDT
@>> if (interfaces.usda.balanceOf(msg.sender) < usdaAmount) {
revert CDSInterface.CDS_Insufficient_USDa_Balance();
}
// Increment burnedUSDaInRedeem
burnedUSDaInRedeem += usdaAmount;
IGlobalVariables.OmniChainData memory omniChainData = interfaces.globalVariables.getOmniChainData();
omniChainData.burnedUSDaInRedeem += usdaAmount; //
// burn usda
bool transfer = interfaces.usda.burnFromUser(msg.sender, usdaAmount);
if (!transfer) revert CDSInterface.CDS_TransferFailed(IBorrowing.AssetName.USDa);
// calculate the USDT USDa ratio
@>> uint128 usdtAmount = ((usdaPrice * usdaAmount) / usdtPrice);
interfaces.treasury.approveTokens(IBorrowing.AssetName.TUSDT, address(interfaces.cds), usdtAmount);
// Transfer usdt to user
@>> bool success = interfaces.usdt.transferFrom(address(interfaces.treasury), msg.sender, usdtAmount);
if (!success) revert CDSInterface.CDS_TransferFailed(IBorrowing.AssetName.TUSDT);
interfaces.globalVariables.setOmniChainData(omniChainData);
return burnedUSDaInRedeem;
}
- Attack is simple, attacker just specifies usdt price to be 1, and the usda price, as much as he wants to withdraw, he needs at least 1 USDA, to basically withdraw all the USDT from the treasury.
- Example to steal
10000
usdt.usdtAmount = ((usdaPrice * usdaAmount) / usdtPrice)
|usdtAmount = ((10000 * usdaAmount) / 1)
Attacker can steal all USDT from treasury just by having 1 USDa token.
Use the oracles to retrieve prices, don't allow for arbitrary inputs from users.