repay_flash_swap lack token type validation, pool accepts repayment with arbitrary token types, enabling theft of real assets.
The function in pool.move lines 1245-1275, repay_flash_swap only checks the numeric amounts of the supplied FungibleAsset matches expected repayment. Doesn't validate that the asset's metadata type equals the pool's configured tokens.
Because of that:
flash_swap and "repay" with equal-sized FAKE tokens, walking away with the USDT.in pool.move lines 1245-1275, repay_flash_swap only checks the numeric amounts of the supplied FungibleAsset matches expected repayment. Doesn't validate that the asset's metadata type equals the pool's configured tokens.
1241 public fun repay_flash_swap(
1242 asset_a: FungibleAsset,
1243 asset_b: FungibleAsset,
1244 receipt: FlashSwapReceipt
1245 ) acquires Pool {
1246 let FlashSwapReceipt {
1247 pool_address,
1248 a2b,
1249 partner_name,
1250 pay_amount,
1251 ref_fee_amount
1252 } = receipt;
1253 let pool = borrow_global_mut<Pool>(pool_address);
1254 if (a2b) {
1255 assert!(fungible_asset::amount(&asset_a) == pay_amount, EAMOUNT_INCORRECT);
1256 // send ref fee to partner
1257 if (ref_fee_amount > 0) {
1258 let ref_fee = fungible_asset::extract(&mut asset_a, ref_fee_amount);
1259 partner::receive_ref_fee(partner_name, ref_fee, pool.asset_a_addr);
1260 };
1261 primary_fungible_store::deposit(pool_address, asset_a);
1262 fungible_asset::destroy_zero(asset_b);
1263 pool.asset_a = pool.asset_a + pay_amount - ref_fee_amount;
1264 } else {
1265 assert!(fungible_asset::amount(&asset_b) == pay_amount, EAMOUNT_INCORRECT);
1266 // send ref fee to partner
1267 if (ref_fee_amount > 0) {
1268 let ref_fee = fungible_asset::extract(&mut asset_b, ref_fee_amount);
1269 partner::receive_ref_fee(partner_name, ref_fee, pool.asset_b_addr);
1270 };
1271 primary_fungible_store::deposit(pool_address, asset_b);
1272 fungible_asset::destroy_zero(asset_a);
1273 pool.asset_b = pool.asset_b + pay_amount - ref_fee_amount;
1274 }
1275 }
Here is my PoC's high-level steps:
Setup Alice creates a USDC/USDT pool and deposits honest liquidity. Attacker: Bob mints a FAKE token.
Exploit of Flash Swap:
pool::flash_swap.pool::repay_flash_swap with 5000 FAKE tokens.See run logs:
Bob borrowed USDT: 4970
Should repay USDC: 5000
EXPLOIT: Repaying with FAKE tokens instead of USDC
EXPLOIT SUCCESSFUL: Pool accepted fake tokens!
Bob's stolen USDT: 4970
Pool real USDC balance before/after: 5982 -> 5982
Pool real USDT balance before/after: 5982 -> 1012
cd CLMM_Dex/DexlynClmm
cp ~/Downloads/token_exploit.t.move CLMM_Dex/DexlynClmm/tests
aptos move test --dev --ignore-compile-warnings --skip-attribute-checks --filter token_exploit_test::test_token_type_validation_exploit
You can grab the test file here https://gist.githubusercontent.com/niafreu/183eaa7f6d89c92c3c934cfd953c517d/raw/10eba79f9681c2f8744600754c22ba1fd2cf1c88/token_exploit.t.move