Platform: Code4rena
Start Date: 05/04/2022
Pot Size: $30,000 USDC
Total HM: 10
Participants: 47
Period: 3 days
Judge: gzeon
Total Solo HM: 4
Id: 106
League: ETH
Rank: 13/47
Findings: 3
Award: $399.31
π Selected for report: 0
π Solo Findings: 0
π Selected for report: cmichel
Also found by: AuditsAreUS, IllIllI, Ruhum, csanuragjain, danb, joshie, t11s, tintin
293.89 USDC - $293.89
Lenders are allowed to "buy out" another lender on a position via the loan
function.
This is supposed to be a purely positive sum action for the borrower, as the new lender must provide "better" terms than their predecessor, as checked here:
However, in practice, this mechanic appears fundamentally flawed for a number of reasons:
By increasing the duration of a loan, the lender is also increasing the amount of interest the borrower must pay to keep their NFT at the end of the loan. If a borrower created a loan only expecting to have to pay $100 in interest over 12 months, if another lend came and extended the loan by 100 years, they'd now have to pay $10100 to keep their NFT. Even if the borrower noticed this and went to close their loan, this is a potential griefing vector, as the lender can keep extending their loan and force the borrower to pay gas each time to close it. There is a low cost for the attacker if the loan amount is low, as with a long enough duration the interest paid can outpace the principal.
For most of the same reasons above, the ability to increase the amount being loaned to a user at any time is dangerous. A malicious lender could 10x a user's loan amount, forcing them to pay more interest to keep their NFT or risk having it seized. Sure the borrower could just default on purpose and keep the tokens, but if the NFT is precious for non monetary reasons or they are simply unaware the borrower could unintentionally wind up paying far more interest then they expected when they created their loan. Once again, even if the borrower is aware of the increased amount and goes to close their loan, this opens up a griefing vector as described above.
Either:
Or a implement a combination of two or more of these options.
#0 - wilsoncusack
2022-04-06T12:12:02Z
again, this is known/intended protocol design
#1 - wilsoncusack
2022-04-06T18:11:19Z
is basically duplicate of the max loan amount ones but will leave
#2 - gzeoneth
2022-04-15T11:50:27Z
The part regarding loan duration is invalid, the borrower can close the loan anytime by paying current accumulatedInterest. Duplicate of #24
π Selected for report: IllIllI
Also found by: 0v3rf10w, 0x1f8b, 0xDjango, 0xkatana, BouSalman, CertoraInc, Dravee, FSchmoede, Hawkeye, Kenshin, Meta0xNull, PPrieditis, Ruhum, TerrierLover, VAD37, WatchPug, berndartmueller, csanuragjain, hake, horsefacts, hubble, m9800, rayn, reassor, robee, samruna, securerodd, shenwilly, sorrynotsorry, t11s, teryanarmen, tintin, z3s
67.476 USDC - $67.48
Backed uses solmate
's SafeTransferLib
to safely transfer non-standard ERC20 tokens:
Notably, SafeTransferLib
does not revert if the token being transferred has no code at all:
This means transferring address(0)
as a token will succeed, which could confuse users. As a result, Backed checks that the token is not address(0)
on this line:
However, this check is insufficient to protect users from accidentally creating loans with invalid tokens, as any address without code will be accepted as a valid token. One particularly consequential example of how this could go wrong for users would be inputting the address of a common token like DAI or USDC for another network on accident, but having the loan creation succeed as if it was the correct token. See the proof of concept for more details:
Rewrite the require like so:
require(loanAssetContractAddress.code.length != 0, "NFTLoanFacilitator: invalid loan");
#0 - wilsoncusack
2022-04-07T01:07:35Z
it's also possible
#1 - wilsoncusack
2022-04-08T00:22:11Z
No funds at risk
#2 - wilsoncusack
2022-04-13T13:59:21Z
#3 - gzeoneth
2022-04-15T13:56:27Z
Downgrading to Low/QA because no funds at risk. Treating as Warden's QA Report.
#4 - JeeberC4
2022-04-17T04:55:35Z
Preserving original title: SafeTransferLib does not revert if the token being transferred has no code
π Selected for report: Dravee
Also found by: 0v3rf10w, 0x1f8b, 0xkatana, CertoraInc, FSchmoede, Funen, IllIllI, Kenshin, Meta0xNull, TerrierLover, Tomio, csanuragjain, joshie, obront, rayn, rfa, robee, saian, securerodd, sorrynotsorry, t11s, z3s
37.9362 USDC - $37.94
A safe cast check is done here:
The check uses <=
, which compiles down to 2 opcodes (ISZERO
, GT
). This check can be done in a single opcode like so:
require(accumulatedInterest < 1 << 128, "NFTLoanFacilitator: accumulated interest exceeds uint128");
because 1 << 128
is equivalent to 2 ** 128
which is equal to type(uin128).max + 1
A check that the from
address equals ownerOf[id]
is performed here:
This is unnecessary as the only place where the internal _transfer
is ever called (via the access controlled public version: loanFacilitatorTransfer
) will only ever pass the owner of the token as ensured by this line:
Therefore the check can be removed to save an SLOAD.
#0 - wilsoncusack
2022-04-06T12:12:37Z
legit