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: 9/59
Findings: 4
Award: $1,317.69
π Selected for report: 0
π 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/main/contracts/NFTPairWithOracle.sol#L198
A lender (attacker) can modify the oracle address of tokenLoanParams
to get collateral directly before expiration.
updateLoanParams
function and update to a malicious oracle address (because the updateLoanParams
function doesnβt check the validation of oracle address).removeCollateral
function to bypass the NFT value check (require
of L288) by using the malicious oracle to get a very low rate (e.g. 0) and get the collateral directly.https://github.com/code-423n4/2022-04-abranft/blob/main/contracts/NFTPairWithOracle.sol#L288
vim
Check the original oracle address and params.oracle
address should be the same. It should not update the oracle address in the updateLoanParams
function.
#0 - cryptolyndon
2022-05-05T22:49:28Z
Confirmed, duplicate of #37
π Selected for report: kenzo
Also found by: AuditsAreUS, Czar102, GimelSec, WatchPug
https://github.com/code-423n4/2022-04-abranft/blob/main/contracts/NFTPair.sol#L266 https://github.com/code-423n4/2022-04-abranft/blob/main/contracts/NFTPair.sol#L540 https://github.com/code-423n4/2022-04-abranft/blob/main/contracts/NFTPairWithOracle.sol#L295 https://github.com/code-423n4/2022-04-abranft/blob/main/contracts/NFTPairWithOracle.sol#L573
A borrower (attacker) can use reentrancy attack to request a loan successfully and the collateral is still owned by the attacker. If a lender (victim) tries to call lend
on the malicious loan which seems normal, the lender will lose money and never get the collateral.
Some ERC721 tokens implement the _beforeTokenTransfer
function which may call another address that is controllable by msg.sender
.
A borrower (attacker) can use the feature to trigger reentrancy attack:
requestLoan
normally with tokenId 1. Now tokenId 1 is owned by NFTPair contract.removeCollateral
to trigger reentrancy attack on L266.collateral.transferFrom(address(this), to, tokenId);
, the attacker uses _beforeTokenTransfer
of the collateral to re-entry requestLoan
with skim = true
.delete tokenLoan[tokenId];
initialize tokenLoan[tokenId]
(including loan status
), the attacker will re-entry requestLoan
successfully with skim = true
and create a new loan of tokenLoan[tokenId]
.There are some attack scenarios:
lend
on the malicious loan which seems normal, the lender will lose money and never get the collateral.lend
, the attacker can frontrun the reentrancy attack before the lend
called by the victim.removeCollateral
to create a malicious loan.repay
function suffers from this reentrancy attack.vim, ethers.js
Delete tokenLoan
after collateral.transferFrom
:
β¦ collateral.transferFrom(address(this), to, tokenId); delete tokenLoan[tokenId]; }
or use ReentrancyGuard.
#0 - cryptolyndon
2022-05-05T22:48:54Z
Duplicate of #61; not an issue.
#1 - 0xean
2022-05-20T22:32:39Z
downgrading per dupe of #61
π 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.6404 MIM - $72.64
We list 1 non-critical finding:
Using ecrecover is against best practice. Preferably use ECDSA.recover instead. EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature unique. However it should be impossible to be a threat by now.
NFTPair.sol 383: require(ecrecover(_getDigest(dataHash), v, r, s) == lender, "NFTPair: signature invalid"); 419: require(ecrecover(_getDigest(dataHash), v, r, s) == borrower, "NFTPair: signature invalid"); NFTPairWithOracle.sol 417: require(ecrecover(_getDigest(dataHash), signature.v, signature.r, signature.s) == lender, "NFTPair: signature invalid"); 452: require(ecrecover(_getDigest(dataHash), signature.v, signature.r, signature.s) == borrower, "NFTPair: signature invalid");
Take these implementation into consideration
https://github.com/OpenZeppelin/openzeppelin-contracts/blob/4a9cc8b4918ef3736229a5cc5a310bdc17bf759f/contracts/utils/cryptography/draft-EIP712.sol https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/extensions/draft-ERC20Permit.sol
#0 - cryptolyndon
2022-05-13T05:03:19Z
Nonces prevent replaying signatures.
π Selected for report: BowTiedWardens
Also found by: 0x1f8b, 0xNazgul, 0xf15ers, 0xkatana, CertoraInc, Funen, GimelSec, Hawkeye, IllIllI, Kulk0, NoamYakov, Tadashi, Tomio, TrungOre, antonttc, catchup, defsec, delfin454000, fatherOfBlocks, gzeon, horsefacts, joestakey, kenta, oyc_109, pauliax, reassor, robee, samruna, simon135, slywaters, sorrynotsorry, z3s
43.8829 MIM - $43.88
++i
rather than i++
In for
loops, using ++i
rather than i++
to save gas.
NFTPair.sol 494: for (uint256 k = 2; k <= COMPOUND_INTEREST_TERMS; k++) { 641: for (uint256 i = 0; i < actions.length; i++) { NFTPairWithOracle.sol 527: for (uint256 k = 2; k <= COMPOUND_INTEREST_TERMS; k++) { 674: for (uint256 i = 0; i < actions.length; i++) {
Use ++i
rather than i++
to save gas.
#0 - cryptolyndon
2022-05-14T01:21:08Z
Refuse to believe this makes a difference.