Astaria contest - Apocalypto'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: 51/103

Findings: 1

Award: $165.48

🌟 Selected for report: 0

🚀 Solo Findings: 0

Findings Information

🌟 Selected for report: rbserver

Also found by: Apocalypto, Jeiwan, evan, jesusrod15, ladboy233, m9800

Labels

bug
3 (High Risk)
satisfactory
duplicate-489

Awards

165.479 USDC - $165.48

External Links

Lines of code

https://github.com/code-423n4/2023-01-astaria/blob/main/src/Vault.sol#L70-L73

Vulnerability details

Impact

According to the documentation, Private Vaults do not interact with AstariaRouter except during deployment. Authorized users can deposit tokens into private vaults to fund strategies, but the withdraw function uses safeTransferFrom without first calling approve which means that deposited tokens will be permanently locked.

Proof of Concept

The Vault.withdraw function uses safeTransferFrom from the solmate ERC20 library to send tokens back to their owners. This function then calls the transferFrom function internally.

function transferFrom( address from, address to, uint256 amount ) public virtual returns (bool) { uint256 allowed = allowance[from][msg.sender]; // Saves gas for limited approvals. if (allowed != type(uint256).max) allowance[from][msg.sender] = allowed - amount;

As shown in the code, the function Vault.withdraw requires a preceding call to approve as it calculates the allowance based on it. Since private Vaults do not interact with AstariaRouter, which uses safeApprove for allowance in Public Vaults, the "transferFrom function always reverts due to underflow, leaving the tokens stuck in the Vault.

This PoC simulate the bug:

pragma solidity =0.8.17; import "forge-std/Test.sol"; import {Authority} from "solmate/auth/Auth.sol"; import {FixedPointMathLib} from "solmate/utils/FixedPointMathLib.sol"; import {MockERC721} from "solmate/test/utils/mocks/MockERC721.sol"; import { MultiRolesAuthority } from "solmate/auth/authorities/MultiRolesAuthority.sol"; import {ERC721} from "gpl/ERC721.sol"; import {SafeCastLib} from "gpl/utils/SafeCastLib.sol"; import {IAstariaRouter, AstariaRouter} from "../AstariaRouter.sol"; import {VaultImplementation} from "../VaultImplementation.sol"; import {PublicVault} from "../PublicVault.sol"; import {Vault} from "../Vault.sol"; import {TransferProxy} from "../TransferProxy.sol"; import {WithdrawProxy} from "../WithdrawProxy.sol"; import {Strings2} from "./utils/Strings2.sol"; import "./TestHelpers.t.sol"; import {OrderParameters} from "seaport/lib/ConsiderationStructs.sol"; contract AstariaTest is TestHelpers { using FixedPointMathLib for uint256; using CollateralLookup for address; using SafeCastLib for uint256; event NonceUpdated(uint256 nonce); event VaultShutdown(); function testVaultWithdrawDoS() external { TestNFT nft = new TestNFT(2); address tokenContract = address(nft); uint256 tokenId = uint256(1); uint256 initialBalance = WETH9.balanceOf(address(this)); address privateVault = _createPrivateVault({ strategist: strategistOne, delegate: strategistTwo }); _lendToPrivateVault( Lender({addr: strategistOne, amountToLend: 50 ether}), privateVault ); vm.startPrank(strategistOne); vm.expectRevert("TRANSFER_FROM_FAILED"); Vault(privateVault).withdraw(1 ether); } }

Tools Used

Manual aditing, foundry

Instead of safeTransferFrom it is recommended to use safeTransfer.

#0 - c4-judge

2023-01-24T09:27:06Z

Picodes marked the issue as duplicate of #489

#1 - c4-judge

2023-02-15T07:50:11Z

Picodes marked the issue as satisfactory

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