Stableswap.buy() Lack of Restrictions asset_in!=asset_out, Allows Users to Steal Funds #133
Labels
3 (High Risk)
Assets can be stolen/lost/compromised directly
bug
Something isn't working
duplicate-58
🤖_133_group
AI based duplicate group recommendation
satisfactory
satisfies C4 submission criteria; eligible for awards
Lines of code
https://github.com/code-423n4/2024-02-hydradx/blob/603187123a20e0cb8a7ea85c6a6d718429caad8d/HydraDX-node/pallets/stableswap/src/lib.rs#L787
Vulnerability details
Vulnerability details
We can use the
Stableswap.buy()
function to purchase another token.First, we specify the
amount_out
, and then we calculate the correspondingamount_in
.The calculation for
amount_in
is as follows:we keep
D
unchanged, calculate the newy
using thecalculate_y_given_out()
method, and then subtract the oldy
to obtain the difference, which representsamount_in
.For example, let's assume the following initial reserves:
If you want to buy 50 usdc, how do calculate the required amount of usdt?
Calculation steps:
D
based on the oldreserves
.new_usdt
usingcalculate_y_internal(xp, D)
. Assumingnew_usdt = 270
.amount_out
=new_usdt - old_usdt = 270 - 220 = 50
.However, due to the lack of restrictions on
asset_in != asset_out
if a user specifies
asset_in == asset_out == usdc
(i.e., the user wants to buy 50 usdc), how much usdc do they need to provide?
Execution steps:
D
based on the oldreserves
.new_usdc
usingcalculate_y_internal(xp, D)
.Since usdt remains unchanged, we can approximate it to the old value (e.g., new_usdc = 200.01).
amount_out
asnew_usdc - old_usdc = 200.01 - 200 = 0.01
.This results in the user needing to provide only 0.01 usdc to obtain 50 usdc.
note: that the
sell()
function also faces a similar issue, but instead of stealing, it results in losing funds.Impact
Users can exploit the system by specifying
asset_in == asset_out
, effectively stealing funds.Proof of Concept
The following code demonstrates the scenario mentioned above, where a user specifies the same
asset_in
andasset_out
.add to
tests/trades.rs
Recommended Mitigation
pub fn buy( origin: OriginFor<T>, pool_id: T::AssetId, asset_out: T::AssetId, asset_in: T::AssetId, amount_out: Balance, max_sell_amount: Balance, ) -> DispatchResult { let who = ensure_signed(origin)?; + ensure!(asset_in != asset_out, Error::<T>::SameAssetTradeNotAllowed);
pub fn sell( origin: OriginFor<T>, pool_id: T::AssetId, asset_in: T::AssetId, asset_out: T::AssetId, amount_in: Balance, min_buy_amount: Balance, ) -> DispatchResult { let who = ensure_signed(origin)?; + ensure!(asset_in != asset_out, Error::<T>::SameAssetTradeNotAllowed);
Assessed type
Error
The text was updated successfully, but these errors were encountered: