Astaria contest - wallstreetvilkas's results

On a mission is to build a highly liquid NFT lending market.

General Information

Platform: Code4rena

Start Date: 05/01/2023

Pot Size: $90,500 USDC

Total HM: 55

Participants: 103

Period: 14 days

Judge: Picodes

Total Solo HM: 18

Id: 202

League: ETH

Astaria

Findings Distribution

Researcher Performance

Rank: 101/103

Findings: 1

Award: $33.24

🌟 Selected for report: 0

🚀 Solo Findings: 0

Findings Information

Labels

bug
3 (High Risk)
satisfactory
duplicate-521

Awards

33.2422 USDC - $33.24

External Links

Lines of code

https://github.com/code-423n4/2023-01-astaria/blob/1bfc58b42109b839528ab1c21dc9803d663df898/src/ClearingHouse.sol#L114-L169

Vulnerability details

Impact Detailed description of the impact of this finding.

Calling the function safeTransferFrom() and passing in a custom, attacker controlled payment token allows the malicious actor to perform reentrancy. https://github.com/code-423n4/2023-01-astaria/blob/1bfc58b42109b839528ab1c21dc9803d663df898/src/ClearingHouse.sol#L153-L158 the following function clears the attacker of all debt, which is costless to the attacker because the token is worthless. After the debt is cleared, the following check is performed: https://github.com/code-423n4/2023-01-astaria/blob/1bfc58b42109b839528ab1c21dc9803d663df898/src/ClearingHouse.sol#L160, this is where the attacker reenters. Attacker calls the releaseToAddress() in collateral token contract, and then he calls into a malicious erc721 contract, the malicious erc721 contract then calls into this function: https://github.com/code-423n4/2023-01-astaria/blob/1bfc58b42109b839528ab1c21dc9803d663df898/src/CollateralToken.sol#L553, and because the idToUnderlying is deleted, it can overwrite it and it won't revert and the nft is sent to the attacker, and he gets to keep it.

Proof of Concept Provide direct links to all referenced code in GitHub. Add screenshots, logs, or any other relevant proof that illustrates the concept.

Create a malicious contract. In the contract, define a custom balanceOf() function which calls into CollateralToken's releaseToAddress() passing in the collateralId and calls the custom, attacker controlled erc721 contract. Because the balanceOf() of our malicious contract is called not once, but twice, make a variable that stores which time the function is being called like this pseudo code:

uint var; erc721 erc721Contract; //this is our malicious erc721 contract which calls into onErc721Received() in collateralToken, which overwrites the underlying id function balanceOf() public returns (uint){ if(var == 0){ var = var + 1; return anyAmountToPassTheFirstCheck; } else{ releaseToAddress() //first call erc721Contract.attack() //this is our custom defined erc721. the attack function will call this function https://github.com/code-423n4/2023-01-astaria/blob/1bfc58b42109b839528ab1c21dc9803d663df898/src/CollateralToken.sol#L553 this overwrites our deleted underlyingid with our custom contract, so nothing reverts

} }

Through this malicious contract, call safeTransferFrom() function in ClearingHouse pass in, encoded metadata (which contains our malicious contract's address uint256(bytes32(maliciousContractAddr)), the collateralId of the nft and then finally the two arguments to and from (it doesn't matter what they are, because they are unused throughout the code). This invokes the balanceOf twice. The second time we reenter, release the nft to the attacker, call into our erc721 contract which overwrites the underlyingid so that it exists when house invokes the settleAuction() function. The nft is transfered to the attacker.

Tools Used

manual review

ASTARIA_ROUTER.LIEN_TOKEN().payDebtViaClearingHouse( paymentToken, collateralId, payment - liquidatorPayment, s.auctionStack.stack ); if (ERC20(paymentToken).balanceOf(address(this)) > 0) { ERC20(paymentToken).safeTransfer( ASTARIA_ROUTER.COLLATERAL_TOKEN().ownerOf(collateralId), ERC20(paymentToken).balanceOf(address(this)) ); } ASTARIA_ROUTER.COLLATERAL_TOKEN().settleAuction(collateralId);

This is the vulnerable code before, I recommend you change it to this:

ASTARIA_ROUTER.LIEN_TOKEN().payDebtViaClearingHouse( paymentToken, collateralId, payment - liquidatorPayment, s.auctionStack.stack ); ASTARIA_ROUTER.COLLATERAL_TOKEN().settleAuction(collateralId); if (ERC20(paymentToken).balanceOf(address(this)) > 0) { ERC20(paymentToken).safeTransfer( ASTARIA_ROUTER.COLLATERAL_TOKEN().ownerOf(collateralId), ERC20(paymentToken).balanceOf(address(this)) ); }

At this point it's too late for the attacker to reenter as nothing can be gained, but another issue still stands, the attacker can pay off debt with worthless tokens. Consider adding a mapping of whitelisted, trusted tokens to pay with, or simply force the user to pay off debt in the tokens that they borrowed.

#0 - c4-judge

2023-01-24T07:52:14Z

Picodes marked the issue as duplicate of #564

#1 - Picodes

2023-02-15T07:26:28Z

Note that the main issue in my opinion is 'the attacker can pay off debt with worthless tokens'. The reentrancy is just another consequence of the fact that you can use custom tokens.

#2 - c4-judge

2023-02-15T07:26:34Z

Picodes marked the issue as satisfactory

#3 - c4-judge

2023-02-23T21:03:28Z

Picodes changed the severity to QA (Quality Assurance)

#4 - c4-judge

2023-02-24T10:37:08Z

This previously downgraded issue has been upgraded by Picodes

#5 - c4-judge

2023-02-24T10:39:21Z

Picodes marked the issue as not a duplicate

#6 - c4-judge

2023-02-24T10:40:08Z

Picodes marked the issue as duplicate of #521

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