sherlock-audit / 2024-09-predict-fun-judging

0 stars 0 forks source link

Breezy Sapphire Salamander - Borrower can grief/DoS Refinancer Bots by setting a ERC1155 callback that reverts when executed on-chain but pass when simulated off-chain #249

Open sherlock-admin2 opened 2 days ago

sherlock-admin2 commented 2 days ago

Breezy Sapphire Salamander

Medium

Borrower can grief/DoS Refinancer Bots by setting a ERC1155 callback that reverts when executed on-chain but pass when simulated off-chain

Summary

Off-chain simulation are never perfect and always differ from on-chain execution. This might be because of some global state chain variable always being the same when using simulation tools, simulated blockchain state not entirely accurate with actual state, or past tx modifying the chain state in an unpredictable way.

An attacker can leverage this to create an refinanciable loan that would always revert when called by the Refinancer bot, but pass when simulated.

This could lock the Refinancer bot into a DoS state for a period of time, trying to refinance an always reverting loan.

Root Cause

Internal pre-conditions

External pre-conditions

Attack Path

  1. attacker create a contract at address 0xbad that will act as a borrower
    • that contract will implement onERC1555Received that will revert only when executed on-chain, but not during simulations
  2. attacker create a first loan offer with another address and accept it with 0xbad, making 0xbad the borrower of the loan
  3. attacker create a second loan offer that would be chosen by the bot as the best loan to refinance the malicious first loan (the bot follows an algorithm and therefore will always have a predictable outcome)
  4. the Refinancer bot detect the loan as refinanciable by the second one, and simulate the execution which will pass
  5. the Refinancer bot include it in its Refinancing[] input array and execute refinance
  6. Revert

Depending on how the bot simulate the call:

Impact

PoC

N/A

Mitigation

Use a try/catch statement in the loop, to not suffer from reverting refinancing. As _refinance is an internal function this is not possible as try/catch only works for external calls, but sponsors could either make it external (and role protected), or create an external wrapper function