Platform: Code4rena
Start Date: 27/04/2022
Pot Size: $50,000 MIM
Total HM: 6
Participants: 59
Period: 5 days
Judge: 0xean
Id: 113
League: ETH
Rank: 15/59
Findings: 2
Award: $731.32
🌟 Selected for report: 1
🚀 Solo Findings: 0
🌟 Selected for report: 0x1337
Also found by: BowTiedWardens, GimelSec, catchup, cccz, gzeon, horsefacts, hyh, kenzo
658.884 MIM - $658.88
https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPairWithOracle.sol#L286-L288 https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPairWithOracle.sol#L200-L211
The intended use of the Oracle is to protect the lender from a drop in the borrower's collateral value. If the collateral value goes up significantly and higher than borrowed amount + interest, the lender should not be able to seize the collateral at the expense of the borrower. However, in the NFTPairWithOracle
contract, the lender could change the Oracle once a loan is outstanding, and therefore seize the collateral at the expense of the borrower, if the actual value of the collateral has increased significantly. This is a critical risk because borrowers asset could be lost to malicious lenders.
In NFTPairWithOracle
, the params
are set by the borrower
when they call requestLoan()
, including the Oracle used. Once a lender agrees with the parameters and calls the lend()
function, the loan.status
changes to LOAN_OUTSTANDING
.
Then, the lender can call the updateLoanParams()
function and pass in its own params
including the Oracle used. The require
statement from line 205 to 211 does not check if params.oracle
and cur.oracle
are the same. A malicious lender could pass in his own oracle
after the loan becomes outstanding, and the change would be reflected in line 221.
In a situation where the actual value of the collateral has gone up by a lot, exceeding the amount the lender is owed (principal + interest), the lender would have an incentive to seize the collateral. If the Oracle is not tampered with, lender should not be able to do this, because line 288 should fail. But a lender could freely change Oracle once the loan is outstanding, then a tampered Oracle could produce a very low rate
in line 287 such that line 288 would pass, allowing the lender to seize the collateral, hurting the borrower.
Manual review
Once a loan is agreed to, the oracle used should not change. I'd recommend adding a check in the require
statement in line 205 - 211 that params.oracle == cur.oracle
#0 - cryptolyndon
2022-05-05T02:25:02Z
Confirmed, this is bad. First report of this particular exploit.
🌟 Selected for report: IllIllI
Also found by: 0x1337, 0x1f8b, 0xDjango, 0xf15ers, AuditsAreUS, BowTiedWardens, CertoraInc, Funen, GimelSec, MaratCerby, Ruhum, WatchPug, antonttc, berndartmueller, bobi, bobirichman, broccolirob, catchup, cccz, defsec, delfin454000, gs8nrv, gzeon, horsefacts, hubble, hyh, ilan, jah, joestakey, kebabsec, kenta, kenzo, m9800, mics, oyc_109, pauliax, reassor, robee, samruna, sikorico, simon135, throttle, unforgiven, z3s
72.4442 MIM - $72.44
https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPair.sol#L177 https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPairWithOracle.sol#L194 https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPairWithOracle.sol#L330-L337 https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPair.sol#L295-L302 https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPair.sol#L591-L598
In the NFTPair
and NFTPairWithOracle
contracts and their clones, there's no check in the init()
function or elsewhere that the asset
is not deflationary. A deflationary token, such as any Safemoon fork, charges a fee for each transfer, and as such when address A sends amount
to address B, address B's balance increases by less than amount
due to such fee.
The BentoBox
contract is not compatible with deflationary token, given how user balance are recorded in the deposit
, withdraw
, and transfer
functions. Therefore, the risk of allowing deflationary token in NFTPair
and NFTPairWithOracle
is that the BentoBox
contract might suffer a loss in asset
as lending and repayment continue. And the lender might be unable to withdraw its balance from BentoBox
even if the borrower repays the principal plus interest, as the BentoBox
might run out of asset
balance for the lender to withdraw. This causes loss of user fund, and could therefore be a major problem.
You could see in the init()
function that asset
can be whatever that the caller of this contract provides.
function init(bytes calldata data) public payable override { require(address(collateral) == address(0), "NFTPair: already initialized"); (collateral, asset) = abi.decode(data, (IERC721, IERC20)); require(address(collateral) != address(0), "NFTPair: bad pair"); }
When lending or repayment or withdraw happens, asset
transfer or withdraw is handled by bentoBox
, where there's no accounting for any potential transfer fee. bentoBox
contract might therefore run out of asset
balance, causing users unable to withdraw the full amount via the function below.
Manual Review
I'd recommend disallowing deflationary token in these contracts. There are different ways to do this. One possible way is to ask a user to send certain amount of token to the contract, and then check if the increase in the contract's token balance equals the amount being sent. The asset
is not deflationary if the check passes. Then a global variable passed
could be used to indicate that. Only then could lending be allowed.
function checkAsset(uint256 amount) public { uint256 oldBalance = asset.balanceOf(address(this)); require(asset.transferFrom(msg.sender, address(this), amount), "transferFrom failed"); uint256 newBalance = asset.balanceOf(address(this)); require(newBalance == oldBalance + amount, "no deflationary token allowed"); require(asset.transfer(msg.sender, amount), "transfer failed"); passed = true; }
#0 - 0xm3rlin
2022-05-04T14:16:23Z
BentoBox does not support deflationary or token transfer fee asset, similarly all contracts on top of it will not. Our UI will likely only do loans against MIM.
#1 - 0xean
2022-05-20T20:28:13Z
Downgrading to low severity as mainly a documentation issues to ensure that users are aware the FOT / Rebasing tokens are not supported.
#2 - JeeberC4
2022-05-23T19:06:18Z
Preserving original title: No Check for Deflationary Tokens