Backed Protocol contest - rayn's results

Protocol for peer to peer NFT-Backed Loans.

General Information

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

Backed Protocol

Findings Distribution

Researcher Performance

Rank: 1/47

Findings: 4

Award: $7,063.20

๐ŸŒŸ Selected for report: 2

๐Ÿš€ Solo Findings: 1

Findings Information

๐ŸŒŸ Selected for report: rayn

Labels

bug
help wanted
3 (High Risk)
resolved
sponsor confirmed

Awards

6144.5095 USDC - $6,144.51

External Links

Lines of code

https://github.com/code-423n4/2022-04-backed/blob/main/contracts/NFTLoanFacilitator.sol#L205-L208 https://github.com/code-423n4/2022-04-backed/blob/main/contracts/NFTLoanFacilitator.sol#L215-L218

Vulnerability details

Impact

If an attacker already calls lend() to lend to a loan, the attacker can manipulate loanInfo by reentrancy attack when any lenders try to buyout. The attacker can set bad values of lendInfo (e.g. very long duration, and 0 interest rate) that the lender who wants to buyout don't expect.

Proof of Concept

An attacker lends a loan, and loanAssetContractAddress in loanInfo is ERC777 which is suffering from reentrancy attack. When a lender (victim) try to buyout the loan of the attacker:

  1. The victim called lend().
  2. In lend(), it always call ERC20(loanAssetContractAddress).safeTransfer to send accumulatedInterest + previousLoanAmount to currentLoanOwner (attacker).
  3. The transfer of loanAssetContractAddress ERC777 will call _callTokensReceived so that the attacker can call lend() again in reentrancy with parameters:
    • loanId: same loan Id
    • interestRate: set to bad value (e.g. 0)
    • amount: same amount
    • durationSeconds: set to bad value (e.g. a long durationSeconds)
    • sendLendTicketTo: same address of the attacker (currentLoanOwner)
  4. Now the variables in loanInfo are changed to bad value, and the victim will get the lend ticket but the loan term is manipulated, and can not set it back (because it requires a better term).

Tools Used

vim

Use nonReentrant modifier on lend() to prevent reentrancy attack:

https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/security/ReentrancyGuard.sol

#0 - wilsoncusack

2022-04-07T17:08:23Z

we should mitigate, but will think on this

#1 - wilsoncusack

2022-04-08T00:49:54Z

not sure whether this should be medium or high risk

#2 - wilsoncusack

2022-04-08T10:37:41Z

thinking more, again we should definitely mitigate, but I think this is less severe because I do not think ERC777 tokens will work with our contract? The on received call will revert? So this would need to be a malicious ERC20 designed just for this

#3 - wilsoncusack

2022-04-08T10:50:42Z

er erc777 does work because reception ack is not needed in the normal case

#5 - gzeoneth

2022-04-15T11:40:51Z

Sponsor confirmed with fix

Findings Information

๐ŸŒŸ Selected for report: rayn

Also found by: hake

Labels

bug
2 (Med Risk)
disagree with severity
resolved
sponsor confirmed

Awards

829.5088 USDC - $829.51

External Links

Lines of code

https://github.com/code-423n4/2022-04-backed/blob/main/contracts/NFTLoanFacilitator.sol#L205-L208 https://github.com/code-423n4/2022-04-backed/blob/main/contracts/NFTLoanFacilitator.sol#L215-L218

Vulnerability details

Impact

If an attacker (lender) lends to a loan, the attacker can always revert transactions when any lenders try to buyout, making anyone can not buyout the loan of the attacker.

Proof of Concept

  1. A victim calls lend(), trying to buyout the loan of the attacker.
  2. In lend(), it always call ERC20(loanAssetContractAddress).safeTransfer to send accumulatedInterest + previousLoanAmount to currentLoanOwner (attacker).
  3. If the transfer of loanAssetContractAddress is ERC777, it will call _callTokensReceived that the attacker can manipulate and always revert it.
  4. Because NFTLoanFacilitator uses safeTransfer and safeTransferFrom to check return value, the transaction of the victim will also be reverted. It makes anyone can not buyout the loan of the attacker.

In _callTokensReceived, the attacker just wants to revert the buyout transaction, but keep repayAndCloseLoan successful. The attacker can call loanInfoStruct(uint256 loanId) in _callTokensReceived to check if the value of loanInfo is changed or not to decide to revert it.

Tools Used

vim

Don't transfer ERC20(loanAssetContractAddress) to currentLoanOwner in lend(), use a global mapping to record redemption of lenders and add an external function redeem for lenders to transfer ERC20(loanAssetContractAddress).

#0 - wilsoncusack

2022-04-07T17:04:39Z

I think this is just part of perils of working with certain assets and I am not sure we will mitigate

#1 - wilsoncusack

2022-04-13T21:06:16Z

Sorry I lost track of this one/labeled incorrectly. This is indeed an issue we intend to address: we will block erc777 tokens.

The worse implication here is that a lender could prevent a borrower from repaying and could seize the NFT.

#2 - wilsoncusack

2022-04-13T21:08:56Z

Still not sure if this should be high or medium. But there are legit ERC777 tokens that a borrower might selecting unknowingly, so probably is high?

#3 - gzeoneth

2022-04-15T11:44:23Z

I suggest this as Med Risk as no fund is loss by preventing buyout.

#4 - wilsoncusack

2022-04-16T01:21:57Z

I suggest this as Med Risk as no fund is loss by preventing buyout.

But as I said above the bigger issue is they could block repayment, guaranteeing default and seizure of collateral?

#5 - gzeoneth

2022-04-16T04:10:26Z

I suggest this as Med Risk as no fund is loss by preventing buyout.

But as I said above the bigger issue is they could block repayment, guaranteeing default and seizure of collateral?

I think you are correct as there is a similar call in L241. However both warden failed to describe such attack path and I am inclined to keep this as Med Risk.

#6 - wilsoncusack

2022-04-21T20:15:35Z

Awards

54.2825 USDC - $54.28

Labels

bug
QA (Quality Assurance)
resolved
sponsor confirmed

External Links

Summary

We list 2 non-critical findings here:

  • (Non) loanEndSeconds does not check valid loanId
  • (Non) Should use safe version of ERC721 functions

In conclusion, it's better to check the validity of the mappingโ€™s key. And using a safe version of ERC721 functions is usually a better choice.

(Non) loanEndSeconds does not check valid loanId

Impact

In NFTLoanFacilitator.sol/loanEndSeconds(), it doesnโ€™t check whether loanId is valid

Proof of Concept

https://github.com/code-423n4/2022-04-backed/blob/main/contracts/NFTLoanFacilitator.sol#L363

Add check for loanId

(Non) Should use safe version of ERC721 functions

Impact

In NFTLoanFacilitator.sol, it uses IERC721Mintable(borrowTicketContract).mint which is not a good practice.

Proof of Concept

https://github.com/code-423n4/2022-04-backed/blob/main/contracts/NFTLoanFacilitator.sol#L102

https://github.com/code-423n4/2022-04-backed/blob/main/contracts/NFTLoanFacilitator.sol#L161

Use safeMint instead of mint

#0 - wilsoncusack

2022-04-15T17:37:34Z

Awards

34.8992 USDC - $34.90

Labels

bug
G (Gas Optimization)
resolved
sponsor confirmed

External Links

Use private variables to save gas

Those can be declared as private for gas savings, only creating view functions for variables that should be read externally.

Proof of Concept

https://github.com/code-423n4/2022-04-backed/blob/main/contracts/NFTLoanFacilitator.sol#L17-L44

Recommendation

Declare these variables to private.

Fetch loan fields into memory to save gas

If storage is accessed more than once, it should be fetch into memory to save gas. loan.lastAccumulatedTimestamp is accessed twice in totalOwed and interestOwed

Proof of Concept

https://github.com/code-423n4/2022-04-backed/blob/main/contracts/NFTLoanFacilitator.sol#L339

https://github.com/code-423n4/2022-04-backed/blob/main/contracts/NFTLoanFacilitator.sol#L343

https://github.com/code-423n4/2022-04-backed/blob/main/contracts/NFTLoanFacilitator.sol#L352

https://github.com/code-423n4/2022-04-backed/blob/main/contracts/NFTLoanFacilitator.sol#L356

Recommendation

Fetch loan.lastAccumulatedTimestamp into memory

uint40 lastAccumulatedTimestamp = loan.lastAccumulatedTimestamp;

#0 - wilsoncusack

2022-04-15T14:49:34Z

AuditHub

A portfolio for auditors, a security profile for protocols, a hub for web3 security.

Built bymalatrax ยฉ 2024

Auditors

Browse

Contests

Browse

Get in touch

ContactTwitter